Skip to content

Commit

Permalink
feat(wallet): implement BalanceTracker, move all test mocks under ./m…
Browse files Browse the repository at this point in the history
…ocks
  • Loading branch information
mkazlauskas committed Oct 14, 2021
1 parent ecdcfb3 commit 98849d2
Show file tree
Hide file tree
Showing 16 changed files with 141 additions and 45 deletions.
74 changes: 49 additions & 25 deletions packages/wallet/src/BalanceTracker.ts
@@ -1,4 +1,9 @@
import { Utxo } from '@cardano-ogmios/schema';
import { Ogmios } from '@cardano-sdk/core';
import Emittery from 'emittery';
import { dummyLogger } from 'ts-log';
import { UtxoRepositoryEvent, UtxoRepositoryFields } from '.';
import { UtxoRepository } from './types';

export interface Balance extends Ogmios.Value {
rewards: Ogmios.Lovelace;
Expand All @@ -9,31 +14,50 @@ export interface Balances {
available: Balance;
}

export enum BalanceTrackerEvent {
Changed = 'changed'
}

export interface BalanceTrackerEvents {
balanceChanged: Balances;
changed: Balances;
}

// export class BalanceTracker extends Emittery<BalanceTrackerEvents> implements Balances {
// total: Balance;
// available: Balance;

// constructor(utxoRepository: UtxoRepository) {
// super();
// const totalValue = this.#getBalance(utxoRepository.allUtxos);
// const availableValue = this.#getBalance(utxoRepository.availableUtxos);
// const totalRewards = utxoRepository.rewards;
// const availableRewards = utxoRepository.availableRewards;
// }

// #getBalance(utxo: Utxo): Ogmios.Value {
// return Ogmios.util.coalesceValueQuantities(
// utxo.map(([_, txOut]) => {
// const { coins, assets } = txOut.value;
// return {
// coins: BigInt(coins),
// assets
// };
// })
// );
// }
// }
export class BalanceTracker extends Emittery<BalanceTrackerEvents> implements Balances {
total!: Balance;
available!: Balance;

constructor(utxoRepository: UtxoRepository, logger = dummyLogger) {
super();
this.#updateBalances(utxoRepository);
utxoRepository.on(UtxoRepositoryEvent.Changed, (fields) => {
this.#updateBalances(fields);
this.emit(BalanceTrackerEvent.Changed, {
available: this.available,
total: this.total
}).catch(logger.error);
});
}

#updateBalances(utxoRepository: UtxoRepositoryFields) {
this.total = {
...this.#getBalance(utxoRepository.allUtxos),
rewards: utxoRepository.allRewards || 0n
};
this.available = {
...this.#getBalance(utxoRepository.availableUtxos),
rewards: utxoRepository.availableRewards || 0n
};
}

#getBalance(utxo: Utxo): Ogmios.Value {
return Ogmios.util.coalesceValueQuantities(
utxo.map(([_, txOut]) => {
const { coins, assets } = txOut.value;
return {
coins: BigInt(coins),
assets
};
})
);
}
}
1 change: 1 addition & 0 deletions packages/wallet/src/index.ts
Expand Up @@ -6,3 +6,4 @@ export * as KeyManagement from './KeyManagement';
export * from './SingleAddressWallet';
export * from './types';
export * from './TransactionError';
export * from './BalanceTracker';
44 changes: 44 additions & 0 deletions packages/wallet/test/BalanceTracker.test.ts
@@ -0,0 +1,44 @@
import { Ogmios } from '@cardano-sdk/core';
import { MockUtxoRepository } from './mocks';
import { BalanceTracker, UtxoRepositoryEvent, UtxoRepositoryFields } from '../src';

const numAssets = (tokenMap?: Ogmios.TokenMap) => Object.keys(tokenMap || {}).length;

describe('BalanceTracker', () => {
let balanceTracker: BalanceTracker;
let utxoRepository: MockUtxoRepository;

beforeEach(() => {
utxoRepository = new MockUtxoRepository();
balanceTracker = new BalanceTracker(utxoRepository);
});

it('constructor balances from utxo repository', () => {
expect(balanceTracker.total.coins).toBeGreaterThan(0n);
expect(balanceTracker.total.assets).toBeTruthy();
expect(balanceTracker.total.rewards).toBeGreaterThan(0n);
});

it('updates balances on UtxoRepositoryEvent.Changed', async () => {
const totalBefore = balanceTracker.total;

const allRewards = utxoRepository.allRewards - 1n;
const availableRewards = utxoRepository.allRewards - 2n;
await utxoRepository.emit(UtxoRepositoryEvent.Changed, {
allUtxos: utxoRepository.allUtxos.slice(1),
availableUtxos: utxoRepository.allUtxos.slice(2),
allRewards,
availableRewards,
delegation: null
} as UtxoRepositoryFields);

expect(balanceTracker.total.coins).toBeLessThan(totalBefore.coins);
expect(balanceTracker.total.coins).toBeGreaterThan(balanceTracker.available.coins);

expect(numAssets(balanceTracker.total.assets)).toBeLessThan(numAssets(totalBefore.assets));
expect(numAssets(balanceTracker.total.assets)).toBeGreaterThan(numAssets(balanceTracker.available.assets));

expect(balanceTracker.total.rewards).toBe(allRewards);
expect(balanceTracker.available.rewards).toBe(availableRewards);
});
});
2 changes: 1 addition & 1 deletion packages/wallet/test/InMemoryTransactionTracker.test.ts
@@ -1,6 +1,6 @@
import { CardanoSerializationLib, CSL, ProviderError, ProviderFailure } from '@cardano-sdk/core';
import { dummyLogger } from 'ts-log';
import { ledgerTip, providerStub, ProviderStub, queryTransactionsResult } from './ProviderStub';
import { ledgerTip, providerStub, ProviderStub, queryTransactionsResult } from './mocks';
import mockDelay from 'delay';
import { TransactionTrackerEvent, InMemoryTransactionTracker, TransactionFailure } from '../src';

Expand Down
11 changes: 9 additions & 2 deletions packages/wallet/test/InMemoryUtxoRepository.test.ts
Expand Up @@ -2,7 +2,15 @@
import { roundRobinRandomImprove, InputSelector } from '@cardano-sdk/cip2';
import { loadCardanoSerializationLib, CardanoSerializationLib, CSL, Ogmios, Cardano } from '@cardano-sdk/core';
import { flushPromises, SelectionConstraints } from '@cardano-sdk/util-dev';
import { providerStub, delegate, rewards, ProviderStub, utxo, delegationAndRewards } from './ProviderStub';
import {
providerStub,
delegate,
rewards,
ProviderStub,
utxo,
delegationAndRewards,
MockTransactionTracker
} from './mocks';
import {
InMemoryUtxoRepository,
KeyManagement,
Expand All @@ -11,7 +19,6 @@ import {
UtxoRepositoryEvent,
UtxoRepositoryFields
} from '../src';
import { MockTransactionTracker } from './mockTransactionTracker';
import { ogmiosToCsl } from '@cardano-sdk/core/src/Ogmios';
import { TxIn, TxOut } from '@cardano-ogmios/schema';
import { TransactionError, TransactionFailure } from '../src/TransactionError';
Expand Down
3 changes: 1 addition & 2 deletions packages/wallet/test/SingleAddressWallet.test.ts
@@ -1,7 +1,7 @@
/* eslint-disable max-len */
import { loadCardanoSerializationLib, CardanoSerializationLib, Cardano } from '@cardano-sdk/core';
import { InputSelector, roundRobinRandomImprove } from '@cardano-sdk/cip2';
import { ProviderStub, providerStub } from './ProviderStub';
import { ProviderStub, providerStub, txTracker } from './mocks';
import {
createSingleAddressWallet,
InMemoryUtxoRepository,
Expand All @@ -10,7 +10,6 @@ import {
SingleAddressWalletDependencies,
UtxoRepository
} from '../src';
import { txTracker } from './mockTransactionTracker';

describe('Wallet', () => {
const name = 'Test Wallet';
Expand Down
@@ -1,5 +1,5 @@
import { CardanoSerializationLib, loadCardanoSerializationLib } from '@cardano-sdk/core';
import { testKeyManager } from '../testKeyManager';
import { testKeyManager } from '../mocks';
import { CertificateFactory } from '../../src/Transaction';
import { KeyManager } from '../../src/KeyManagement';

Expand Down
@@ -1,5 +1,5 @@
import { loadCardanoSerializationLib, ProtocolParametersRequiredByWallet } from '@cardano-sdk/core';
import { testKeyManager } from '../testKeyManager';
import { testKeyManager } from '../mocks';
import { Transaction } from '../../src';
import { InitializeTxProps } from '../../src/Transaction';

Expand Down
@@ -1,18 +1,14 @@
import { InputSelector, roundRobinRandomImprove } from '@cardano-sdk/cip2';
import { loadCardanoSerializationLib, CardanoSerializationLib, CSL, CardanoProvider, Ogmios } from '@cardano-sdk/core';
import { SelectionConstraints } from '@cardano-sdk/util-dev';
import { providerStub } from '../ProviderStub';
import { providerStub, txTracker, testKeyManager } from '../mocks';
import {
CertificateFactory,
createTransactionInternals,
CreateTxInternalsProps,
Withdrawal
} from '../../src/Transaction';
import { KeyManager } from '../../src/KeyManagement';
import { testKeyManager } from '../testKeyManager';
import { UtxoRepository } from '../../src/types';
import { InMemoryUtxoRepository } from '../../src/InMemoryUtxoRepository';
import { txTracker } from '../mockTransactionTracker';
import { UtxoRepository, InMemoryUtxoRepository, KeyManagement } from '../../src';

const address =
'addr_test1qq585l3hyxgj3nas2v3xymd23vvartfhceme6gv98aaeg9muzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475q2g7k3g';
Expand All @@ -22,7 +18,7 @@ describe('Transaction.createTransactionInternals', () => {
let provider: CardanoProvider;
let inputSelector: InputSelector;
let utxoRepository: UtxoRepository;
let keyManager: KeyManager;
let keyManager: KeyManagement.KeyManager;
let outputs: Set<CSL.TransactionOutput>;

const createSimpleTransactionInternals = async (props?: Partial<CreateTxInternalsProps>) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/wallet/test/Transaction/withdrawal.test.ts
@@ -1,5 +1,5 @@
import { loadCardanoSerializationLib } from '@cardano-sdk/core';
import { testKeyManager } from '../testKeyManager';
import { testKeyManager } from '../mocks';
import { Transaction } from '../../src';

describe('Transaction.withdrawal', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/wallet/test/integration/withdrawal.test.ts
Expand Up @@ -16,7 +16,7 @@ import {
UtxoRepositoryEvent
} from '@cardano-sdk/wallet';
// Not testing with a real provider
import { providerStub } from '../ProviderStub';
import { providerStub } from '../mocks';

const walletProps: SingleAddressWalletProps = { name: 'some-wallet' };
const networkId = Cardano.NetworkId.mainnet;
Expand Down
@@ -1,5 +1,5 @@
import Emittery from 'emittery';
import { TransactionTrackerEvents } from '../src';
import { TransactionTrackerEvents } from '../../src';

export class MockTransactionTracker extends Emittery<TransactionTrackerEvents> {
track = jest.fn();
Expand Down
13 changes: 13 additions & 0 deletions packages/wallet/test/mocks/MockUtxoRepository.ts
@@ -0,0 +1,13 @@
import Emittery from 'emittery';
import { delegate, rewards, utxo } from './ProviderStub';
import { UtxoRepository, UtxoRepositoryEvents } from '../../src';

export class MockUtxoRepository extends Emittery<UtxoRepositoryEvents> implements UtxoRepository {
sync = jest.fn().mockResolvedValue(void 0);
selectInputs = jest.fn();
allUtxos = utxo;
availableUtxos = utxo;
allRewards = rewards;
availableRewards = rewards;
delegation = delegate;
}
@@ -1,5 +1,6 @@
/* eslint-disable max-len */
import * as Schema from '@cardano-ogmios/schema';
import { PXL, TSLA } from '@cardano-sdk/util-dev/src/assetId';

export const stakeKeyHash = 'stake_test1up7pvfq8zn4quy45r2g572290p9vf99mr9tn7r9xrgy2l2qdsf58d';

Expand All @@ -13,7 +14,11 @@ export const utxo: Schema.Utxo = [
address:
'addr_test1qzs0umu0s2ammmpw0hea0w2crtcymdjvvlqngpgqy76gpfnuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475qp3y3vz',
value: {
coins: 4_027_026_465
coins: 4_027_026_465,
assets: {
[TSLA]: 10n,
[PXL]: 5n
}
},
datum: null
}
Expand All @@ -27,7 +32,10 @@ export const utxo: Schema.Utxo = [
address:
'addr_test1qq585l3hyxgj3nas2v3xymd23vvartfhceme6gv98aaeg9muzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475q2g7k3g',
value: {
coins: 3_289_566
coins: 3_289_566,
assets: {
[TSLA]: 15n
}
},
datum: null
}
Expand Down
4 changes: 4 additions & 0 deletions packages/wallet/test/mocks/index.ts
@@ -0,0 +1,4 @@
export * from './ProviderStub';
export * from './MockUtxoRepository';
export * from './MockTransactionTracker';
export * from './testKeyManager';
@@ -1,5 +1,5 @@
import { Cardano, CardanoSerializationLib } from '@cardano-sdk/core';
import { KeyManagement } from '../src';
import { KeyManagement } from '../../src';

export const testKeyManager = (csl: CardanoSerializationLib) =>
KeyManagement.createInMemoryKeyManager({
Expand Down

0 comments on commit 98849d2

Please sign in to comment.