@cardano-sdk/e2e 0.32.8 → 0.33.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 +29 -0
- package/dist/cjs/factories.d.ts +26 -2
- package/dist/cjs/factories.d.ts.map +1 -1
- package/dist/cjs/factories.js +30 -2
- package/dist/cjs/factories.js.map +1 -1
- package/dist/cjs/tools/multi-delegation-data-gen/index.js +3 -3
- package/dist/cjs/tools/multi-delegation-data-gen/index.js.map +1 -1
- package/dist/cjs/tools/multi-delegation-data-gen/utils/utils.d.ts +8 -8
- package/dist/cjs/tools/multi-delegation-data-gen/utils/utils.d.ts.map +1 -1
- package/dist/cjs/tools/multi-delegation-data-gen/utils/utils.js +1 -1
- package/dist/cjs/tools/multi-delegation-data-gen/utils/utils.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/cjs/util/handle-util.d.ts.map +1 -1
- package/dist/cjs/util/handle-util.js +8 -2
- package/dist/cjs/util/handle-util.js.map +1 -1
- package/dist/cjs/util/util.d.ts +4 -4
- package/dist/cjs/util/util.d.ts.map +1 -1
- package/dist/cjs/util/util.js +13 -6
- package/dist/cjs/util/util.js.map +1 -1
- package/dist/esm/factories.d.ts +26 -2
- package/dist/esm/factories.d.ts.map +1 -1
- package/dist/esm/factories.js +29 -2
- package/dist/esm/factories.js.map +1 -1
- package/dist/esm/tools/multi-delegation-data-gen/index.js +3 -3
- package/dist/esm/tools/multi-delegation-data-gen/index.js.map +1 -1
- package/dist/esm/tools/multi-delegation-data-gen/utils/utils.d.ts +8 -8
- package/dist/esm/tools/multi-delegation-data-gen/utils/utils.d.ts.map +1 -1
- package/dist/esm/tools/multi-delegation-data-gen/utils/utils.js +1 -1
- package/dist/esm/tools/multi-delegation-data-gen/utils/utils.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/util/handle-util.d.ts.map +1 -1
- package/dist/esm/util/handle-util.js +8 -2
- package/dist/esm/util/handle-util.js.map +1 -1
- package/dist/esm/util/util.d.ts +4 -4
- package/dist/esm/util/util.d.ts.map +1 -1
- package/dist/esm/util/util.js +14 -7
- package/dist/esm/util/util.js.map +1 -1
- package/package.json +18 -18
- package/src/factories.ts +71 -2
- package/src/tools/multi-delegation-data-gen/index.ts +4 -4
- package/src/tools/multi-delegation-data-gen/utils/utils.ts +12 -12
- package/src/util/handle-util.ts +8 -2
- package/src/util/util.ts +19 -10
- package/test/artillery/wallet-restoration/types.ts +2 -2
- package/test/load-test-custom/wallet-init/wallet-init.test.ts +4 -4
- package/test/load-test-custom/wallet-restoration/wallet-restoration.test.ts +2 -2
- package/test/long-running/delegation-rewards.test.ts +6 -6
- package/test/long-running/multisig-wallet/multisig-delegation-rewards.test.ts +6 -6
- package/test/long-running/shared-wallet-delegation-rewards.test.ts +199 -0
- package/test/wallet/PersonalWallet/byron.test.ts +2 -2
- package/test/wallet/PersonalWallet/delegation.test.ts +5 -5
- package/test/wallet/PersonalWallet/delegationDistribution.test.ts +15 -15
- package/test/wallet/PersonalWallet/handle.test.ts +4 -4
- package/test/wallet/PersonalWallet/metadata.test.ts +2 -2
- package/test/wallet/PersonalWallet/mint.test.ts +10 -4
- package/test/wallet/PersonalWallet/multiAddress.test.ts +2 -2
- package/test/wallet/PersonalWallet/multisignature.test.ts +10 -4
- package/test/wallet/PersonalWallet/nft.test.ts +27 -9
- package/test/wallet/PersonalWallet/phase2validation.test.ts +4 -4
- package/test/wallet/PersonalWallet/pouchDbWalletStores.test.ts +1 -1
- package/test/wallet/PersonalWallet/txChainHistory.test.ts +4 -3
- package/test/wallet/PersonalWallet/unspendableUtxos.test.ts +3 -3
- package/test/wallet/SharedWallet/delegation.test.ts +245 -0
- package/test/wallet/SharedWallet/simpleTx.test.ts +123 -0
- package/test/wallet/SharedWallet/ultils.ts +327 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable sonarjs/no-duplicate-string */
|
|
2
2
|
import { Asset, Cardano, metadatum, nativeScriptPolicyId } from '@cardano-sdk/core';
|
|
3
|
-
import { Assets,
|
|
3
|
+
import { Assets, BaseWallet, FinalizeTxProps } from '@cardano-sdk/wallet';
|
|
4
4
|
import { InitializeTxProps } from '@cardano-sdk/tx-construction';
|
|
5
5
|
import { KeyRole, TransactionSigner, util } from '@cardano-sdk/key-management';
|
|
6
6
|
import {
|
|
@@ -21,7 +21,7 @@ const env = getEnv(walletVariables);
|
|
|
21
21
|
const logger = createLogger();
|
|
22
22
|
|
|
23
23
|
// Returns [assets in wallet balance, assetInfos with nftMetadata for the assets in the balance]
|
|
24
|
-
const walletBalanceAssetsAndNfts = (wallet:
|
|
24
|
+
const walletBalanceAssetsAndNfts = (wallet: BaseWallet) =>
|
|
25
25
|
combineLatest([wallet.balance.utxo.total$, wallet.assetInfo$]).pipe(
|
|
26
26
|
filter(([balance]) => !!balance.assets),
|
|
27
27
|
map(([balance, assetInfos]): [Cardano.TokenMap, Assets] => [balance.assets!, assetInfos]),
|
|
@@ -41,7 +41,7 @@ describe('PersonalWallet.assets/nft', () => {
|
|
|
41
41
|
const TOKEN_METADATA_2_INDEX = 1;
|
|
42
42
|
const TOKEN_BURN_INDEX = 2;
|
|
43
43
|
|
|
44
|
-
let wallet:
|
|
44
|
+
let wallet: BaseWallet;
|
|
45
45
|
let policySigner: TransactionSigner;
|
|
46
46
|
let policyId: Cardano.PolicyId;
|
|
47
47
|
let policyScript: Cardano.NativeScript;
|
|
@@ -160,15 +160,21 @@ describe('PersonalWallet.assets/nft', () => {
|
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
162
|
]),
|
|
163
|
-
|
|
163
|
+
signingOptions: {
|
|
164
|
+
extraSigners: [policySigner]
|
|
165
|
+
},
|
|
166
|
+
witness: { scripts: [policyScript] }
|
|
164
167
|
};
|
|
165
168
|
|
|
166
169
|
const unsignedTx = await wallet.initializeTx(txProps);
|
|
167
170
|
|
|
168
171
|
const finalizeProps: FinalizeTxProps = {
|
|
169
172
|
auxiliaryData,
|
|
173
|
+
signingOptions: {
|
|
174
|
+
extraSigners: [policySigner]
|
|
175
|
+
},
|
|
170
176
|
tx: unsignedTx,
|
|
171
|
-
witness: {
|
|
177
|
+
witness: { scripts: [policyScript] }
|
|
172
178
|
};
|
|
173
179
|
|
|
174
180
|
const signedTx = await wallet.finalizeTx(finalizeProps);
|
|
@@ -270,14 +276,20 @@ describe('PersonalWallet.assets/nft', () => {
|
|
|
270
276
|
}
|
|
271
277
|
}
|
|
272
278
|
]),
|
|
273
|
-
|
|
279
|
+
signingOptions: {
|
|
280
|
+
extraSigners: [policySigner]
|
|
281
|
+
},
|
|
282
|
+
witness: { scripts: [policyScript] }
|
|
274
283
|
};
|
|
275
284
|
|
|
276
285
|
const unsignedTx = await wallet.initializeTx(txProps);
|
|
277
286
|
|
|
278
287
|
const finalizeProps: FinalizeTxProps = {
|
|
288
|
+
signingOptions: {
|
|
289
|
+
extraSigners: [policySigner]
|
|
290
|
+
},
|
|
279
291
|
tx: unsignedTx,
|
|
280
|
-
witness: {
|
|
292
|
+
witness: { scripts: [policyScript] }
|
|
281
293
|
};
|
|
282
294
|
|
|
283
295
|
const signedTx = await wallet.finalizeTx(finalizeProps);
|
|
@@ -334,15 +346,21 @@ describe('PersonalWallet.assets/nft', () => {
|
|
|
334
346
|
}
|
|
335
347
|
}
|
|
336
348
|
]),
|
|
337
|
-
|
|
349
|
+
signingOptions: {
|
|
350
|
+
extraSigners: [policySigner]
|
|
351
|
+
},
|
|
352
|
+
witness: { scripts: [policyScript] }
|
|
338
353
|
};
|
|
339
354
|
|
|
340
355
|
const unsignedTx = await wallet.initializeTx(txProps);
|
|
341
356
|
|
|
342
357
|
const finalizeProps: FinalizeTxProps = {
|
|
343
358
|
auxiliaryData,
|
|
359
|
+
signingOptions: {
|
|
360
|
+
extraSigners: [policySigner]
|
|
361
|
+
},
|
|
344
362
|
tx: unsignedTx,
|
|
345
|
-
witness: {
|
|
363
|
+
witness: { scripts: [policyScript] }
|
|
346
364
|
};
|
|
347
365
|
|
|
348
366
|
const signedTx = await wallet.finalizeTx(finalizeProps);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { BaseWallet, FinalizeTxProps, TransactionFailure } from '@cardano-sdk/wallet';
|
|
1
2
|
import { Cardano, Serialization } from '@cardano-sdk/core';
|
|
2
|
-
import { FinalizeTxProps, PersonalWallet, TransactionFailure } from '@cardano-sdk/wallet';
|
|
3
3
|
import { Hash32ByteBase16 } from '@cardano-sdk/crypto';
|
|
4
4
|
import { HexBlob, isNotNil } from '@cardano-sdk/util';
|
|
5
5
|
import { InitializeTxProps } from '@cardano-sdk/tx-construction';
|
|
@@ -16,7 +16,7 @@ const logger = createLogger();
|
|
|
16
16
|
* @param wallet The wallet which will set the collateral.
|
|
17
17
|
*/
|
|
18
18
|
const createCollateral = async (
|
|
19
|
-
wallet:
|
|
19
|
+
wallet: BaseWallet
|
|
20
20
|
): Promise<{ collateralInput: Cardano.TxIn; collateralCoinValue: bigint }> => {
|
|
21
21
|
const txBuilder = wallet.createTxBuilder();
|
|
22
22
|
|
|
@@ -53,7 +53,7 @@ const createCollateral = async (
|
|
|
53
53
|
};
|
|
54
54
|
|
|
55
55
|
describe('PersonalWallet/phase2validation', () => {
|
|
56
|
-
let wallet:
|
|
56
|
+
let wallet: BaseWallet;
|
|
57
57
|
|
|
58
58
|
afterAll(() => {
|
|
59
59
|
wallet.shutdown();
|
|
@@ -150,6 +150,6 @@ describe('PersonalWallet/phase2validation', () => {
|
|
|
150
150
|
expect([TransactionFailure.Phase2Validation, TransactionFailure.FailedToSubmit].includes(failedTx.reason)).toBe(
|
|
151
151
|
true
|
|
152
152
|
);
|
|
153
|
-
expect(txFoundInHistory.body.
|
|
153
|
+
expect(txFoundInHistory.body.totalCollateral).toEqual(collateralCoinValue);
|
|
154
154
|
});
|
|
155
155
|
});
|
|
@@ -14,7 +14,7 @@ describe('PersonalWallet/pouchDbWalletStores', () => {
|
|
|
14
14
|
stores1 = storage.createPouchDbWalletStores(walletName, { logger });
|
|
15
15
|
});
|
|
16
16
|
|
|
17
|
-
it('stores and restores
|
|
17
|
+
it('stores and restores BaseWallet, continues sync after initial load', async () => {
|
|
18
18
|
const wallet1 = (await getWallet({ env, logger, name: 'Test Wallet', stores: stores1 })).wallet;
|
|
19
19
|
|
|
20
20
|
// wallet1 fetched all responses from wallet provider
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { BaseWallet } from '@cardano-sdk/wallet';
|
|
1
2
|
import { Cardano, CardanoNodeUtil, ProviderError } from '@cardano-sdk/core';
|
|
2
|
-
import { PersonalWallet } from '@cardano-sdk/wallet';
|
|
3
3
|
import { filter, firstValueFrom, map, take } from 'rxjs';
|
|
4
4
|
import { getEnv, getWallet, normalizeTxBody, walletReady, walletVariables } from '../../../src';
|
|
5
5
|
import { isNotNil } from '@cardano-sdk/util';
|
|
@@ -8,7 +8,7 @@ import { logger } from '@cardano-sdk/util-dev';
|
|
|
8
8
|
const env = getEnv(walletVariables);
|
|
9
9
|
|
|
10
10
|
describe('PersonalWallet/txChainHistory', () => {
|
|
11
|
-
let wallet:
|
|
11
|
+
let wallet: BaseWallet;
|
|
12
12
|
let signedTx: Cardano.Tx<Cardano.TxBody>;
|
|
13
13
|
|
|
14
14
|
beforeEach(async () => {
|
|
@@ -61,7 +61,8 @@ describe('PersonalWallet/txChainHistory', () => {
|
|
|
61
61
|
expect(normalizeTxBody(txFoundInHistory.body)).toEqual(normalizeTxBody(signedTx.body));
|
|
62
62
|
});
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
// TODO LW-9972
|
|
65
|
+
it.skip('can detect a ValueNotConserved error', async () => {
|
|
65
66
|
expect.assertions(1);
|
|
66
67
|
// Search chain history to see if the transaction is there.
|
|
67
68
|
await firstValueFrom(
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* eslint-disable sonarjs/no-duplicate-string */
|
|
2
|
-
import {
|
|
2
|
+
import { BaseWallet, utxoEquals } from '@cardano-sdk/wallet';
|
|
3
3
|
import { createLogger } from '@cardano-sdk/util-dev';
|
|
4
4
|
import { filter, firstValueFrom, map, take } from 'rxjs';
|
|
5
5
|
import { firstValueFromTimed, getEnv, getWallet, walletReady, walletVariables } from '../../../src';
|
|
@@ -9,8 +9,8 @@ const env = getEnv(walletVariables);
|
|
|
9
9
|
const logger = createLogger();
|
|
10
10
|
|
|
11
11
|
describe('PersonalWallet/unspendableUtxos', () => {
|
|
12
|
-
let wallet1:
|
|
13
|
-
let wallet2:
|
|
12
|
+
let wallet1: BaseWallet;
|
|
13
|
+
let wallet2: BaseWallet;
|
|
14
14
|
|
|
15
15
|
afterAll(() => {
|
|
16
16
|
wallet1.shutdown();
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/* eslint-disable max-statements */
|
|
2
|
+
import { BaseWallet, ObservableWallet } from '@cardano-sdk/wallet';
|
|
3
|
+
import { BigIntMath, isNotNil } from '@cardano-sdk/util';
|
|
4
|
+
import { Cardano, StakePoolProvider } from '@cardano-sdk/core';
|
|
5
|
+
import {
|
|
6
|
+
TX_TIMEOUT_DEFAULT,
|
|
7
|
+
firstValueFromTimed,
|
|
8
|
+
getEnv,
|
|
9
|
+
getWallet,
|
|
10
|
+
normalizeTxBody,
|
|
11
|
+
waitForWalletStateSettle,
|
|
12
|
+
walletReady,
|
|
13
|
+
walletVariables
|
|
14
|
+
} from '../../../src';
|
|
15
|
+
import { buildSharedWallets } from './ultils';
|
|
16
|
+
import { combineLatest, filter, firstValueFrom, map, take } from 'rxjs';
|
|
17
|
+
import { logger } from '@cardano-sdk/util-dev';
|
|
18
|
+
|
|
19
|
+
const env = getEnv(walletVariables);
|
|
20
|
+
|
|
21
|
+
const getWalletStateSnapshot = async (wallet: ObservableWallet) => {
|
|
22
|
+
const [rewardAccount] = await firstValueFrom(wallet.delegation.rewardAccounts$);
|
|
23
|
+
const [publicStakeKey] = await firstValueFrom(wallet.publicStakeKeys$);
|
|
24
|
+
const balanceAvailable = await firstValueFrom(wallet.balance.utxo.available$);
|
|
25
|
+
const balanceTotal = await firstValueFrom(wallet.balance.utxo.total$);
|
|
26
|
+
const deposit = await firstValueFrom(wallet.balance.rewardAccounts.deposit$);
|
|
27
|
+
const epoch = await firstValueFrom(wallet.currentEpoch$);
|
|
28
|
+
const utxoTotal = await firstValueFrom(wallet.utxo.total$);
|
|
29
|
+
const utxoAvailable = await firstValueFrom(wallet.utxo.available$);
|
|
30
|
+
const rewardsBalance = await firstValueFrom(wallet.balance.rewardAccounts.rewards$);
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
balance: { available: balanceAvailable, deposit, total: balanceTotal },
|
|
34
|
+
epoch: epoch.epochNo,
|
|
35
|
+
isStakeCredentialRegistered: rewardAccount.credentialStatus === Cardano.StakeCredentialStatus.Registered,
|
|
36
|
+
publicStakeKey,
|
|
37
|
+
rewardAccount,
|
|
38
|
+
rewardsBalance,
|
|
39
|
+
utxo: { available: utxoTotal, total: utxoAvailable }
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const waitForTx = async (wallet: ObservableWallet, hash: Cardano.TransactionId) => {
|
|
44
|
+
await firstValueFromTimed(
|
|
45
|
+
combineLatest([
|
|
46
|
+
wallet.transactions.history$.pipe(filter((txs) => txs.some(({ id }) => id === hash))),
|
|
47
|
+
// test that onChain$ works
|
|
48
|
+
wallet.transactions.outgoing.onChain$.pipe(filter(({ id }) => id === hash))
|
|
49
|
+
]),
|
|
50
|
+
'Tx not found on-chain for too long',
|
|
51
|
+
TX_TIMEOUT_DEFAULT
|
|
52
|
+
);
|
|
53
|
+
await waitForWalletStateSettle(wallet);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
describe('SharedWallet/delegation', () => {
|
|
57
|
+
let fundingTx: Cardano.Tx<Cardano.TxBody>;
|
|
58
|
+
let faucetWallet: BaseWallet;
|
|
59
|
+
let faucetAddress: Cardano.PaymentAddress;
|
|
60
|
+
let aliceMultiSigWallet: BaseWallet;
|
|
61
|
+
let bobMultiSigWallet: BaseWallet;
|
|
62
|
+
let charlotteMultiSigWallet: BaseWallet;
|
|
63
|
+
let stakePoolProvider: StakePoolProvider;
|
|
64
|
+
|
|
65
|
+
const initialFunds = 10_000_000n;
|
|
66
|
+
|
|
67
|
+
beforeAll(async () => {
|
|
68
|
+
jest.setTimeout(180_000);
|
|
69
|
+
|
|
70
|
+
({
|
|
71
|
+
wallet: faucetWallet,
|
|
72
|
+
providers: { stakePoolProvider }
|
|
73
|
+
} = await getWallet({ env, logger, name: 'Sending Wallet', polling: { interval: 50 } }));
|
|
74
|
+
|
|
75
|
+
// Make sure the wallet has sufficient funds to run this test
|
|
76
|
+
await walletReady(faucetWallet, initialFunds);
|
|
77
|
+
|
|
78
|
+
faucetAddress = (await firstValueFrom(faucetWallet.addresses$))[0].address;
|
|
79
|
+
|
|
80
|
+
({ aliceMultiSigWallet, bobMultiSigWallet, charlotteMultiSigWallet } = await buildSharedWallets(
|
|
81
|
+
env,
|
|
82
|
+
await firstValueFrom(faucetWallet.genesisParameters$),
|
|
83
|
+
logger
|
|
84
|
+
));
|
|
85
|
+
|
|
86
|
+
await Promise.all([
|
|
87
|
+
waitForWalletStateSettle(aliceMultiSigWallet),
|
|
88
|
+
waitForWalletStateSettle(bobMultiSigWallet),
|
|
89
|
+
waitForWalletStateSettle(charlotteMultiSigWallet)
|
|
90
|
+
]);
|
|
91
|
+
|
|
92
|
+
const [{ address: receivingAddress }] = await firstValueFrom(aliceMultiSigWallet.addresses$);
|
|
93
|
+
|
|
94
|
+
logger.info(`Address ${faucetAddress} will send ${initialFunds} lovelace to address ${receivingAddress}.`);
|
|
95
|
+
|
|
96
|
+
// Send 10 tADA to the shared wallet.
|
|
97
|
+
const txBuilder = faucetWallet.createTxBuilder();
|
|
98
|
+
const txOutput = await txBuilder.buildOutput().address(receivingAddress).coin(initialFunds).build();
|
|
99
|
+
fundingTx = (await txBuilder.addOutput(txOutput).build().sign()).tx;
|
|
100
|
+
await faucetWallet.submitTx(fundingTx);
|
|
101
|
+
|
|
102
|
+
logger.info(
|
|
103
|
+
`Submitted transaction id: ${fundingTx.id}, inputs: ${JSON.stringify(
|
|
104
|
+
fundingTx.body.inputs.map((txIn) => [txIn.txId, txIn.index])
|
|
105
|
+
)} and outputs:${JSON.stringify(
|
|
106
|
+
fundingTx.body.outputs.map((txOut) => [txOut.address, Number.parseInt(txOut.value.coins.toString())])
|
|
107
|
+
)}.`
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
afterAll(() => {
|
|
112
|
+
faucetWallet.shutdown();
|
|
113
|
+
aliceMultiSigWallet.shutdown();
|
|
114
|
+
bobMultiSigWallet.shutdown();
|
|
115
|
+
charlotteMultiSigWallet.shutdown();
|
|
116
|
+
faucetWallet.shutdown();
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const chooseDifferentPoolIdRandomly = async (delegateeBefore1stTx?: Cardano.PoolId): Promise<Cardano.PoolId> => {
|
|
120
|
+
const activePools = await stakePoolProvider.queryStakePools({
|
|
121
|
+
filters: { status: [Cardano.StakePoolStatus.Active] },
|
|
122
|
+
pagination: { limit: 2, startAt: 0 }
|
|
123
|
+
});
|
|
124
|
+
const filteredPools = activePools.pageResults.filter(({ id }) => id !== delegateeBefore1stTx);
|
|
125
|
+
return filteredPools[Math.round(Math.random() * (filteredPools.length - 1))]?.id;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
test('delegation preconditions', async () => {
|
|
129
|
+
const addresses = await firstValueFrom(aliceMultiSigWallet.addresses$);
|
|
130
|
+
const currentEpoch = await firstValueFrom(aliceMultiSigWallet.currentEpoch$);
|
|
131
|
+
expect(addresses[0].rewardAccount).toBeTruthy();
|
|
132
|
+
expect(currentEpoch.epochNo).toBeGreaterThan(0);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test('balance & transaction', async () => {
|
|
136
|
+
const txFoundInHistory = await firstValueFrom(
|
|
137
|
+
aliceMultiSigWallet.transactions.history$.pipe(
|
|
138
|
+
map((txs) => txs.find((tx) => tx.id === fundingTx.id)),
|
|
139
|
+
filter(isNotNil),
|
|
140
|
+
take(1)
|
|
141
|
+
)
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
logger.info(`Found transaction id in chain history: ${txFoundInHistory.id}`);
|
|
145
|
+
|
|
146
|
+
expect(txFoundInHistory).toBeDefined();
|
|
147
|
+
expect(txFoundInHistory.id).toEqual(fundingTx.id);
|
|
148
|
+
expect(normalizeTxBody(txFoundInHistory.body)).toEqual(normalizeTxBody(fundingTx.body));
|
|
149
|
+
|
|
150
|
+
const tx1OutputCoins = 1_000_000n;
|
|
151
|
+
await walletReady(aliceMultiSigWallet, tx1OutputCoins);
|
|
152
|
+
|
|
153
|
+
const protocolParameters = await firstValueFrom(aliceMultiSigWallet.protocolParameters$);
|
|
154
|
+
const stakeKeyDeposit = BigInt(protocolParameters.stakeKeyDeposit);
|
|
155
|
+
const initialState = await getWalletStateSnapshot(aliceMultiSigWallet);
|
|
156
|
+
expect(initialState.balance.total.coins).toBe(initialState.balance.available.coins);
|
|
157
|
+
const poolId = await chooseDifferentPoolIdRandomly(initialState.rewardAccount.delegatee?.nextNextEpoch?.id);
|
|
158
|
+
expect(poolId).toBeDefined();
|
|
159
|
+
const initialDeposit = initialState.isStakeCredentialRegistered ? stakeKeyDeposit : 0n;
|
|
160
|
+
expect(initialState.balance.deposit).toBe(initialDeposit);
|
|
161
|
+
|
|
162
|
+
// Make a 1st tx with key registration (if not already registered) and stake delegation
|
|
163
|
+
// Also send some coin to another wallet
|
|
164
|
+
const destAddresses = (await firstValueFrom(faucetWallet.addresses$))[0].address;
|
|
165
|
+
const txBuilder = aliceMultiSigWallet.createTxBuilder();
|
|
166
|
+
|
|
167
|
+
let tx = (
|
|
168
|
+
await txBuilder
|
|
169
|
+
.addOutput(await txBuilder.buildOutput().address(destAddresses).coin(tx1OutputCoins).build())
|
|
170
|
+
.delegateFirstStakeCredential(poolId)
|
|
171
|
+
.build()
|
|
172
|
+
.sign()
|
|
173
|
+
).tx;
|
|
174
|
+
|
|
175
|
+
tx = await bobMultiSigWallet.updateWitness({ sender: { id: 'e2e' }, tx });
|
|
176
|
+
tx = await charlotteMultiSigWallet.updateWitness({ sender: { id: 'e2e' }, tx });
|
|
177
|
+
await aliceMultiSigWallet.submitTx(tx);
|
|
178
|
+
|
|
179
|
+
// Test it locks available balance after tx is submitted
|
|
180
|
+
await firstValueFromTimed(
|
|
181
|
+
aliceMultiSigWallet.transactions.outgoing.inFlight$.pipe(filter((inFlight) => inFlight.length === 1)),
|
|
182
|
+
'No tx in flight'
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
const tx1PendingState = await getWalletStateSnapshot(aliceMultiSigWallet);
|
|
186
|
+
|
|
187
|
+
// Updates total and available balance right after tx is submitted
|
|
188
|
+
const coinsSpentOnDeposit = initialState.isStakeCredentialRegistered ? 0n : stakeKeyDeposit;
|
|
189
|
+
const newRewardsWhileSigningAndSubmitting = tx1PendingState.rewardsBalance;
|
|
190
|
+
const expectedCoinsAfterTx1 =
|
|
191
|
+
initialState.balance.total.coins -
|
|
192
|
+
tx1OutputCoins -
|
|
193
|
+
tx.body.fee -
|
|
194
|
+
coinsSpentOnDeposit +
|
|
195
|
+
BigIntMath.sum(tx.body.withdrawals?.map((wd) => wd.quantity) || []) +
|
|
196
|
+
newRewardsWhileSigningAndSubmitting;
|
|
197
|
+
expect(tx1PendingState.balance.total.coins).toEqual(expectedCoinsAfterTx1);
|
|
198
|
+
expect(tx1PendingState.balance.available.coins).toEqual(expectedCoinsAfterTx1);
|
|
199
|
+
expect(tx1PendingState.balance.deposit).toEqual(stakeKeyDeposit);
|
|
200
|
+
|
|
201
|
+
await waitForTx(aliceMultiSigWallet, tx.id);
|
|
202
|
+
const tx1ConfirmedState = await getWalletStateSnapshot(aliceMultiSigWallet);
|
|
203
|
+
|
|
204
|
+
// Updates total and available balance after tx is on-chain
|
|
205
|
+
expect(tx1ConfirmedState.balance.total.coins).toBe(expectedCoinsAfterTx1);
|
|
206
|
+
expect(tx1ConfirmedState.balance.total).toEqual(tx1ConfirmedState.balance.available);
|
|
207
|
+
expect(tx1PendingState.balance.deposit).toEqual(stakeKeyDeposit);
|
|
208
|
+
|
|
209
|
+
// If less than two epochs have elapsed, delegatee will still delegate to former pool during current epoch
|
|
210
|
+
// if more than two epochs has elapsed, delegatee will delegate to new pool.
|
|
211
|
+
if (tx1ConfirmedState.epoch - initialState.epoch < 2) {
|
|
212
|
+
expect(tx1ConfirmedState.rewardAccount.delegatee?.currentEpoch?.id).toEqual(
|
|
213
|
+
initialState?.rewardAccount.delegatee?.currentEpoch?.id
|
|
214
|
+
);
|
|
215
|
+
expect(tx1ConfirmedState.rewardAccount.delegatee?.nextEpoch?.id).toEqual(
|
|
216
|
+
initialState?.rewardAccount.delegatee?.nextEpoch?.id
|
|
217
|
+
);
|
|
218
|
+
expect(tx1ConfirmedState.rewardAccount.delegatee?.nextNextEpoch?.id).toEqual(poolId);
|
|
219
|
+
} else {
|
|
220
|
+
expect(tx1ConfirmedState.rewardAccount.delegatee?.currentEpoch?.id).toEqual(poolId);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
expect(tx1ConfirmedState.rewardAccount.credentialStatus).toBe(Cardano.StakeCredentialStatus.Registered);
|
|
224
|
+
|
|
225
|
+
// Make a 2nd tx with key de-registration
|
|
226
|
+
tx = (await aliceMultiSigWallet.createTxBuilder().delegateFirstStakeCredential(null).build().sign()).tx;
|
|
227
|
+
tx = await bobMultiSigWallet.updateWitness({ sender: { id: 'e2e' }, tx });
|
|
228
|
+
tx = await charlotteMultiSigWallet.updateWitness({ sender: { id: 'e2e' }, tx });
|
|
229
|
+
|
|
230
|
+
await aliceMultiSigWallet.submitTx(tx);
|
|
231
|
+
|
|
232
|
+
await waitForTx(aliceMultiSigWallet, tx.id);
|
|
233
|
+
const tx2ConfirmedState = await getWalletStateSnapshot(aliceMultiSigWallet);
|
|
234
|
+
|
|
235
|
+
// No longer delegating
|
|
236
|
+
expect(tx2ConfirmedState.rewardAccount.delegatee?.nextNextEpoch?.id).toBeUndefined();
|
|
237
|
+
expect(tx2ConfirmedState.rewardAccount.credentialStatus).toBe(Cardano.StakeCredentialStatus.Unregistered);
|
|
238
|
+
|
|
239
|
+
// Deposit is returned to wallet balance
|
|
240
|
+
const expectedCoinsAfterTx2 = expectedCoinsAfterTx1 + stakeKeyDeposit - tx.body.fee;
|
|
241
|
+
expect(tx2ConfirmedState.balance.total.coins).toBe(expectedCoinsAfterTx2);
|
|
242
|
+
expect(tx2ConfirmedState.balance.total).toEqual(tx2ConfirmedState.balance.available);
|
|
243
|
+
expect(tx2ConfirmedState.balance.deposit).toBe(0n);
|
|
244
|
+
});
|
|
245
|
+
});
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { BaseWallet } from '@cardano-sdk/wallet';
|
|
2
|
+
import { Cardano } from '@cardano-sdk/core';
|
|
3
|
+
import { buildSharedWallets } from './ultils';
|
|
4
|
+
import { filter, firstValueFrom, map, take } from 'rxjs';
|
|
5
|
+
import {
|
|
6
|
+
getEnv,
|
|
7
|
+
getWallet,
|
|
8
|
+
normalizeTxBody,
|
|
9
|
+
waitForWalletStateSettle,
|
|
10
|
+
walletReady,
|
|
11
|
+
walletVariables
|
|
12
|
+
} from '../../../src';
|
|
13
|
+
import { isNotNil } from '@cardano-sdk/util';
|
|
14
|
+
import { logger } from '@cardano-sdk/util-dev';
|
|
15
|
+
|
|
16
|
+
const env = getEnv(walletVariables);
|
|
17
|
+
|
|
18
|
+
describe('SharedWallet/simpleTx', () => {
|
|
19
|
+
let fundingTx: Cardano.Tx<Cardano.TxBody>;
|
|
20
|
+
let faucetWallet: BaseWallet;
|
|
21
|
+
let faucetAddress: Cardano.PaymentAddress;
|
|
22
|
+
let aliceMultiSigWallet: BaseWallet;
|
|
23
|
+
let bobMultiSigWallet: BaseWallet;
|
|
24
|
+
let charlotteMultiSigWallet: BaseWallet;
|
|
25
|
+
const initialFunds = 10_000_000n;
|
|
26
|
+
|
|
27
|
+
beforeAll(async () => {
|
|
28
|
+
({ wallet: faucetWallet } = await getWallet({ env, logger, name: 'Sending Wallet', polling: { interval: 50 } }));
|
|
29
|
+
|
|
30
|
+
// Make sure the wallet has sufficient funds to run this test
|
|
31
|
+
await walletReady(faucetWallet, initialFunds);
|
|
32
|
+
|
|
33
|
+
faucetAddress = (await firstValueFrom(faucetWallet.addresses$))[0].address;
|
|
34
|
+
|
|
35
|
+
({ aliceMultiSigWallet, bobMultiSigWallet, charlotteMultiSigWallet } = await buildSharedWallets(
|
|
36
|
+
env,
|
|
37
|
+
await firstValueFrom(faucetWallet.genesisParameters$),
|
|
38
|
+
logger
|
|
39
|
+
));
|
|
40
|
+
|
|
41
|
+
await Promise.all([
|
|
42
|
+
waitForWalletStateSettle(aliceMultiSigWallet),
|
|
43
|
+
waitForWalletStateSettle(bobMultiSigWallet),
|
|
44
|
+
waitForWalletStateSettle(charlotteMultiSigWallet)
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
const [{ address: receivingAddress }] = await firstValueFrom(aliceMultiSigWallet.addresses$);
|
|
48
|
+
|
|
49
|
+
logger.info(`Address ${faucetAddress} will send ${initialFunds} lovelace to address ${receivingAddress}.`);
|
|
50
|
+
|
|
51
|
+
// Send 10 tADA to the shared wallet.
|
|
52
|
+
const txBuilder = faucetWallet.createTxBuilder();
|
|
53
|
+
const txOutput = await txBuilder.buildOutput().address(receivingAddress).coin(initialFunds).build();
|
|
54
|
+
fundingTx = (await txBuilder.addOutput(txOutput).build().sign()).tx;
|
|
55
|
+
await faucetWallet.submitTx(fundingTx);
|
|
56
|
+
|
|
57
|
+
logger.info(
|
|
58
|
+
`Submitted transaction id: ${fundingTx.id}, inputs: ${JSON.stringify(
|
|
59
|
+
fundingTx.body.inputs.map((txIn) => [txIn.txId, txIn.index])
|
|
60
|
+
)} and outputs:${JSON.stringify(
|
|
61
|
+
fundingTx.body.outputs.map((txOut) => [txOut.address, Number.parseInt(txOut.value.coins.toString())])
|
|
62
|
+
)}.`
|
|
63
|
+
);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
afterAll(() => {
|
|
67
|
+
aliceMultiSigWallet.shutdown();
|
|
68
|
+
bobMultiSigWallet.shutdown();
|
|
69
|
+
charlotteMultiSigWallet.shutdown();
|
|
70
|
+
faucetWallet.shutdown();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('submit a transaction and find it in chain history', async () => {
|
|
74
|
+
const txFoundInHistory = await firstValueFrom(
|
|
75
|
+
aliceMultiSigWallet.transactions.history$.pipe(
|
|
76
|
+
map((txs) => txs.find((tx) => tx.id === fundingTx.id)),
|
|
77
|
+
filter(isNotNil),
|
|
78
|
+
take(1)
|
|
79
|
+
)
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
logger.info(`Found transaction id in chain history: ${txFoundInHistory.id}`);
|
|
83
|
+
|
|
84
|
+
expect(txFoundInHistory).toBeDefined();
|
|
85
|
+
expect(txFoundInHistory.id).toEqual(fundingTx.id);
|
|
86
|
+
expect(normalizeTxBody(txFoundInHistory.body)).toEqual(normalizeTxBody(fundingTx.body));
|
|
87
|
+
|
|
88
|
+
const aliceAddress = (await firstValueFrom(aliceMultiSigWallet.addresses$))[0].address;
|
|
89
|
+
const bobAddress = (await firstValueFrom(bobMultiSigWallet.addresses$))[0].address;
|
|
90
|
+
const charlotteAddress = (await firstValueFrom(charlotteMultiSigWallet.addresses$))[0].address;
|
|
91
|
+
|
|
92
|
+
expect(aliceAddress).toEqual(bobAddress);
|
|
93
|
+
expect(aliceAddress).toEqual(charlotteAddress);
|
|
94
|
+
|
|
95
|
+
// Lets send ada from the shared wallet to the faucet wallet
|
|
96
|
+
|
|
97
|
+
// Alice will initiate the transaction.
|
|
98
|
+
const txBuilder = aliceMultiSigWallet.createTxBuilder();
|
|
99
|
+
const txOut = await txBuilder.buildOutput().address(faucetAddress).coin(1_000_000n).build();
|
|
100
|
+
let tx = (await txBuilder.addOutput(txOut).build().sign()).tx;
|
|
101
|
+
|
|
102
|
+
// Bob updates the transaction with his witness
|
|
103
|
+
tx = await bobMultiSigWallet.updateWitness({ sender: { id: 'e2e' }, tx });
|
|
104
|
+
|
|
105
|
+
// Charlotte updates the transaction with her witness
|
|
106
|
+
tx = await charlotteMultiSigWallet.updateWitness({ sender: { id: 'e2e' }, tx });
|
|
107
|
+
const txId = await charlotteMultiSigWallet.submitTx(tx);
|
|
108
|
+
|
|
109
|
+
const finalTxFound = await firstValueFrom(
|
|
110
|
+
aliceMultiSigWallet.transactions.history$.pipe(
|
|
111
|
+
map((txs) => txs.find((hydratedTx) => hydratedTx.id === txId)),
|
|
112
|
+
filter(isNotNil),
|
|
113
|
+
take(1)
|
|
114
|
+
)
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
logger.info(`Found transaction id in chain history: ${finalTxFound.id}`);
|
|
118
|
+
|
|
119
|
+
expect(finalTxFound).toBeDefined();
|
|
120
|
+
expect(finalTxFound.id).toEqual(tx.id);
|
|
121
|
+
expect(normalizeTxBody(finalTxFound.body)).toEqual(normalizeTxBody(tx.body));
|
|
122
|
+
});
|
|
123
|
+
});
|