-
Notifications
You must be signed in to change notification settings - Fork 62
/
Copy pathmultiAddress.test.ts
166 lines (136 loc) · 5.79 KB
/
multiAddress.test.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/* eslint-disable sonarjs/no-duplicate-string */
import { AddressType, GroupedAddress, KeyPurpose, util } from '@cardano-sdk/key-management';
import { BaseWallet } from '@cardano-sdk/wallet';
import { Cardano } from '@cardano-sdk/core';
import {
KeyAgentFactoryProps,
bip32Ed25519Factory,
createStandaloneKeyAgent,
firstValueFromTimed,
getWallet,
normalizeTxBody,
walletReady
} from '../../../src';
import { createLogger } from '@cardano-sdk/util-dev';
import { filter, map, take } from 'rxjs';
import { getEnv, walletVariables } from '../../../src/environment';
import { isNotNil } from '@cardano-sdk/util';
const env = getEnv(walletVariables);
const logger = createLogger();
const PAYMENT_INDICES_TO_GENERATE = 30;
const PAYMENT_ADDRESSES_TO_GENERATE = PAYMENT_INDICES_TO_GENERATE * 2; // External + Internal address for each payment derivation index
const COINS_PER_ADDRESS = 3_000_000n;
describe('PersonalWallet/multiAddress', () => {
let wallet: BaseWallet;
beforeAll(async () => {
wallet = (await getWallet({ env, idx: 0, logger, name: 'Wallet' })).wallet;
});
afterAll(() => {
wallet.shutdown();
});
// eslint-disable-next-line max-statements
it('can discover and spend UTXOs from a multi address wallet', async () => {
await walletReady(wallet);
const genesis = await firstValueFromTimed(wallet.genesisParameters$);
// Create a random set of mnemonics for a brand-new wallet
const mnemonics = util.generateMnemonicWords();
const multiAddressKeyAgent = await createStandaloneKeyAgent(
mnemonics,
genesis,
await bip32Ed25519Factory.create(env.KEY_MANAGEMENT_PARAMS.bip32Ed25519, null, logger),
KeyPurpose.STANDARD
);
let txBuilder = wallet.createTxBuilder();
const addressesToBeDiscovered = new Array<GroupedAddress>();
// Deposit some tADA at some generated addresses from the previously generated mnemonics.
for (let i = 0; i < PAYMENT_INDICES_TO_GENERATE; ++i) {
const [addressExternal, addressInternal] = await Promise.all([
multiAddressKeyAgent.deriveAddress(
{
index: i,
type: AddressType.External
},
0
),
multiAddressKeyAgent.deriveAddress(
{
index: i,
type: AddressType.Internal
},
0
)
]);
addressesToBeDiscovered.push(addressExternal, addressInternal);
txBuilder.addOutput(txBuilder.buildOutput().address(addressExternal.address).coin(3_000_000n).toTxOut());
txBuilder.addOutput(txBuilder.buildOutput().address(addressInternal.address).coin(3_000_000n).toTxOut());
}
const { tx: signedTx } = await txBuilder.build().sign();
await wallet.submitTx(signedTx);
// Search chain history to see if the transaction is there.
const txFoundInHistory = await firstValueFromTimed(
wallet.transactions.history$.pipe(
map((txs) => txs.find((tx) => tx.id === signedTx.id)),
filter(isNotNil),
take(1)
)
);
expect(txFoundInHistory.id).toEqual(signedTx.id);
expect(normalizeTxBody(txFoundInHistory.body)).toEqual(normalizeTxBody(signedTx.body));
// Create a new wallet using the mnemonics previously generated.
const mnemonicString = mnemonics.join(' ');
const customKeyParams: KeyAgentFactoryProps = {
accountIndex: 0,
chainId: env.KEY_MANAGEMENT_PARAMS.chainId,
mnemonic: mnemonicString,
passphrase: 'some_passphrase'
};
const newWallet = await getWallet({
customKeyParams,
env,
idx: 0,
logger,
name: 'New Multi Address Wallet',
polling: { interval: 500 }
});
await walletReady(newWallet.wallet);
const walletAddresses = await firstValueFromTimed(newWallet.wallet.addresses$);
const rewardAddresses = await firstValueFromTimed(newWallet.wallet.delegation.rewardAccounts$);
// Let's check if all addresses has been discovered.
expect(walletAddresses).toEqual(addressesToBeDiscovered);
// All addresses are built using the same stake key. Check that there is a single reward account
const expectedRewardAccount = walletAddresses[0].rewardAccount;
expect(rewardAddresses).toEqual([
expect.objectContaining<Partial<Cardano.RewardAccountInfo>>({ address: expectedRewardAccount })
]);
const totalBalance = await firstValueFromTimed(newWallet.wallet.balance.utxo.total$);
const expectedAmount = PAYMENT_ADDRESSES_TO_GENERATE * Number(COINS_PER_ADDRESS);
expect(Number(totalBalance.coins)).toEqual(expectedAmount);
// Now lets see if the wallet can spend from all these addresses.
txBuilder = newWallet.wallet.createTxBuilder();
const fundingWalletAddresses = await firstValueFromTimed(wallet.addresses$);
const { tx: returnAdaSignedTx } = await txBuilder
.addOutput(
txBuilder
.buildOutput()
.address(fundingWalletAddresses[0].address)
.coin(totalBalance.coins - 1_500_000n) // Let's leave some behind for fees.
.toTxOut()
)
.build()
.sign();
await newWallet.wallet.submitTx(returnAdaSignedTx);
// Search chain history to see if the transaction is there.
const returnAdaTxFoundInHistory = await firstValueFromTimed(
newWallet.wallet.transactions.history$.pipe(
map((txs) => txs.find((tx) => tx.id === returnAdaSignedTx.id)),
filter(isNotNil),
take(1)
)
);
expect(returnAdaTxFoundInHistory.id).toEqual(returnAdaSignedTx.id);
expect(normalizeTxBody(returnAdaTxFoundInHistory.body)).toEqual(normalizeTxBody(returnAdaSignedTx.body));
const endingBalance = await firstValueFromTimed(newWallet.wallet.balance.utxo.total$);
const expectedEndingBalance = 1_500_000n - returnAdaTxFoundInHistory.body.fee;
expect(endingBalance.coins).toEqual(expectedEndingBalance);
});
});