@cardano-sdk/e2e 0.25.0 → 0.26.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/.env.example +1 -7
- package/CHANGELOG.md +13 -0
- package/README.md +0 -29
- package/dist/cjs/environment.d.ts.map +1 -1
- package/dist/cjs/environment.js.map +1 -1
- package/dist/cjs/factories.d.ts.map +1 -1
- package/dist/cjs/factories.js +7 -1
- package/dist/cjs/factories.js.map +1 -1
- package/dist/cjs/measurement-util.d.ts.map +1 -1
- package/dist/cjs/measurement-util.js.map +1 -1
- package/dist/cjs/scripts/is-local-network-ready.js.map +1 -1
- package/dist/cjs/scripts/mnemonic.js.map +1 -1
- package/dist/cjs/tools/multi-delegation-data-gen/utils/files.d.ts.map +1 -1
- package/dist/cjs/tools/multi-delegation-data-gen/utils/files.js.map +1 -1
- package/dist/cjs/tools/multi-delegation-data-gen/utils/terminal-progress-monitor.d.ts.map +1 -1
- package/dist/cjs/tools/multi-delegation-data-gen/utils/terminal-progress-monitor.js.map +1 -1
- 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.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/environment.d.ts.map +1 -1
- package/dist/esm/environment.js.map +1 -1
- package/dist/esm/factories.d.ts.map +1 -1
- package/dist/esm/factories.js +7 -1
- package/dist/esm/factories.js.map +1 -1
- package/dist/esm/measurement-util.d.ts.map +1 -1
- package/dist/esm/measurement-util.js.map +1 -1
- package/dist/esm/scripts/is-local-network-ready.js.map +1 -1
- package/dist/esm/scripts/mnemonic.js.map +1 -1
- package/dist/esm/tools/multi-delegation-data-gen/utils/files.d.ts.map +1 -1
- package/dist/esm/tools/multi-delegation-data-gen/utils/files.js.map +1 -1
- package/dist/esm/tools/multi-delegation-data-gen/utils/terminal-progress-monitor.d.ts.map +1 -1
- package/dist/esm/tools/multi-delegation-data-gen/utils/terminal-progress-monitor.js.map +1 -1
- 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.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/docker-compose.yml +4 -0
- package/jest.config.js +0 -1
- package/local-network/scripts/common.sh +19 -0
- package/local-network/scripts/make-babbage.sh +2 -1
- package/local-network/scripts/mint-handles.sh +2 -18
- package/local-network/scripts/mint-tokens.sh +2 -18
- package/local-network/scripts/mnemonic_keys.sh +0 -0
- package/local-network/scripts/setup-wallets.sh +2 -17
- package/local-network/templates/babbage/submit-api-config.json +115 -0
- package/package.json +27 -29
- package/src/environment.ts +2 -6
- package/src/factories.ts +11 -5
- package/src/measurement-util.ts +1 -4
- package/src/scripts/is-local-network-ready.ts +1 -3
- package/src/scripts/mnemonic.ts +1 -3
- package/src/tools/multi-delegation-data-gen/utils/files.ts +1 -3
- package/src/tools/multi-delegation-data-gen/utils/terminal-progress-monitor.ts +2 -6
- package/src/tools/multi-delegation-data-gen/utils/utils.ts +1 -3
- package/test/artillery/StakePoolSearch.ts +5 -16
- package/test/artillery/artillery.ts +20 -61
- package/test/artillery/wallet-restoration/WalletRestoration.ts +1 -3
- package/test/artillery/wallet-restoration/queries.ts +1 -3
- package/test/artillery/wallet-restoration/types.ts +1 -3
- package/test/load-test-custom/stake-pool-search/stake-pool-search.test.ts +1 -3
- package/test/load-test-custom/wallet-init/wallet-init.test.ts +10 -1
- package/test/load-test-custom/wallet-restoration/wallet-restoration.test.ts +1 -4
- package/test/local-network/register-pool.test.ts +24 -14
- package/test/long-running/cache-invalidation.test.ts +7 -4
- package/test/long-running/multisig-wallet/MultiSigTx.ts +117 -0
- package/test/long-running/multisig-wallet/MultiSigWallet.ts +491 -0
- package/test/long-running/multisig-wallet/multisig-delegation-rewards.test.ts +318 -0
- package/test/projection/offline-fork.test.ts +6 -6
- package/test/projection/single-tenant-utxo.test.ts +1 -1
- package/test/providers/StakePoolProvider.test.ts +22 -17
- package/test/wallet/PersonalWallet/delegation.test.ts +6 -3
- package/test/wallet/PersonalWallet/handle.test.ts +2 -1
- package/test/wallet/PersonalWallet/mint.test.ts +2 -1
- package/test/wallet/PersonalWallet/multiAddress.test.ts +2 -1
- package/test/wallet/PersonalWallet/multisignature.test.ts +4 -2
- package/test/wallet/PersonalWallet/nft.test.ts +2 -1
- package/test/web-extension/extension/ui.ts +2 -8
- package/test/load-testing/tx-submit-load.test.ts +0 -341
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import * as Crypto from '@cardano-sdk/crypto';
|
|
2
|
+
import { Cardano, EraSummary, StakePoolProvider, createSlotEpochCalc } from '@cardano-sdk/core';
|
|
3
|
+
import { InMemoryKeyAgent, KeyRole } from '@cardano-sdk/key-management';
|
|
4
|
+
import { MultiSigTx } from './MultiSigTx';
|
|
5
|
+
import { MultiSigWallet } from './MultiSigWallet';
|
|
6
|
+
import { Observable, filter, firstValueFrom, map, take } from 'rxjs';
|
|
7
|
+
import { PersonalWallet } from '@cardano-sdk/wallet';
|
|
8
|
+
import { TrackerSubject } from '@cardano-sdk/util-rxjs';
|
|
9
|
+
import {
|
|
10
|
+
bip32Ed25519Factory,
|
|
11
|
+
createStandaloneKeyAgent,
|
|
12
|
+
getEnv,
|
|
13
|
+
getWallet,
|
|
14
|
+
waitForEpoch,
|
|
15
|
+
walletReady,
|
|
16
|
+
walletVariables
|
|
17
|
+
} from '../../../src';
|
|
18
|
+
import { isNotNil } from '@cardano-sdk/util';
|
|
19
|
+
import { logger } from '@cardano-sdk/util-dev';
|
|
20
|
+
|
|
21
|
+
const env = getEnv(walletVariables);
|
|
22
|
+
|
|
23
|
+
// eslint-disable-next-line max-len
|
|
24
|
+
const aliceMnemonics =
|
|
25
|
+
'decorate survey empower stairs pledge humble social leisure baby wrap grief exact monster rug dash kiss perfect select science light frame play swallow day';
|
|
26
|
+
|
|
27
|
+
// eslint-disable-next-line max-len
|
|
28
|
+
const bobMnemonics =
|
|
29
|
+
'salon zoo engage submit smile frost later decide wing sight chaos renew lizard rely canal coral scene hobby scare step bus leaf tobacco slice';
|
|
30
|
+
|
|
31
|
+
// eslint-disable-next-line max-len
|
|
32
|
+
const charlotteMnemonics =
|
|
33
|
+
'phrase raw learn suspect inmate powder combine apology regular hero gain chronic fruit ritual short screen goddess odor keen creek brand today kit machine';
|
|
34
|
+
|
|
35
|
+
const DERIVATION_PATH = {
|
|
36
|
+
index: 0,
|
|
37
|
+
role: KeyRole.External
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const getPoolIds = async (stakePoolProvider: StakePoolProvider, count: number) => {
|
|
41
|
+
const activePools = await stakePoolProvider.queryStakePools({
|
|
42
|
+
filters: { pledgeMet: true, status: [Cardano.StakePoolStatus.Active] },
|
|
43
|
+
pagination: { limit: count, startAt: 0 }
|
|
44
|
+
});
|
|
45
|
+
expect(activePools.totalResultCount).toBeGreaterThanOrEqual(count);
|
|
46
|
+
const poolIds = activePools.pageResults.map(({ id }) => id);
|
|
47
|
+
expect(poolIds.every((poolId) => poolId !== undefined)).toBeTruthy();
|
|
48
|
+
logger.info('Wallet funds will be staked to pools:', poolIds);
|
|
49
|
+
return poolIds;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const fundMultiSigWallet = async (sendingWallet: PersonalWallet, address: Cardano.PaymentAddress) => {
|
|
53
|
+
logger.info(`Funding multisig wallet with address: ${address}`);
|
|
54
|
+
|
|
55
|
+
const tAdaToSend = 5_000_000n;
|
|
56
|
+
|
|
57
|
+
const txBuilder = sendingWallet.createTxBuilder();
|
|
58
|
+
const txOut = await txBuilder.buildOutput().address(address).coin(tAdaToSend).build();
|
|
59
|
+
const { tx: signedTx } = await txBuilder.addOutput(txOut).build().sign();
|
|
60
|
+
await sendingWallet.submitTx(signedTx);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const getKeyAgent = async (mnemonics: string, faucetWallet: PersonalWallet, bip32Ed25519: Crypto.Bip32Ed25519) => {
|
|
64
|
+
const genesis = await firstValueFrom(faucetWallet.genesisParameters$);
|
|
65
|
+
|
|
66
|
+
const keyAgent = await createStandaloneKeyAgent(mnemonics.split(' '), genesis, bip32Ed25519);
|
|
67
|
+
|
|
68
|
+
const pubKey = await keyAgent.derivePublicKey(DERIVATION_PATH);
|
|
69
|
+
|
|
70
|
+
return { keyAgent, pubKey };
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const generateTxs = async (sendingWallet: PersonalWallet, receivingWallet: PersonalWallet) => {
|
|
74
|
+
logger.info('Sending 100 txs to generate reward fees');
|
|
75
|
+
|
|
76
|
+
const tAdaToSend = 5_000_000n;
|
|
77
|
+
const [{ address: receivingAddress }] = await firstValueFrom(receivingWallet.addresses$);
|
|
78
|
+
|
|
79
|
+
for (let i = 0; i < 100; i++) {
|
|
80
|
+
const txBuilder = sendingWallet.createTxBuilder();
|
|
81
|
+
const txOut = await txBuilder.buildOutput().address(receivingAddress).coin(tAdaToSend).build();
|
|
82
|
+
const { tx: signedTx } = await txBuilder.addOutput(txOut).build().sign();
|
|
83
|
+
await sendingWallet.submitTx(signedTx);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const createMultiSignWallet = async (
|
|
88
|
+
keyAgent: InMemoryKeyAgent,
|
|
89
|
+
faucetWallet: PersonalWallet,
|
|
90
|
+
participants: Array<Crypto.Ed25519PublicKeyHex>
|
|
91
|
+
) => {
|
|
92
|
+
const props = {
|
|
93
|
+
chainHistoryProvider: faucetWallet.chainHistoryProvider,
|
|
94
|
+
expectedSigners: participants,
|
|
95
|
+
inMemoryKeyAgent: keyAgent,
|
|
96
|
+
networkId: env.KEY_MANAGEMENT_PARAMS.chainId.networkId,
|
|
97
|
+
networkInfoProvider: faucetWallet.networkInfoProvider,
|
|
98
|
+
pollingInterval: 50,
|
|
99
|
+
rewardsProvider: faucetWallet.rewardsProvider,
|
|
100
|
+
txSubmitProvider: faucetWallet.txSubmitProvider,
|
|
101
|
+
utxoProvider: faucetWallet.utxoProvider
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
return await MultiSigWallet.createMultiSigWallet(props);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const getTxConfirmationEpoch = async (
|
|
108
|
+
history$: Observable<Cardano.HydratedTx[]>,
|
|
109
|
+
tx: Cardano.Tx<Cardano.TxBody>,
|
|
110
|
+
eraSummaries$: TrackerSubject<EraSummary[]>
|
|
111
|
+
) => {
|
|
112
|
+
const txs = await firstValueFrom(history$.pipe(filter((_) => _.some(({ id }) => id === tx.id))));
|
|
113
|
+
const observedTx = txs.find(({ id }) => id === tx.id);
|
|
114
|
+
const slotEpochCalc = createSlotEpochCalc(await firstValueFrom(eraSummaries$));
|
|
115
|
+
|
|
116
|
+
return slotEpochCalc(observedTx!.blockHeader.slot);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
describe('multi signature wallet', () => {
|
|
120
|
+
let faucetWallet: PersonalWallet;
|
|
121
|
+
let aliceKeyAgent: InMemoryKeyAgent;
|
|
122
|
+
let bobKeyAgent: InMemoryKeyAgent;
|
|
123
|
+
let charlotteKeyAgent: InMemoryKeyAgent;
|
|
124
|
+
let alicePubKey: Crypto.Ed25519PublicKeyHex;
|
|
125
|
+
let bobPubKey: Crypto.Ed25519PublicKeyHex;
|
|
126
|
+
let charlottePubKey: Crypto.Ed25519PublicKeyHex;
|
|
127
|
+
let faucetAddress: Cardano.PaymentAddress;
|
|
128
|
+
let aliceMultiSigWallet: MultiSigWallet;
|
|
129
|
+
let bobMultiSigWallet: MultiSigWallet;
|
|
130
|
+
let charlotteMultiSigWallet: MultiSigWallet;
|
|
131
|
+
let multiSigParticipants: Crypto.Ed25519PublicKeyHex[];
|
|
132
|
+
|
|
133
|
+
const initializeFaucet = async () => {
|
|
134
|
+
({ wallet: faucetWallet } = await getWallet({
|
|
135
|
+
env,
|
|
136
|
+
logger,
|
|
137
|
+
name: 'Faucet Wallet',
|
|
138
|
+
polling: { interval: 50 }
|
|
139
|
+
}));
|
|
140
|
+
|
|
141
|
+
await walletReady(faucetWallet);
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
beforeAll(async () => {
|
|
145
|
+
await initializeFaucet();
|
|
146
|
+
const bip32Ed25519 = await bip32Ed25519Factory.create(env.KEY_MANAGEMENT_PARAMS.bip32Ed25519, null, logger);
|
|
147
|
+
|
|
148
|
+
({ keyAgent: aliceKeyAgent, pubKey: alicePubKey } = await getKeyAgent(aliceMnemonics, faucetWallet, bip32Ed25519));
|
|
149
|
+
({ keyAgent: bobKeyAgent, pubKey: bobPubKey } = await getKeyAgent(bobMnemonics, faucetWallet, bip32Ed25519));
|
|
150
|
+
({ keyAgent: charlotteKeyAgent, pubKey: charlottePubKey } = await getKeyAgent(
|
|
151
|
+
charlotteMnemonics,
|
|
152
|
+
faucetWallet,
|
|
153
|
+
bip32Ed25519
|
|
154
|
+
));
|
|
155
|
+
|
|
156
|
+
faucetAddress = (await firstValueFrom(faucetWallet.addresses$))[0].address;
|
|
157
|
+
|
|
158
|
+
multiSigParticipants = [alicePubKey, bobPubKey, charlottePubKey];
|
|
159
|
+
|
|
160
|
+
aliceMultiSigWallet = await createMultiSignWallet(aliceKeyAgent, faucetWallet, multiSigParticipants);
|
|
161
|
+
bobMultiSigWallet = await createMultiSignWallet(bobKeyAgent, faucetWallet, multiSigParticipants);
|
|
162
|
+
charlotteMultiSigWallet = await createMultiSignWallet(charlotteKeyAgent, faucetWallet, multiSigParticipants);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
afterAll(() => {
|
|
166
|
+
faucetWallet?.shutdown();
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('can receive balance and can spend balance', async () => {
|
|
170
|
+
expect(aliceMultiSigWallet.getPaymentAddress()).toEqual(bobMultiSigWallet.getPaymentAddress());
|
|
171
|
+
expect(aliceMultiSigWallet.getPaymentAddress()).toEqual(charlotteMultiSigWallet.getPaymentAddress());
|
|
172
|
+
|
|
173
|
+
await fundMultiSigWallet(faucetWallet, bobMultiSigWallet.getPaymentAddress());
|
|
174
|
+
|
|
175
|
+
const multiSigWalletBalance = await firstValueFrom(
|
|
176
|
+
bobMultiSigWallet.getBalance().pipe(
|
|
177
|
+
map((value) => value.coins),
|
|
178
|
+
filter((value) => value > 0n),
|
|
179
|
+
take(1)
|
|
180
|
+
)
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
expect(multiSigWalletBalance).toBeGreaterThan(0n);
|
|
184
|
+
|
|
185
|
+
// Alice will initiate the transaction on her wallet.
|
|
186
|
+
let tx = await aliceMultiSigWallet.transferFunds(faucetAddress, { coins: 2_000_000n });
|
|
187
|
+
|
|
188
|
+
// Alice then signs the transaction and relay it to Bob.
|
|
189
|
+
tx = await aliceMultiSigWallet.sign(tx);
|
|
190
|
+
const aliceSerializedTx = tx.toCbor();
|
|
191
|
+
|
|
192
|
+
// .... Bob receives the transaction and signs it.
|
|
193
|
+
let bobTx = MultiSigTx.fromCbor(aliceSerializedTx);
|
|
194
|
+
bobTx = await bobMultiSigWallet.sign(bobTx);
|
|
195
|
+
|
|
196
|
+
// Bob can then check if there are any missing signatures. If there are, he can then
|
|
197
|
+
// check who is missing and send the transaction to them.
|
|
198
|
+
expect(bobTx.isFullySigned()).toBe(false);
|
|
199
|
+
expect(bobTx.getMissingSigners()).toEqual([charlottePubKey]);
|
|
200
|
+
|
|
201
|
+
const bobSerializedTx = bobTx.toCbor();
|
|
202
|
+
|
|
203
|
+
// .... Charlotte receives the transaction and signs it.
|
|
204
|
+
let charlotteTx = MultiSigTx.fromCbor(bobSerializedTx);
|
|
205
|
+
charlotteTx = await charlotteMultiSigWallet.sign(charlotteTx);
|
|
206
|
+
|
|
207
|
+
// Charlotte can then check if there are any missing signatures. And if all signatures
|
|
208
|
+
// are complete she can submit it to the network.
|
|
209
|
+
expect(charlotteTx.getMissingSigners()).toEqual([]);
|
|
210
|
+
expect(charlotteTx.isFullySigned()).toBe(true);
|
|
211
|
+
const txId = await charlotteMultiSigWallet.submit(charlotteTx);
|
|
212
|
+
|
|
213
|
+
// Search chain history to see if the transaction is there.
|
|
214
|
+
const txFoundInHistory = await firstValueFrom(
|
|
215
|
+
faucetWallet.transactions.history$.pipe(
|
|
216
|
+
map((txs) => txs.find((hTx) => hTx.id === txId)),
|
|
217
|
+
filter(isNotNil),
|
|
218
|
+
take(1)
|
|
219
|
+
)
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
expect(txFoundInHistory).toBeTruthy();
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// eslint-disable-next-line max-statements
|
|
226
|
+
it('delegate to a pool and claim rewards', async () => {
|
|
227
|
+
expect(aliceMultiSigWallet.getRewardAccount()).toEqual(bobMultiSigWallet.getRewardAccount());
|
|
228
|
+
expect(aliceMultiSigWallet.getRewardAccount()).toEqual(charlotteMultiSigWallet.getRewardAccount());
|
|
229
|
+
|
|
230
|
+
await fundMultiSigWallet(faucetWallet, bobMultiSigWallet.getPaymentAddress());
|
|
231
|
+
|
|
232
|
+
const multiSigWalletBalance = await firstValueFrom(
|
|
233
|
+
bobMultiSigWallet.getBalance().pipe(
|
|
234
|
+
map((value) => value.coins),
|
|
235
|
+
filter((value) => value > 0n),
|
|
236
|
+
take(1)
|
|
237
|
+
)
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
expect(multiSigWalletBalance).toBeGreaterThan(0n);
|
|
241
|
+
|
|
242
|
+
const [poolId] = await getPoolIds(faucetWallet.stakePoolProvider, 1);
|
|
243
|
+
|
|
244
|
+
// Alice will initiate the delegation transaction on her wallet.
|
|
245
|
+
let tx = await aliceMultiSigWallet.delegate(poolId);
|
|
246
|
+
tx = await aliceMultiSigWallet.sign(tx);
|
|
247
|
+
tx = await bobMultiSigWallet.sign(tx);
|
|
248
|
+
tx = await charlotteMultiSigWallet.sign(tx);
|
|
249
|
+
|
|
250
|
+
const txId = await charlotteMultiSigWallet.submit(tx);
|
|
251
|
+
|
|
252
|
+
// Search chain history to see if the transaction is there.
|
|
253
|
+
const txFoundInHistory = await firstValueFrom(
|
|
254
|
+
charlotteMultiSigWallet.getTransactionHistory().pipe(
|
|
255
|
+
map((txs) => txs.find((hTx) => hTx.id === txId)),
|
|
256
|
+
filter(isNotNil),
|
|
257
|
+
take(1)
|
|
258
|
+
)
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
expect(txFoundInHistory).toBeTruthy();
|
|
262
|
+
|
|
263
|
+
// Delegation is completed. Now we wait for rewards to be available.
|
|
264
|
+
const delegationTxConfirmedAtEpoch = await getTxConfirmationEpoch(
|
|
265
|
+
charlotteMultiSigWallet.getTransactionHistory(),
|
|
266
|
+
tx.getTransaction(),
|
|
267
|
+
faucetWallet.eraSummaries$
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
logger.info(`Delegation tx confirmed at epoch #${delegationTxConfirmedAtEpoch}`);
|
|
271
|
+
|
|
272
|
+
await waitForEpoch(faucetWallet, delegationTxConfirmedAtEpoch + 2);
|
|
273
|
+
await generateTxs(faucetWallet, faucetWallet);
|
|
274
|
+
await waitForEpoch(faucetWallet, delegationTxConfirmedAtEpoch + 4);
|
|
275
|
+
|
|
276
|
+
// Check reward
|
|
277
|
+
const multiSigWalletRewardBalance = await firstValueFrom(
|
|
278
|
+
bobMultiSigWallet.getRewardAccountBalance().pipe(
|
|
279
|
+
filter((value) => value > 0n),
|
|
280
|
+
take(1)
|
|
281
|
+
)
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
expect(multiSigWalletRewardBalance).toBeGreaterThan(0n);
|
|
285
|
+
|
|
286
|
+
logger.info(`Generated rewards: ${multiSigWalletRewardBalance} tLovelace`);
|
|
287
|
+
|
|
288
|
+
tx = await aliceMultiSigWallet.transferFunds(faucetAddress, { coins: 2_000_000n });
|
|
289
|
+
expect(tx.getTransaction().body.withdrawals?.length).toBeGreaterThan(0);
|
|
290
|
+
|
|
291
|
+
tx = await aliceMultiSigWallet.sign(tx);
|
|
292
|
+
tx = await bobMultiSigWallet.sign(tx);
|
|
293
|
+
tx = await charlotteMultiSigWallet.sign(tx);
|
|
294
|
+
|
|
295
|
+
const spendRewardsTx = await charlotteMultiSigWallet.submit(tx);
|
|
296
|
+
|
|
297
|
+
// Search chain history to see if the transaction is there.
|
|
298
|
+
const spendRewardsTxFoundInHistory = await firstValueFrom(
|
|
299
|
+
faucetWallet.transactions.history$.pipe(
|
|
300
|
+
map((txs) => txs.find((hTx) => hTx.id === spendRewardsTx)),
|
|
301
|
+
filter(isNotNil),
|
|
302
|
+
take(1)
|
|
303
|
+
)
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
expect(spendRewardsTxFoundInHistory).toBeTruthy();
|
|
307
|
+
|
|
308
|
+
// Check reward
|
|
309
|
+
const finalRewardBalance = await firstValueFrom(
|
|
310
|
+
bobMultiSigWallet.getRewardAccountBalance().pipe(
|
|
311
|
+
filter((value) => value === 0n),
|
|
312
|
+
take(1)
|
|
313
|
+
)
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
expect(finalRewardBalance).toEqual(0n);
|
|
317
|
+
});
|
|
318
|
+
});
|
|
@@ -28,7 +28,7 @@ import { ReconnectionConfig } from '@cardano-sdk/util-rxjs';
|
|
|
28
28
|
import { createDatabase } from 'typeorm-extension';
|
|
29
29
|
import { getEnv } from '../../src';
|
|
30
30
|
|
|
31
|
-
const
|
|
31
|
+
const dataWithStakeDeregistration = chainSyncData(ChainSyncDataSet.WithStakeKeyDeregistration);
|
|
32
32
|
|
|
33
33
|
const ogmiosConnectionConfig = ((): ConnectionConfig => {
|
|
34
34
|
const { OGMIOS_URL } = getEnv(['OGMIOS_URL']);
|
|
@@ -66,22 +66,22 @@ const createForkProjectionSource = (
|
|
|
66
66
|
// eslint-disable-next-line sort-keys-fix/sort-keys-fix
|
|
67
67
|
findIntersect: (points) => {
|
|
68
68
|
const intersectionPoint = points[0] as Point;
|
|
69
|
-
const
|
|
69
|
+
const someEventsWithStakeRegistration = dataWithStakeDeregistration.allEvents
|
|
70
70
|
.filter(
|
|
71
71
|
(evt): evt is Omit<ChainSyncRollForward, 'requestNext'> =>
|
|
72
72
|
evt.eventType === ChainSyncEventType.RollForward &&
|
|
73
73
|
evt.block.body.some((tx) =>
|
|
74
|
-
tx.body.certificates?.some((cert) => cert.__typename === Cardano.CertificateType.
|
|
74
|
+
tx.body.certificates?.some((cert) => cert.__typename === Cardano.CertificateType.StakeRegistration)
|
|
75
75
|
)
|
|
76
76
|
)
|
|
77
77
|
.slice(0, 2);
|
|
78
78
|
return of({
|
|
79
79
|
chainSync$: new Observable<ChainSyncEvent>((subscriber) => {
|
|
80
|
-
const events = [...
|
|
80
|
+
const events = [...someEventsWithStakeRegistration];
|
|
81
81
|
const next = () => {
|
|
82
82
|
const nextEvt = events.shift();
|
|
83
83
|
if (nextEvt) {
|
|
84
|
-
const blockOffset =
|
|
84
|
+
const blockOffset = someEventsWithStakeRegistration.length - events.length;
|
|
85
85
|
const slot = Cardano.Slot(intersectionPoint.slot + blockOffset * 20);
|
|
86
86
|
const blockNo = Cardano.BlockNo(lastEvt.block.header.blockNo + blockOffset);
|
|
87
87
|
subscriber.next({
|
|
@@ -104,7 +104,7 @@ const createForkProjectionSource = (
|
|
|
104
104
|
}),
|
|
105
105
|
intersection: {
|
|
106
106
|
point: intersectionPoint,
|
|
107
|
-
tip:
|
|
107
|
+
tip: someEventsWithStakeRegistration[someEventsWithStakeRegistration.length - 1].tip
|
|
108
108
|
}
|
|
109
109
|
});
|
|
110
110
|
},
|
|
@@ -75,7 +75,7 @@ const countUniqueOutputAddresses = (queryRunner: QueryRunner) =>
|
|
|
75
75
|
.getRawMany()
|
|
76
76
|
.then((results) => results.length);
|
|
77
77
|
|
|
78
|
-
describe('single-tenant utxo projection', () => {
|
|
78
|
+
describe.skip('single-tenant utxo projection', () => {
|
|
79
79
|
let cardanoNode: ObservableCardanoNode;
|
|
80
80
|
let connection$: Observable<Postgres.TypeormConnection>;
|
|
81
81
|
let buffer: Postgres.TypeormStabilityWindowBuffer;
|
|
@@ -388,6 +388,7 @@ describe('StakePoolProvider', () => {
|
|
|
388
388
|
|
|
389
389
|
filters = { identifier: { values: poolsId.map((id) => ({ id })) } };
|
|
390
390
|
});
|
|
391
|
+
|
|
391
392
|
it('ascending sort by live saturation', async () => {
|
|
392
393
|
if (poolsId.length < 2)
|
|
393
394
|
return console.log(
|
|
@@ -403,6 +404,7 @@ describe('StakePoolProvider', () => {
|
|
|
403
404
|
.map((p) => p.metrics?.saturation)
|
|
404
405
|
);
|
|
405
406
|
});
|
|
407
|
+
|
|
406
408
|
it('descending sort by live saturation', async () => {
|
|
407
409
|
if (poolsId.length < 2)
|
|
408
410
|
return console.log(
|
|
@@ -420,42 +422,45 @@ describe('StakePoolProvider', () => {
|
|
|
420
422
|
});
|
|
421
423
|
});
|
|
422
424
|
|
|
423
|
-
describe('sort by
|
|
425
|
+
describe.each(['lastRos', 'ros'] as const)('sort by %s', (field) => {
|
|
424
426
|
let filters: QueryStakePoolsArgs['filters'] = {};
|
|
425
427
|
const poolsId: Cardano.PoolId[] = [];
|
|
426
428
|
|
|
427
429
|
beforeAll(() => {
|
|
428
|
-
for (const pool of pools)
|
|
429
|
-
if (poolsId.length < 20 && pool.metrics?.
|
|
430
|
+
for (const pool of pools)
|
|
431
|
+
if (poolsId.length < 20 && pool.metrics?.[field] !== undefined && pool.metrics?.[field] > 0)
|
|
430
432
|
poolsId.push(pool.id);
|
|
431
|
-
|
|
432
|
-
}
|
|
433
|
+
|
|
433
434
|
filters = { identifier: { values: poolsId.map((id) => ({ id })) } };
|
|
434
435
|
});
|
|
435
|
-
|
|
436
|
+
|
|
437
|
+
it(`ascending sort by ${field}`, async () => {
|
|
436
438
|
if (poolsId.length < 2)
|
|
437
|
-
return console.log(
|
|
438
|
-
|
|
439
|
+
return console.log(`test 'ascending sort by ${field}' can't run because no suitable pools were found`);
|
|
440
|
+
|
|
441
|
+
const { pageResults, totalResultCount } = await query(filters, { field, order: 'asc' });
|
|
439
442
|
|
|
440
443
|
expect(totalResultCount).toBe(poolsId.length);
|
|
441
|
-
expect(pageResults.map((p) => p.metrics?.
|
|
444
|
+
expect(pageResults.map((p) => p.metrics?.[field])).toStrictEqual(
|
|
442
445
|
pools
|
|
443
446
|
.filter(({ id }) => poolsId.includes(id))
|
|
444
|
-
.sort((a, b) => (a.metrics
|
|
445
|
-
.map((p) => p.metrics
|
|
447
|
+
.sort((a, b) => (a.metrics![field]! < b.metrics![field]! ? -1 : 1))
|
|
448
|
+
.map((p) => p.metrics![field])
|
|
446
449
|
);
|
|
447
450
|
});
|
|
448
|
-
|
|
451
|
+
|
|
452
|
+
it(`descending sort by ${field}`, async () => {
|
|
449
453
|
if (poolsId.length < 2)
|
|
450
|
-
return console.log(
|
|
451
|
-
|
|
454
|
+
return console.log(`test 'descending sort by ${field}' can't run because no suitable pools were found`);
|
|
455
|
+
|
|
456
|
+
const { pageResults, totalResultCount } = await query(filters, { field, order: 'desc' });
|
|
452
457
|
|
|
453
458
|
expect(totalResultCount).toBe(poolsId.length);
|
|
454
|
-
expect(pageResults.map((p) => p.metrics?.
|
|
459
|
+
expect(pageResults.map((p) => p.metrics?.[field])).toStrictEqual(
|
|
455
460
|
pools
|
|
456
461
|
.filter(({ id }) => poolsId.includes(id))
|
|
457
|
-
.sort((a, b) => (a.metrics
|
|
458
|
-
.map((p) => p.metrics
|
|
462
|
+
.sort((a, b) => (a.metrics![field]! > b.metrics![field]! ? -1 : 1))
|
|
463
|
+
.map((p) => p.metrics![field])
|
|
459
464
|
);
|
|
460
465
|
});
|
|
461
466
|
});
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/* eslint-disable max-statements */
|
|
2
|
+
import * as Crypto from '@cardano-sdk/crypto';
|
|
2
3
|
import { BigIntMath } from '@cardano-sdk/util';
|
|
3
4
|
import { Cardano } from '@cardano-sdk/core';
|
|
4
5
|
import { ObservableWallet, PersonalWallet } from '@cardano-sdk/wallet';
|
|
5
6
|
import {
|
|
6
7
|
TX_TIMEOUT_DEFAULT,
|
|
7
8
|
TestWallet,
|
|
9
|
+
bip32Ed25519Factory,
|
|
8
10
|
firstValueFromTimed,
|
|
9
11
|
getEnv,
|
|
10
12
|
getWallet,
|
|
@@ -27,6 +29,7 @@ const getWalletStateSnapshot = async (wallet: ObservableWallet) => {
|
|
|
27
29
|
const utxoTotal = await firstValueFrom(wallet.utxo.total$);
|
|
28
30
|
const utxoAvailable = await firstValueFrom(wallet.utxo.available$);
|
|
29
31
|
const rewardsBalance = await firstValueFrom(wallet.balance.rewardAccounts.rewards$);
|
|
32
|
+
|
|
30
33
|
return {
|
|
31
34
|
balance: { available: balanceAvailable, deposit, total: balanceTotal },
|
|
32
35
|
epoch: epoch.epochNo,
|
|
@@ -54,6 +57,7 @@ const waitForTx = async (wallet: ObservableWallet, hash: Cardano.TransactionId)
|
|
|
54
57
|
describe('PersonalWallet/delegation', () => {
|
|
55
58
|
let wallet1: TestWallet;
|
|
56
59
|
let wallet2: TestWallet;
|
|
60
|
+
let bip32Ed25519: Crypto.Bip32Ed25519;
|
|
57
61
|
|
|
58
62
|
beforeAll(async () => {
|
|
59
63
|
jest.setTimeout(180_000);
|
|
@@ -61,6 +65,7 @@ describe('PersonalWallet/delegation', () => {
|
|
|
61
65
|
wallet2 = await getWallet({ env, idx: 1, logger, name: 'Test Wallet 2', polling: { interval: 500 } });
|
|
62
66
|
|
|
63
67
|
await Promise.all([waitForWalletStateSettle(wallet1.wallet), waitForWalletStateSettle(wallet2.wallet)]);
|
|
68
|
+
bip32Ed25519 = await bip32Ed25519Factory.create(env.KEY_MANAGEMENT_PARAMS.bip32Ed25519, null, logger);
|
|
64
69
|
});
|
|
65
70
|
|
|
66
71
|
afterAll(() => {
|
|
@@ -164,9 +169,7 @@ describe('PersonalWallet/delegation', () => {
|
|
|
164
169
|
expect(tx1ConfirmedState.rewardAccount.delegatee?.currentEpoch?.id).toEqual(poolId);
|
|
165
170
|
}
|
|
166
171
|
|
|
167
|
-
const stakeKeyHash = await (
|
|
168
|
-
await sourceWallet.keyAgent.getBip32Ed25519()
|
|
169
|
-
).getPubKeyHash(tx1ConfirmedState.publicStakeKey.publicStakeKey);
|
|
172
|
+
const stakeKeyHash = await bip32Ed25519.getPubKeyHash(tx1ConfirmedState.publicStakeKey.publicStakeKey);
|
|
170
173
|
expect(stakeKeyHash).toEqual(Cardano.RewardAccount.toHash(tx1ConfirmedState.rewardAccount.address));
|
|
171
174
|
expect(tx1ConfirmedState.publicStakeKey.keyStatus).toBe(Cardano.StakeKeyStatus.Registered);
|
|
172
175
|
|
|
@@ -3,6 +3,7 @@ import { Cardano, metadatum } from '@cardano-sdk/core';
|
|
|
3
3
|
import { KeyAgent, TransactionSigner } from '@cardano-sdk/key-management';
|
|
4
4
|
import { PersonalWallet } from '@cardano-sdk/wallet';
|
|
5
5
|
import {
|
|
6
|
+
bip32Ed25519Factory,
|
|
6
7
|
burnTokens,
|
|
7
8
|
coinsRequiredByHandleMint,
|
|
8
9
|
createHandleMetadata,
|
|
@@ -49,7 +50,7 @@ describe('Ada handle', () => {
|
|
|
49
50
|
keyAgent = await createStandaloneKeyAgent(
|
|
50
51
|
env.KEY_MANAGEMENT_PARAMS.mnemonic.split(' '),
|
|
51
52
|
await firstValueFrom(wallet.genesisParameters$),
|
|
52
|
-
await
|
|
53
|
+
await bip32Ed25519Factory.create(env.KEY_MANAGEMENT_PARAMS.bip32Ed25519, null, logger)
|
|
53
54
|
);
|
|
54
55
|
({ policyScript, policySigner, policyId } = await createHandlePolicy(keyAgent));
|
|
55
56
|
const handleProviderPolicyId = await getHandlePolicyId(
|
|
@@ -3,6 +3,7 @@ import { FinalizeTxProps, PersonalWallet } from '@cardano-sdk/wallet';
|
|
|
3
3
|
import { InitializeTxProps } from '@cardano-sdk/tx-construction';
|
|
4
4
|
import { KeyRole, util } from '@cardano-sdk/key-management';
|
|
5
5
|
import {
|
|
6
|
+
bip32Ed25519Factory,
|
|
6
7
|
burnTokens,
|
|
7
8
|
createStandaloneKeyAgent,
|
|
8
9
|
getEnv,
|
|
@@ -36,7 +37,7 @@ describe('PersonalWallet/mint', () => {
|
|
|
36
37
|
const aliceKeyAgent = await createStandaloneKeyAgent(
|
|
37
38
|
env.KEY_MANAGEMENT_PARAMS.mnemonic.split(' '),
|
|
38
39
|
genesis,
|
|
39
|
-
await
|
|
40
|
+
await bip32Ed25519Factory.create(env.KEY_MANAGEMENT_PARAMS.bip32Ed25519, null, logger)
|
|
40
41
|
);
|
|
41
42
|
|
|
42
43
|
const derivationPath = {
|
|
@@ -3,6 +3,7 @@ import { AddressType, GroupedAddress, util } from '@cardano-sdk/key-management';
|
|
|
3
3
|
import { Cardano } from '@cardano-sdk/core';
|
|
4
4
|
import {
|
|
5
5
|
KeyAgentFactoryProps,
|
|
6
|
+
bip32Ed25519Factory,
|
|
6
7
|
createStandaloneKeyAgent,
|
|
7
8
|
firstValueFromTimed,
|
|
8
9
|
getWallet,
|
|
@@ -42,7 +43,7 @@ describe('PersonalWallet/multiAddress', () => {
|
|
|
42
43
|
const multiAddressKeyAgent = await createStandaloneKeyAgent(
|
|
43
44
|
mnemonics,
|
|
44
45
|
genesis,
|
|
45
|
-
await
|
|
46
|
+
await bip32Ed25519Factory.create(env.KEY_MANAGEMENT_PARAMS.bip32Ed25519, null, logger)
|
|
46
47
|
);
|
|
47
48
|
|
|
48
49
|
let txBuilder = wallet.createTxBuilder();
|
|
@@ -4,6 +4,7 @@ import { FinalizeTxProps, PersonalWallet } from '@cardano-sdk/wallet';
|
|
|
4
4
|
import { InitializeTxProps } from '@cardano-sdk/tx-construction';
|
|
5
5
|
import { KeyRole, util } from '@cardano-sdk/key-management';
|
|
6
6
|
import {
|
|
7
|
+
bip32Ed25519Factory,
|
|
7
8
|
burnTokens,
|
|
8
9
|
createStandaloneKeyAgent,
|
|
9
10
|
getEnv,
|
|
@@ -34,15 +35,16 @@ describe('PersonalWallet/multisignature', () => {
|
|
|
34
35
|
|
|
35
36
|
const genesis = await firstValueFrom(wallet.genesisParameters$);
|
|
36
37
|
|
|
38
|
+
const bip32Ed25519 = await bip32Ed25519Factory.create(env.KEY_MANAGEMENT_PARAMS.bip32Ed25519, null, logger);
|
|
37
39
|
const aliceKeyAgent = await createStandaloneKeyAgent(
|
|
38
40
|
env.KEY_MANAGEMENT_PARAMS.mnemonic.split(' '),
|
|
39
41
|
genesis,
|
|
40
|
-
|
|
42
|
+
bip32Ed25519
|
|
41
43
|
);
|
|
42
44
|
const bobKeyAgent = await createStandaloneKeyAgent(
|
|
43
45
|
env.KEY_MANAGEMENT_PARAMS.mnemonic.split(' '),
|
|
44
46
|
genesis,
|
|
45
|
-
|
|
47
|
+
bip32Ed25519
|
|
46
48
|
);
|
|
47
49
|
|
|
48
50
|
const aliceDerivationPath = {
|
|
@@ -4,6 +4,7 @@ import { Assets, FinalizeTxProps, PersonalWallet } 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 {
|
|
7
|
+
bip32Ed25519Factory,
|
|
7
8
|
burnTokens,
|
|
8
9
|
createStandaloneKeyAgent,
|
|
9
10
|
firstValueFromTimed,
|
|
@@ -60,7 +61,7 @@ describe('PersonalWallet.assets/nft', () => {
|
|
|
60
61
|
const keyAgent = await createStandaloneKeyAgent(
|
|
61
62
|
env.KEY_MANAGEMENT_PARAMS.mnemonic.split(' '),
|
|
62
63
|
genesis,
|
|
63
|
-
await
|
|
64
|
+
await bip32Ed25519Factory.create(env.KEY_MANAGEMENT_PARAMS.bip32Ed25519, null, logger)
|
|
64
65
|
);
|
|
65
66
|
|
|
66
67
|
const derivationPath = {
|
|
@@ -165,10 +165,7 @@ const deactivateWallet = async (): Promise<void> => {
|
|
|
165
165
|
clearWalletValues();
|
|
166
166
|
};
|
|
167
167
|
|
|
168
|
-
/**
|
|
169
|
-
* Wallet does not have any active delegations.
|
|
170
|
-
* Show a `<p class="noDelegation">No delegation found</p>`
|
|
171
|
-
*/
|
|
168
|
+
/** Wallet does not have any active delegations. Show a `<p class="noDelegation">No delegation found</p>` */
|
|
172
169
|
const createEmptyDelegationEl = () => {
|
|
173
170
|
const emptyDistribution = document.createElement('p');
|
|
174
171
|
emptyDistribution.classList.add('noDelegation');
|
|
@@ -176,10 +173,7 @@ const createEmptyDelegationEl = () => {
|
|
|
176
173
|
return emptyDistribution;
|
|
177
174
|
};
|
|
178
175
|
|
|
179
|
-
/**
|
|
180
|
-
* Create a list item for a delegation
|
|
181
|
-
* `<li> <span class="poolId">thePoolId</span> <span class="percent">50</span> </li>`
|
|
182
|
-
*/
|
|
176
|
+
/** Create a list item for a delegation `<li> <span class="poolId">thePoolId</span> <span class="percent">50</span> </li>` */
|
|
183
177
|
const createDelegationLi = (poolId: string, percent: string) => {
|
|
184
178
|
const delegationLi = document.createElement('li');
|
|
185
179
|
const poolIdSpan = document.createElement('span');
|