Skip to content

Commit

Permalink
fixup! feat(wallet): util to track utxo balance by address
Browse files Browse the repository at this point in the history
  • Loading branch information
mirceahasegan committed May 31, 2023
1 parent dcbe2f1 commit 4bb58e4
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 18 deletions.
27 changes: 18 additions & 9 deletions packages/wallet/src/services/BalanceTracker.ts
Expand Up @@ -2,6 +2,7 @@ import { BalanceTracker, DelegationTracker, TransactionalObservables } from './t
import { Cardano, coalesceValueQuantities } from '@cardano-sdk/core';

import { Observable, combineLatest, distinctUntilChanged, map } from 'rxjs';
import { utxoEquals } from './util';

const mapUtxoValue = map<Cardano.Utxo[], Cardano.Value>((utxo) =>
coalesceValueQuantities(utxo.map(([_, txOut]) => txOut.value))
Expand Down Expand Up @@ -47,18 +48,26 @@ export const createBalanceTracker = (
}
});

/** Returns utxos filtered by txOut.address or all utxos if address is undefined */
const filterByAddress = (address?: Cardano.PaymentAddress) => (utxos$: Observable<Cardano.Utxo[]>) =>
utxos$.pipe(map((utxos) => utxos.filter(([, txOut]) => !address || address === txOut.address)));
/** Returns utxos filtered by the txOut.addresses or all utxos if addresses are undefined or empty array */
const filterUtxosByAddress = (addresses?: Cardano.PaymentAddress[]) => (utxos$: Observable<Cardano.Utxo[]>) =>
utxos$.pipe(map((utxos) => utxos.filter(([, txOut]) => !addresses?.length || addresses.includes(txOut.address))));

/** Creates utxo balance aggregated by txOut.address. If address is undefined, it creates a single balance from all utxos */
export const createBalanceByAddressTracker = (
/** Creates utxo balance aggregated by txOut.address. If addresses are undefined or an empty array, it creates a single balance from all utxos */
export const createUtxoBalanceByAddressTracker = (
utxoTracker: TransactionalObservables<Cardano.Utxo[]>,
address?: Cardano.PaymentAddress
addresses?: Cardano.PaymentAddress[]
): Pick<BalanceTracker, 'utxo'> => ({
utxo: {
available$: utxoTracker.available$.pipe(filterByAddress(address)).pipe(mapUtxoValue),
total$: utxoTracker.total$.pipe(filterByAddress(address)).pipe(mapUtxoValue),
unspendable$: utxoTracker.unspendable$.pipe(filterByAddress(address)).pipe(mapUtxoValue)
available$: utxoTracker.available$.pipe(
filterUtxosByAddress(addresses),
distinctUntilChanged(utxoEquals),
mapUtxoValue
),
total$: utxoTracker.total$.pipe(filterUtxosByAddress(addresses), distinctUntilChanged(utxoEquals), mapUtxoValue),
unspendable$: utxoTracker.unspendable$.pipe(
filterUtxosByAddress(addresses),
distinctUntilChanged(utxoEquals),
mapUtxoValue
)
}
});
37 changes: 28 additions & 9 deletions packages/wallet/test/services/BalanceTracker.test.ts
Expand Up @@ -3,7 +3,7 @@
/* eslint-disable space-in-parens */
import { BehaviorObservable } from '@cardano-sdk/util-rxjs';
import { Cardano, coalesceValueQuantities } from '@cardano-sdk/core';
import { DelegationTracker, createBalanceByAddressTracker, createBalanceTracker } from '../../src/services';
import { DelegationTracker, createBalanceTracker, createUtxoBalanceByAddressTracker } from '../../src/services';
import { createTestScheduler, mockProviders } from '@cardano-sdk/util-dev';

const { utxo, utxo2 } = mockProviders;
Expand Down Expand Up @@ -48,25 +48,32 @@ describe('createBalanceTracker', () => {
});
});

it('createBalanceByAddressTracker returns balance filtered by address', () => {
it('createUtxoBalanceByAddressTracker returns balance filtered by addresses', () => {
const address = utxo[0][1].address;
const address2 = Cardano.PaymentAddress('addr_test1qzs0umu0s2ammmpw0hea0w2crtcymdjvvlqngpgqy76gpfnuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475qp3y3vz');
const utxoMultipleAddresses = utxo.slice(0, 3);

// Change the first utxo address so it's filtered out
utxoMultipleAddresses[0][1].address = Cardano.PaymentAddress('addr_test1qzs0umu0s2ammmpw0hea0w2crtcymdjvvlqngpgqy76gpfnuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475qp3y3vz');
utxoMultipleAddresses[0][1].address = address2;
createTestScheduler().run(({ hot, expectObservable }) => {
const available$ = hot( 'a', { a: utxoMultipleAddresses }) as unknown as BehaviorObservable<Cardano.Utxo[]>;
const total$ = hot( 'a', { a: utxoMultipleAddresses }) as unknown as BehaviorObservable<Cardano.Utxo[]>;
const unspendable$ = hot( 'a', { a: utxoMultipleAddresses }) as unknown as BehaviorObservable<Cardano.Utxo[]>;
const available$ = hot( 'aa', { a: utxoMultipleAddresses }) as unknown as BehaviorObservable<Cardano.Utxo[]>;
const total$ = hot( 'aa', { a: utxoMultipleAddresses }) as unknown as BehaviorObservable<Cardano.Utxo[]>;
const unspendable$ = hot('aa', { a: utxoMultipleAddresses }) as unknown as BehaviorObservable<Cardano.Utxo[]>;

const balanceByAddressTracker = createBalanceByAddressTracker(
const balanceByAddressTracker = createUtxoBalanceByAddressTracker(
{ available$, total$, unspendable$ },
address
[address]
);

const balanceNoFilterTracker = createBalanceByAddressTracker({ available$, total$, unspendable$ });
const balanceBy2AddressesTracker = createUtxoBalanceByAddressTracker(
{ available$, total$, unspendable$ },
[address, address2]
);

const balanceNoFilterTracker = createUtxoBalanceByAddressTracker({ available$, total$, unspendable$ });


// Check emitted values when filtering by one address
const filteredUtxos = utxoMultipleAddresses.slice(1);
expectObservable(balanceByAddressTracker!.utxo.total$).toBe('a', {
a: coalesceValueQuantities(filteredUtxos.map((u) => u[1].value))
Expand All @@ -78,6 +85,7 @@ describe('createBalanceTracker', () => {
a: coalesceValueQuantities(filteredUtxos.map((u) => u[1].value))
});

// Check emitted values when there is no filtering, so all utxos are returned
expectObservable(balanceNoFilterTracker!.utxo.total$).toBe('a', {
a: coalesceValueQuantities(utxoMultipleAddresses.map((u) => u[1].value))
});
Expand All @@ -87,6 +95,17 @@ describe('createBalanceTracker', () => {
expectObservable(balanceNoFilterTracker!.utxo.unspendable$).toBe('a', {
a: coalesceValueQuantities(utxoMultipleAddresses.map((u) => u[1].value))
});

// Check emitted values when there filtering by two addresses
expectObservable(balanceBy2AddressesTracker!.utxo.total$).toBe('a', {
a: coalesceValueQuantities(utxoMultipleAddresses.map((u) => u[1].value))
});
expectObservable(balanceBy2AddressesTracker!.utxo.available$).toBe('a', {
a: coalesceValueQuantities(utxoMultipleAddresses.map((u) => u[1].value))
});
expectObservable(balanceBy2AddressesTracker!.utxo.unspendable$).toBe('a', {
a: coalesceValueQuantities(utxoMultipleAddresses.map((u) => u[1].value))
});
});
});
});

0 comments on commit 4bb58e4

Please sign in to comment.