@cardano-sdk/e2e 0.13.1 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cardano-sdk/e2e",
3
- "version": "0.13.1",
3
+ "version": "0.14.0",
4
4
  "description": "End to end tests for the cardano-js-sdk packages.",
5
5
  "engines": {
6
6
  "node": ">=16.0"
@@ -80,18 +80,18 @@
80
80
  "dependencies": {
81
81
  "@cardano-foundation/ledgerjs-hw-app-cardano": "^6.0.0",
82
82
  "@cardano-ogmios/client": "5.6.0",
83
- "@cardano-sdk/cardano-services": "~0.12.1",
84
- "@cardano-sdk/cardano-services-client": "~0.9.5",
85
- "@cardano-sdk/core": "~0.13.0",
83
+ "@cardano-sdk/cardano-services": "~0.13.0",
84
+ "@cardano-sdk/cardano-services-client": "~0.9.6",
85
+ "@cardano-sdk/core": "~0.14.0",
86
86
  "@cardano-sdk/crypto": "~0.1.6",
87
- "@cardano-sdk/hardware-ledger": "~0.2.4",
88
- "@cardano-sdk/key-management": "~0.7.3",
89
- "@cardano-sdk/ogmios": "~0.12.1",
90
- "@cardano-sdk/tx-construction": "~0.6.0",
87
+ "@cardano-sdk/hardware-ledger": "~0.2.5",
88
+ "@cardano-sdk/key-management": "~0.7.4",
89
+ "@cardano-sdk/ogmios": "~0.12.2",
90
+ "@cardano-sdk/tx-construction": "~0.7.0",
91
91
  "@cardano-sdk/util": "~0.11.0",
92
- "@cardano-sdk/util-dev": "~0.12.1",
93
- "@cardano-sdk/util-rxjs": "~0.4.12",
94
- "@cardano-sdk/wallet": "~0.15.0",
92
+ "@cardano-sdk/util-dev": "~0.13.0",
93
+ "@cardano-sdk/util-rxjs": "~0.4.13",
94
+ "@cardano-sdk/wallet": "~0.16.0",
95
95
  "@vespaiach/axios-fetch-adapter": "^0.3.0",
96
96
  "axios": "^0.27.2",
97
97
  "bunyan": "^1.8.15",
@@ -116,10 +116,10 @@
116
116
  "@babel/core": "^7.18.2",
117
117
  "@babel/preset-env": "^7.18.2",
118
118
  "@babel/preset-typescript": "^7.17.12",
119
- "@cardano-sdk/dapp-connector": "~0.9.4",
120
- "@cardano-sdk/projection": "~0.6.5",
121
- "@cardano-sdk/projection-typeorm": "~0.3.1",
122
- "@cardano-sdk/web-extension": "~0.12.1",
119
+ "@cardano-sdk/dapp-connector": "~0.9.5",
120
+ "@cardano-sdk/projection": "~0.6.6",
121
+ "@cardano-sdk/projection-typeorm": "~0.3.2",
122
+ "@cardano-sdk/web-extension": "~0.12.2",
123
123
  "@dcspark/cardano-multiplatform-lib-browser": "^3.1.1",
124
124
  "@emurgo/cardano-message-signing-asmjs": "^1.0.1",
125
125
  "@types/bunyan": "^1.8.8",
@@ -164,5 +164,5 @@
164
164
  "webpack-cli": "^4.9.2",
165
165
  "webpack-merge": "^5.8.0"
166
166
  },
167
- "gitHead": "1e727698158588f87464c9a7f09ededfa4be9970"
167
+ "gitHead": "9567993a2d29031dda1a41411485b8f3e58aef06"
168
168
  }
package/src/factories.ts CHANGED
@@ -83,7 +83,7 @@ export const handleProviderFactory = new ProviderFactory<HandleProvider>();
83
83
  addressDiscoveryFactory.register('SingleAddressDiscovery', async () => new SingleAddressDiscovery());
84
84
  addressDiscoveryFactory.register(
85
85
  'HDSequentialDiscovery',
86
- async ({ chainHistoryProvider }) => new HDSequentialDiscovery(chainHistoryProvider, 20, 5)
86
+ async ({ chainHistoryProvider }) => new HDSequentialDiscovery(chainHistoryProvider, 20)
87
87
  );
88
88
 
89
89
  // bip32Ed25519
@@ -280,6 +280,7 @@ export type GetWalletProps = {
280
280
  logger: Logger;
281
281
  name: string;
282
282
  polling?: PollingConfig;
283
+ handlePolicyIds?: Cardano.PolicyId[];
283
284
  stores?: storage.WalletStores;
284
285
  customKeyParams?: KeyAgentFactoryProps;
285
286
  keyAgent?: AsyncKeyAgent;
@@ -311,7 +312,7 @@ const patchInitializeTxToRespectEpochBoundary = <T extends ObservableWallet>(
311
312
  * @returns an object containing the wallet and providers passed to it
312
313
  */
313
314
  export const getWallet = async (props: GetWalletProps) => {
314
- const { env, idx, logger, name, polling, stores, customKeyParams, keyAgent } = props;
315
+ const { env, idx, logger, name, polling, handlePolicyIds, stores, customKeyParams, keyAgent } = props;
315
316
  const providers = {
316
317
  addressDiscovery: await addressDiscoveryFactory.create(
317
318
  env.ADDRESS_DISCOVERY,
@@ -370,7 +371,7 @@ export const getWallet = async (props: GetWalletProps) => {
370
371
  ? () => Promise.resolve(keyAgent)
371
372
  : await keyManagementFactory.create(env.KEY_MANAGEMENT_PROVIDER, keyManagementParams, logger),
372
373
  createWallet: async (asyncKeyAgent: AsyncKeyAgent) =>
373
- new PersonalWallet({ name, polling }, { ...providers, keyAgent: asyncKeyAgent, logger, stores }),
374
+ new PersonalWallet({ handlePolicyIds, name, polling }, { ...providers, keyAgent: asyncKeyAgent, logger, stores }),
374
375
  logger
375
376
  });
376
377
 
package/test/util.ts CHANGED
@@ -315,20 +315,7 @@ export const burnTokens = async ({
315
315
 
316
316
  const signedTx = await wallet.finalizeTx(finalizeProps);
317
317
  await submitAndConfirm(wallet, signedTx);
318
-
319
- // Wait until all assets are burned
320
- await firstValueFromTimed(
321
- wallet.balance.utxo.available$.pipe(
322
- map(({ assets: availableAssets }) => availableAssets),
323
- filter(
324
- (availableAssets) =>
325
- !availableAssets?.size ||
326
- ![...tokens!].some(([id]) => [...availableAssets].some(([assetId]) => assetId === id))
327
- )
328
- ),
329
- 'Not all assets were burned',
330
- FAST_OPERATION_TIMEOUT_DEFAULT
331
- );
318
+ await txConfirmed(wallet, signedTx);
332
319
  };
333
320
 
334
321
  export const mapToGroupedAddress = (addrModel: AddressesModel): GroupedAddress => ({
@@ -1,3 +1,4 @@
1
+ import { AddressType } from '@cardano-sdk/key-management';
1
2
  import { Cardano } from '@cardano-sdk/core';
2
3
  import { DelegatedStake, PersonalWallet, createUtxoBalanceByAddressTracker } from '@cardano-sdk/wallet';
3
4
  import { MINUTE, getWallet } from '../../../src';
@@ -6,10 +7,23 @@ import { Percent } from '@cardano-sdk/util';
6
7
  import { createLogger } from '@cardano-sdk/util-dev';
7
8
  import { firstValueFromTimed, submitAndConfirm, walletReady } from '../../util';
8
9
  import { getEnv, walletVariables } from '../../../src/environment';
10
+ import delay from 'delay';
9
11
 
10
12
  const env = getEnv(walletVariables);
11
13
  const logger = createLogger();
12
14
  const TEST_FUNDS = 1_000_000_000n;
15
+ const distributionMessage = 'ObservableWallet.delegation.distribution$:';
16
+
17
+ const deriveStakeKeys = async (wallet: PersonalWallet) => {
18
+ await walletReady(wallet, 0n);
19
+ // Add 4 new addresses with different stake keys.
20
+ for (let i = 1; i < 5; ++i) {
21
+ await wallet.keyAgent.deriveAddress({ index: 0, type: AddressType.External }, i);
22
+ }
23
+ // Allow status tracker to change status with debounce.
24
+ // Otherwise the updates are be debounced and next calls find the wallet ready before it had a chance to update the status.
25
+ await delay(2);
26
+ };
13
27
 
14
28
  /** Distribute the wallet funds evenly across all its addresses */
15
29
  const distributeFunds = async (wallet: PersonalWallet) => {
@@ -22,7 +36,7 @@ const distributeFunds = async (wallet: PersonalWallet) => {
22
36
 
23
37
  const coinDeficit = TEST_FUNDS - totalCoins;
24
38
  if (coinDeficit > 10_000_000n) {
25
- logger.debug(
39
+ logger.info(
26
40
  `Insufficient funds in wallet account index 1. Missing ${coinDeficit}. Transferring from wallet account index 0`
27
41
  );
28
42
  const fundingWallet = (await getWallet({ env, idx: 0, logger, name: 'WalletAcct0', polling: { interval: 50 } }))
@@ -42,10 +56,12 @@ const distributeFunds = async (wallet: PersonalWallet) => {
42
56
 
43
57
  const txBuilder = wallet.createTxBuilder();
44
58
 
45
- logger.debug(`Sending ${coinsPerAddress} to the ${addresses.length - 1} derived addresses`);
59
+ logger.info(`Sending ${coinsPerAddress} to the ${addresses.length - 1} derived addresses`);
46
60
  // The first one was generated when the wallet was created.
47
61
  for (let i = 1; i < addresses.length; ++i) {
48
62
  const derivedAddress = addresses[i];
63
+ logger.info('Funding', derivedAddress.address, coinsPerAddress);
64
+ logger.info(derivedAddress.rewardAccount);
49
65
  txBuilder.addOutput(txBuilder.buildOutput().address(derivedAddress.address).coin(coinsPerAddress).toTxOut());
50
66
  }
51
67
 
@@ -56,7 +72,8 @@ const distributeFunds = async (wallet: PersonalWallet) => {
56
72
  /** await for rewardAccounts$ to be registered, unregistered, as defined in states */
57
73
  const rewardAccountStatuses = async (
58
74
  rewardAccounts$: Observable<Cardano.RewardAccountInfo[]>,
59
- statuses: Cardano.StakeKeyStatus[]
75
+ statuses: Cardano.StakeKeyStatus[],
76
+ timeout = MINUTE
60
77
  ) =>
61
78
  firstValueFromTimed(
62
79
  rewardAccounts$.pipe(
@@ -65,21 +82,32 @@ const rewardAccountStatuses = async (
65
82
  filter((statusArr) => statusArr.every((s) => statuses.includes(s)))
66
83
  ),
67
84
  `Timeout waiting for all reward accounts stake keys to be one of ${statuses.join('|')}`,
68
- MINUTE
85
+ timeout
69
86
  );
70
87
 
71
88
  /** Create stakeKey deregistration transaction for all reward accounts */
72
89
  const deregisterAllStakeKeys = async (wallet: PersonalWallet): Promise<void> => {
73
- const txBuilder = wallet.createTxBuilder();
74
- txBuilder.delegate();
75
- const { tx: deregTx } = await txBuilder.build().sign();
76
- await submitAndConfirm(wallet, deregTx);
77
-
78
- await rewardAccountStatuses(wallet.delegation.rewardAccounts$, [
79
- Cardano.StakeKeyStatus.Unregistered,
80
- Cardano.StakeKeyStatus.Unregistering
81
- ]);
82
- logger.debug('Deregistered all stake keys');
90
+ await walletReady(wallet, 0n);
91
+ try {
92
+ await rewardAccountStatuses(
93
+ wallet.delegation.rewardAccounts$,
94
+ [Cardano.StakeKeyStatus.Unregistered, Cardano.StakeKeyStatus.Unregistering],
95
+ 0
96
+ );
97
+ logger.info('Stake keys are already deregistered');
98
+ } catch {
99
+ // Some stake keys are registered. Deregister them
100
+ const txBuilder = wallet.createTxBuilder();
101
+ txBuilder.delegate();
102
+ const { tx: deregTx } = await txBuilder.build().sign();
103
+ await submitAndConfirm(wallet, deregTx);
104
+
105
+ await rewardAccountStatuses(wallet.delegation.rewardAccounts$, [
106
+ Cardano.StakeKeyStatus.Unregistered,
107
+ Cardano.StakeKeyStatus.Unregistering
108
+ ]);
109
+ logger.info('Deregistered all stake keys');
110
+ }
83
111
  };
84
112
 
85
113
  const createStakeKeyRegistrationCert = (rewardAccount: Cardano.RewardAccount): Cardano.Certificate => ({
@@ -129,9 +157,10 @@ describe('PersonalWallet/delegationDistribution', () => {
129
157
  let wallet: PersonalWallet;
130
158
 
131
159
  beforeAll(async () => {
132
- wallet = (await getWallet({ env, idx: 1, logger, name: 'Wallet', polling: { interval: 50 } })).wallet;
133
- await distributeFunds(wallet);
160
+ wallet = (await getWallet({ env, idx: 3, logger, name: 'Wallet', polling: { interval: 50 } })).wallet;
161
+ await deriveStakeKeys(wallet);
134
162
  await deregisterAllStakeKeys(wallet);
163
+ await distributeFunds(wallet);
135
164
  });
136
165
 
137
166
  afterAll(() => {
@@ -140,14 +169,20 @@ describe('PersonalWallet/delegationDistribution', () => {
140
169
 
141
170
  it('reports observable wallet multi delegation as delegationDistribution by pool', async () => {
142
171
  await walletReady(wallet);
172
+
143
173
  const walletAddresses = await firstValueFromTimed(wallet.addresses$);
144
174
  const rewardAccounts = await firstValueFrom(wallet.delegation.rewardAccounts$);
145
175
 
176
+ expect(rewardAccounts.length).toBe(5);
177
+
146
178
  // No stake distribution initially
147
179
  const delegationDistribution = await firstValueFrom(wallet.delegation.distribution$);
180
+ logger.info('Empty delegation distribution initially');
148
181
  expect(delegationDistribution).toEqual(new Map());
149
182
 
150
183
  const poolIds = await delegateToMultiplePools(wallet);
184
+ // Redistribute the funds because delegation costs send change to the first account, messing up the uniform distribution
185
+ await distributeFunds(wallet);
151
186
 
152
187
  // Check that reward addresses were delegated
153
188
  await walletReady(wallet);
@@ -176,6 +211,9 @@ describe('PersonalWallet/delegationDistribution', () => {
176
211
  }));
177
212
  const actualDelegationDistribution = await firstValueFrom(wallet.delegation.distribution$);
178
213
 
214
+ logger.info('Funds were distributed evenly across the addresses.');
215
+ logger.info(distributionMessage, actualDelegationDistribution);
216
+
179
217
  expect([...actualDelegationDistribution.values()]).toEqual(expectedDelegationDistribution);
180
218
 
181
219
  // Send all coins to the last address. Check that stake distribution is 100 for that address and 0 for the rest
@@ -195,6 +233,10 @@ describe('PersonalWallet/delegationDistribution', () => {
195
233
 
196
234
  let simplifiedDelegationDistribution: Partial<DelegatedStake>[] = await firstValueFrom(
197
235
  wallet.delegation.distribution$.pipe(
236
+ tap((delegatedStake) => {
237
+ logger.info('All funds were moved to', walletAddresses[walletAddresses.length - 1].address);
238
+ logger.info(distributionMessage, delegatedStake);
239
+ }),
198
240
  map((delegatedStake) =>
199
241
  [...delegatedStake.values()].map(({ pool, percentage }) => ({
200
242
  id: pool.id,
@@ -204,6 +246,7 @@ describe('PersonalWallet/delegationDistribution', () => {
204
246
  )
205
247
  )
206
248
  );
249
+
207
250
  expect(simplifiedDelegationDistribution).toEqual(
208
251
  rewardAccounts.map((_, index) => ({
209
252
  id: poolIds[index].id,
@@ -220,6 +263,10 @@ describe('PersonalWallet/delegationDistribution', () => {
220
263
  await submitAndConfirm(wallet, txDelegateTo1Pool);
221
264
  simplifiedDelegationDistribution = await firstValueFrom(
222
265
  wallet.delegation.distribution$.pipe(
266
+ tap((distribution) => {
267
+ logger.info('All stake keys are delegated to poolId:', poolIds[0].id);
268
+ logger.info(distributionMessage, distribution);
269
+ }),
223
270
  map((distribution) =>
224
271
  [...distribution.values()].map((delegatedStake) => ({
225
272
  id: delegatedStake.pool.id,
@@ -0,0 +1,271 @@
1
+ /* eslint-disable sonarjs/no-duplicate-string */
2
+ import { Cardano, metadatum, nativeScriptPolicyId } from '@cardano-sdk/core';
3
+ import { FinalizeTxProps, PersonalWallet } from '@cardano-sdk/wallet';
4
+ import { InitializeTxProps } from '@cardano-sdk/tx-construction';
5
+ import { KeyRole, TransactionSigner, util } from '@cardano-sdk/key-management';
6
+ import { Metadatum, TokenMap } from '@cardano-sdk/core/dist/cjs/Cardano';
7
+ import { burnTokens, createStandaloneKeyAgent, submitAndConfirm, txConfirmed, walletReady } from '../../util';
8
+ import { createLogger } from '@cardano-sdk/util-dev';
9
+ import { firstValueFrom } from 'rxjs';
10
+ import { getEnv, getWallet, walletVariables } from '../../../src';
11
+
12
+ const env = getEnv(walletVariables);
13
+ const logger = createLogger();
14
+
15
+ type HandleMetadata = {
16
+ [policyId: string]: {
17
+ [handleName: string]: {
18
+ augmentations: [];
19
+ core: {
20
+ handleEncoding: string;
21
+ og: number;
22
+ prefix: string;
23
+ termsofuse: string;
24
+ version: number;
25
+ };
26
+ description: string;
27
+ image: string;
28
+ name: string;
29
+ website: string;
30
+ };
31
+ };
32
+ };
33
+
34
+ const createHandleMetadata = (handlePolicyId: string, handleNames: string[]): HandleMetadata => {
35
+ const result: HandleMetadata[0] = {};
36
+ for (const key of handleNames) {
37
+ result[key] = {
38
+ augmentations: [],
39
+ core: {
40
+ handleEncoding: 'utf-8',
41
+ og: 0,
42
+ prefix: '$',
43
+ termsofuse: 'https://cardanofoundation.org/en/terms-and-conditions/',
44
+ version: 0
45
+ },
46
+ description: 'The Handle Standard',
47
+ image: 'ipfs://some-hash',
48
+ name: '$handle1',
49
+ website: 'https://cardano.org/'
50
+ };
51
+ }
52
+ return { [handlePolicyId]: result };
53
+ };
54
+
55
+ describe('Ada handle', () => {
56
+ let wallet: PersonalWallet;
57
+ let receivingWallet: PersonalWallet;
58
+ let policySigner: TransactionSigner;
59
+ let policyId: Cardano.PolicyId;
60
+ let policyScript: Cardano.NativeScript;
61
+ let assetIds: Cardano.AssetId[];
62
+
63
+ const assetNames = ['6861646e6c6531', '6861646e6c6532'];
64
+ let walletAddress: Cardano.PaymentAddress;
65
+ const coins = 2_000_000n; // number of coins to use in each transaction
66
+
67
+ const initPolicyId = async () => {
68
+ wallet = (await getWallet({ env, idx: 0, logger, name: 'Handle Init Wallet', polling: { interval: 50 } })).wallet;
69
+ await walletReady(wallet, coins);
70
+ const derivationPath = {
71
+ index: 2,
72
+ role: KeyRole.External
73
+ };
74
+ const keyAgent = await createStandaloneKeyAgent(
75
+ env.KEY_MANAGEMENT_PARAMS.mnemonic.split(' '),
76
+ await firstValueFrom(wallet.genesisParameters$),
77
+ await wallet.keyAgent.getBip32Ed25519()
78
+ );
79
+ const pubKey = await keyAgent.derivePublicKey(derivationPath);
80
+ const keyHash = await keyAgent.bip32Ed25519.getPubKeyHash(pubKey);
81
+ policySigner = new util.KeyAgentTransactionSigner(keyAgent, derivationPath);
82
+ policyScript = {
83
+ __type: Cardano.ScriptType.Native,
84
+ kind: Cardano.NativeScriptKind.RequireAllOf,
85
+ scripts: [
86
+ {
87
+ __type: Cardano.ScriptType.Native,
88
+ keyHash,
89
+ kind: Cardano.NativeScriptKind.RequireSignature
90
+ }
91
+ ]
92
+ };
93
+ policyId = nativeScriptPolicyId(policyScript);
94
+ wallet.shutdown();
95
+ };
96
+
97
+ const mint = async (tokens: Cardano.TokenMap, txMetadatum: Cardano.Metadatum) => {
98
+ const auxiliaryData = {
99
+ blob: new Map([[721n, txMetadatum]])
100
+ };
101
+
102
+ const txProps: InitializeTxProps = {
103
+ auxiliaryData,
104
+ mint: tokens,
105
+ outputs: new Set([
106
+ {
107
+ address: walletAddress,
108
+ value: {
109
+ assets: tokens,
110
+ coins
111
+ }
112
+ }
113
+ ]),
114
+ witness: { extraSigners: [policySigner], scripts: [policyScript] }
115
+ };
116
+
117
+ const unsignedTx = await wallet.initializeTx(txProps);
118
+
119
+ const finalizeProps: FinalizeTxProps = {
120
+ auxiliaryData,
121
+ tx: unsignedTx,
122
+ witness: { extraSigners: [policySigner], scripts: [policyScript] }
123
+ };
124
+
125
+ const signedTx = await wallet.finalizeTx(finalizeProps);
126
+ await submitAndConfirm(wallet, signedTx);
127
+ };
128
+
129
+ const restartWallet = async () => {
130
+ wallet.shutdown();
131
+ wallet = (
132
+ await getWallet({
133
+ env,
134
+ handlePolicyIds: [policyId],
135
+ idx: 0,
136
+ logger,
137
+ name: 'Minting Wallet',
138
+ polling: { interval: 50 }
139
+ })
140
+ ).wallet;
141
+ await walletReady(wallet, coins);
142
+ };
143
+
144
+ beforeAll(async () => {
145
+ await initPolicyId();
146
+ wallet = (
147
+ await getWallet({
148
+ env,
149
+ handlePolicyIds: [policyId],
150
+ idx: 0,
151
+ logger,
152
+ name: 'Minting Wallet',
153
+ polling: { interval: 50 }
154
+ })
155
+ ).wallet;
156
+ receivingWallet = (
157
+ await getWallet({
158
+ env,
159
+ handlePolicyIds: [policyId],
160
+ idx: 1,
161
+ logger,
162
+ name: 'Receiving Wallet',
163
+ polling: { interval: 50 }
164
+ })
165
+ ).wallet;
166
+ await Promise.all([walletReady(wallet, coins), walletReady(receivingWallet, 0n)]);
167
+ assetIds = [Cardano.AssetId(`${policyId}${assetNames[0]}`), Cardano.AssetId(`${policyId}${assetNames[1]}`)];
168
+ walletAddress = (await firstValueFrom(wallet.addresses$))[0].address;
169
+ });
170
+
171
+ afterEach(async () => {
172
+ await burnTokens({
173
+ policySigners: [policySigner],
174
+ scripts: [policyScript],
175
+ wallet
176
+ });
177
+ await burnTokens({
178
+ policySigners: [policySigner],
179
+ scripts: [policyScript],
180
+ wallet: receivingWallet
181
+ });
182
+ });
183
+
184
+ afterAll(async () => {
185
+ wallet.shutdown();
186
+ receivingWallet.shutdown();
187
+ });
188
+
189
+ it("PersonalWallet discovers it's own handles", async () => {
190
+ const tokens = new Map([
191
+ [assetIds[0], 1n],
192
+ [assetIds[1], 1n]
193
+ ]);
194
+ const txMetadatum = metadatum.jsonToMetadatum(createHandleMetadata(policyId, ['handle1', 'handle2']));
195
+ await mint(tokens, txMetadatum);
196
+ let utxo = await firstValueFrom(wallet.balance.utxo.available$);
197
+ let receivingUtxo = await firstValueFrom(receivingWallet.balance.utxo.available$);
198
+ expect(utxo.assets?.size).toEqual(2);
199
+ expect(receivingUtxo.assets).toBeUndefined();
200
+ let handles = await firstValueFrom(wallet.handles$);
201
+ let receivingHandles = await firstValueFrom(receivingWallet.handles$);
202
+ expect(handles.length).toEqual(2);
203
+ expect(receivingHandles.length).toEqual(0);
204
+
205
+ const token = new Map([[assetIds[0], 1n]]);
206
+ const destAddresses = (await firstValueFrom(receivingWallet.addresses$))[0].address;
207
+ const txBuilder = wallet.createTxBuilder();
208
+ const { tx } = await txBuilder
209
+ .addOutput(await txBuilder.buildOutput().address(destAddresses).coin(coins).assets(token).build())
210
+ .build()
211
+ .sign();
212
+ await wallet.submitTx(tx);
213
+ await txConfirmed(receivingWallet, tx);
214
+
215
+ utxo = await firstValueFrom(wallet.balance.utxo.available$);
216
+ receivingUtxo = await firstValueFrom(receivingWallet.balance.utxo.available$);
217
+ expect(utxo.assets?.size).toEqual(1);
218
+ expect(receivingUtxo.assets?.size).toEqual(1);
219
+ expect(receivingUtxo.assets?.keys().next().value).toEqual(assetIds[0]);
220
+ handles = await firstValueFrom(wallet.handles$);
221
+ receivingHandles = await firstValueFrom(receivingWallet.handles$);
222
+ expect(handles.length).toEqual(1);
223
+ expect(receivingHandles.length).toEqual(1);
224
+ });
225
+
226
+ describe('double mint handling', () => {
227
+ it('filters out double mints in separate transactions', async () => {
228
+ const tokens: TokenMap = new Map([[assetIds[0], 1n]]);
229
+ const txMetadatum: Metadatum = metadatum.jsonToMetadatum(createHandleMetadata(policyId, ['handle1']));
230
+ await mint(tokens, txMetadatum);
231
+ let handles = await firstValueFrom(wallet.handles$);
232
+ expect(handles.length).toEqual(1);
233
+ await mint(tokens, txMetadatum);
234
+ await restartWallet();
235
+ const utxo = await firstValueFrom(wallet.balance.utxo.available$);
236
+ expect(utxo.assets?.values().next().value).toEqual(2n);
237
+ handles = await firstValueFrom(wallet.handles$);
238
+ expect(handles).toEqual([]);
239
+ });
240
+
241
+ it('filters out double mints from within the same transaction', async () => {
242
+ const tokens = new Map([[assetIds[0], 2n]]);
243
+ const txMetadatum: Metadatum = metadatum.jsonToMetadatum(createHandleMetadata(policyId, ['handle1']));
244
+ await mint(tokens, txMetadatum);
245
+ await restartWallet();
246
+ const utxo = await firstValueFrom(wallet.balance.utxo.available$);
247
+ expect(utxo.assets?.values().next().value).toEqual(2n);
248
+ const handles = await firstValueFrom(wallet.handles$);
249
+ expect(handles).toEqual([]);
250
+ });
251
+
252
+ it('shows handle after corrective burn', async () => {
253
+ const tokens = new Map([[assetIds[0], 2n]]);
254
+ const txMetadatum: Metadatum = metadatum.jsonToMetadatum(createHandleMetadata(policyId, ['handle1']));
255
+ await mint(tokens, txMetadatum);
256
+ let utxo = await firstValueFrom(wallet.balance.utxo.available$);
257
+ expect(utxo.assets?.values().next().value).toEqual(2n);
258
+ await burnTokens({
259
+ policySigners: [policySigner],
260
+ scripts: [policyScript],
261
+ tokens: new Map([[assetIds[0], 1n]]),
262
+ wallet
263
+ });
264
+ await restartWallet();
265
+ utxo = await firstValueFrom(wallet.balance.utxo.available$);
266
+ expect(utxo.assets?.values().next().value).toEqual(1n);
267
+ const correctedHandles = await firstValueFrom(wallet.handles$);
268
+ expect(correctedHandles.length).toEqual(1);
269
+ });
270
+ });
271
+ });
@@ -39,20 +39,7 @@ describe('PersonalWallet/multiAddress', () => {
39
39
 
40
40
  let txBuilder = wallet.createTxBuilder();
41
41
 
42
- let addressesToBeDiscovered = new Array<GroupedAddress>();
43
-
44
- // Let's add the 5 stake keys.
45
- for (let i = 0; i < 5; ++i) {
46
- addressesToBeDiscovered.push(
47
- await multiAddressKeyAgent.deriveAddress(
48
- {
49
- index: 0,
50
- type: AddressType.External
51
- },
52
- i
53
- )
54
- );
55
- }
42
+ const addressesToBeDiscovered = new Array<GroupedAddress>();
56
43
 
57
44
  // Deposit some tADA at some generated addresses from the previously generated mnemonics.
58
45
  for (let i = 0; i < PAYMENT_ADDRESSES_TO_GENERATE; ++i) {
@@ -68,9 +55,6 @@ describe('PersonalWallet/multiAddress', () => {
68
55
  txBuilder.addOutput(txBuilder.buildOutput().address(address.address).coin(3_000_000n).toTxOut());
69
56
  }
70
57
 
71
- // Remove duplicates
72
- addressesToBeDiscovered = [...new Set(addressesToBeDiscovered)];
73
-
74
58
  const { tx: signedTx } = await txBuilder.build().sign();
75
59
 
76
60
  await wallet.submitTx(signedTx);