@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/CHANGELOG.md +11 -0
- package/dist/cjs/factories.d.ts +1 -0
- package/dist/cjs/factories.d.ts.map +1 -1
- package/dist/cjs/factories.js +3 -3
- package/dist/cjs/factories.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/factories.d.ts +1 -0
- package/dist/esm/factories.d.ts.map +1 -1
- package/dist/esm/factories.js +3 -3
- package/dist/esm/factories.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/package.json +16 -16
- package/src/factories.ts +4 -3
- package/test/util.ts +1 -14
- package/test/wallet/PersonalWallet/delegationDistribution.test.ts +63 -16
- package/test/wallet/PersonalWallet/handle.test.ts +271 -0
- package/test/wallet/PersonalWallet/multiAddress.test.ts +1 -17
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cardano-sdk/e2e",
|
|
3
|
-
"version": "0.
|
|
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.
|
|
84
|
-
"@cardano-sdk/cardano-services-client": "~0.9.
|
|
85
|
-
"@cardano-sdk/core": "~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.
|
|
88
|
-
"@cardano-sdk/key-management": "~0.7.
|
|
89
|
-
"@cardano-sdk/ogmios": "~0.12.
|
|
90
|
-
"@cardano-sdk/tx-construction": "~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.
|
|
93
|
-
"@cardano-sdk/util-rxjs": "~0.4.
|
|
94
|
-
"@cardano-sdk/wallet": "~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.
|
|
120
|
-
"@cardano-sdk/projection": "~0.6.
|
|
121
|
-
"@cardano-sdk/projection-typeorm": "~0.3.
|
|
122
|
-
"@cardano-sdk/web-extension": "~0.12.
|
|
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": "
|
|
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
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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:
|
|
133
|
-
await
|
|
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
|
-
|
|
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);
|