@deserialize/multi-vm-wallet 1.5.11 โ 1.5.21
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/MULTI_CHAIN_SAVINGS_PLAN.md +1028 -0
- package/MULTI_CHAIN_SAVINGS_USAGE.md +428 -0
- package/SAVINGS_TEST_EXAMPLES.md +411 -0
- package/dist/evm/evm.js +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +32 -2
- package/dist/savings/evm-savings.d.ts +52 -0
- package/dist/savings/evm-savings.js +159 -0
- package/dist/savings/index.d.ts +9 -1
- package/dist/savings/index.js +12 -1
- package/dist/savings/multi-chain-savings.d.ts +48 -0
- package/dist/savings/multi-chain-savings.js +203 -0
- package/dist/savings/savings-manager.d.ts +28 -0
- package/dist/savings/savings-manager.js +55 -0
- package/dist/savings/svm-savings.d.ts +40 -0
- package/dist/savings/svm-savings.js +176 -0
- package/dist/svm/index.d.ts +1 -0
- package/dist/svm/index.js +1 -0
- package/dist/test.js +111 -4
- package/package.json +1 -1
- package/utils/evm/evm.ts +1 -1
- package/utils/index.ts +12 -2
- package/utils/savings/evm-savings.ts +354 -0
- package/utils/savings/index.ts +18 -1
- package/utils/savings/multi-chain-savings.ts +458 -0
- package/utils/savings/savings-manager.ts +189 -0
- package/utils/savings/svm-savings.ts +394 -0
- package/utils/svm/index.ts +2 -1
- package/utils/test.ts +167 -8
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
# Multi-Chain Savings Manager - Test Examples
|
|
2
|
+
|
|
3
|
+
**Date**: 2026-02-02
|
|
4
|
+
**Status**: โ
Ready to test
|
|
5
|
+
**Location**: `utils/test.ts`
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## ๐งช Three Test Functions Added
|
|
10
|
+
|
|
11
|
+
### 1. EVM Savings Test (`testSavingsPocket`)
|
|
12
|
+
|
|
13
|
+
Tests the **EVMSavingsManager** for EVM-compatible chains (Ethereum, Polygon, BSC, Base, etc.)
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
const testSavingsPocket = async () => {
|
|
17
|
+
console.log('\n========== EVM Savings Test ==========');
|
|
18
|
+
const mnemonic = EVMVM.generateMnemonicFromPrivateKey(evmPrivateKeyExposed)
|
|
19
|
+
console.log('Mnemonic:', mnemonic);
|
|
20
|
+
|
|
21
|
+
// Using new EVMSavingsManager (multi-chain compatible)
|
|
22
|
+
const savingsManager = new EVMSavingsManager(mnemonic, evmChainConfig, wallet.index)
|
|
23
|
+
|
|
24
|
+
const pocket0 = savingsManager.getPocket(0)
|
|
25
|
+
console.log('\nPocket 0 Info:');
|
|
26
|
+
console.log(' Address:', pocket0.address);
|
|
27
|
+
console.log(' Derivation path:', pocket0.derivationPath);
|
|
28
|
+
console.log(' Index:', pocket0.index);
|
|
29
|
+
|
|
30
|
+
// Get balance (updated API: getPocketBalance(pocketIndex, tokens[]))
|
|
31
|
+
const balance = await savingsManager.getPocketBalance(0, [])
|
|
32
|
+
console.log('\nPocket 0 Balance:');
|
|
33
|
+
console.log(' Native balance:', balance[0].balance.formatted, 'ETH');
|
|
34
|
+
|
|
35
|
+
// Get multiple pockets
|
|
36
|
+
const pocket1 = savingsManager.getPocket(1)
|
|
37
|
+
const pocket2 = savingsManager.getPocket(2)
|
|
38
|
+
console.log('\nAdditional Pockets:');
|
|
39
|
+
console.log(' Pocket 1:', pocket1.address);
|
|
40
|
+
console.log(' Pocket 2:', pocket2.address);
|
|
41
|
+
|
|
42
|
+
// Cleanup
|
|
43
|
+
savingsManager.dispose();
|
|
44
|
+
console.log('\nโ
EVM Savings Test Complete\n');
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**What it tests:**
|
|
49
|
+
- โ
EVM pocket derivation (coin type 60)
|
|
50
|
+
- โ
Address generation
|
|
51
|
+
- โ
Balance queries
|
|
52
|
+
- โ
Multiple pockets (0, 1, 2)
|
|
53
|
+
- โ
Memory cleanup with `dispose()`
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
### 2. SVM (Solana) Savings Test (`testSavingsPocketSVM`)
|
|
58
|
+
|
|
59
|
+
Tests the **SVMSavingsManager** for Solana blockchain
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
const testSavingsPocketSVM = async () => {
|
|
63
|
+
console.log('\n========== SVM (Solana) Savings Test ==========');
|
|
64
|
+
const mnemonic = EVMVM.generateMnemonicFromPrivateKey(evmPrivateKeyExposed)
|
|
65
|
+
console.log('Mnemonic:', mnemonic);
|
|
66
|
+
|
|
67
|
+
// Using SVMSavingsManager for Solana
|
|
68
|
+
const savingsManager = new SVMSavingsManager(
|
|
69
|
+
mnemonic,
|
|
70
|
+
chainConfig.rpcUrl,
|
|
71
|
+
0 // wallet index
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
const pocket0 = savingsManager.getPocket(0)
|
|
75
|
+
console.log('\nPocket 0 Info:');
|
|
76
|
+
console.log(' Address:', pocket0.address.toBase58());
|
|
77
|
+
console.log(' Derivation path:', pocket0.derivationPath);
|
|
78
|
+
console.log(' Index:', pocket0.index);
|
|
79
|
+
|
|
80
|
+
// Get balance
|
|
81
|
+
const balance = await savingsManager.getPocketBalance(0, [])
|
|
82
|
+
console.log('\nPocket 0 Balance:');
|
|
83
|
+
console.log(' Native balance:', balance[0].balance.formatted, 'SOL');
|
|
84
|
+
|
|
85
|
+
// Get multiple pockets
|
|
86
|
+
const pocket1 = savingsManager.getPocket(1)
|
|
87
|
+
const pocket2 = savingsManager.getPocket(2)
|
|
88
|
+
console.log('\nAdditional Pockets:');
|
|
89
|
+
console.log(' Pocket 1:', pocket1.address.toBase58());
|
|
90
|
+
console.log(' Pocket 2:', pocket2.address.toBase58());
|
|
91
|
+
|
|
92
|
+
// Note: Solana addresses are DIFFERENT from EVM addresses!
|
|
93
|
+
console.log('\n๐ก Note: Solana uses coin type 501, so addresses differ from EVM');
|
|
94
|
+
|
|
95
|
+
// Cleanup
|
|
96
|
+
savingsManager.dispose();
|
|
97
|
+
console.log('\nโ
SVM Savings Test Complete\n');
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**What it tests:**
|
|
102
|
+
- โ
Solana pocket derivation (coin type 501)
|
|
103
|
+
- โ
PublicKey address generation
|
|
104
|
+
- โ
SOL balance queries
|
|
105
|
+
- โ
Multiple pockets with different addresses than EVM
|
|
106
|
+
- โ
Memory cleanup
|
|
107
|
+
|
|
108
|
+
**Key Difference**: Solana addresses are **completely different** from EVM addresses due to different coin type!
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
### 3. Multi-Chain Savings Test (`testSavingsPocketMultiChain`)
|
|
113
|
+
|
|
114
|
+
Tests the **MultiChainSavingsManager** across multiple chains simultaneously
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
const testSavingsPocketMultiChain = async () => {
|
|
118
|
+
console.log('\n========== Multi-Chain Savings Test ==========');
|
|
119
|
+
const mnemonic = EVMVM.generateMnemonicFromPrivateKey(evmPrivateKeyExposed)
|
|
120
|
+
console.log('Mnemonic:', mnemonic);
|
|
121
|
+
|
|
122
|
+
// Create multi-chain manager with EVM and Solana
|
|
123
|
+
const multiChainManager = new MultiChainSavingsManager(
|
|
124
|
+
mnemonic,
|
|
125
|
+
[
|
|
126
|
+
{
|
|
127
|
+
id: 'base',
|
|
128
|
+
type: 'EVM',
|
|
129
|
+
config: evmChainConfig
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
id: 'ethereum',
|
|
133
|
+
type: 'EVM',
|
|
134
|
+
config: {
|
|
135
|
+
chainId: 1,
|
|
136
|
+
name: 'Ethereum',
|
|
137
|
+
rpcUrl: 'https://eth-mainnet.g.alchemy.com/v2/demo',
|
|
138
|
+
explorerUrl: 'https://etherscan.io',
|
|
139
|
+
nativeToken: { name: 'Ethereum', symbol: 'ETH', decimals: 18 },
|
|
140
|
+
confirmationNo: 1,
|
|
141
|
+
vmType: 'EVM'
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
id: 'solana',
|
|
146
|
+
type: 'SVM',
|
|
147
|
+
config: {
|
|
148
|
+
rpcUrl: chainConfig.rpcUrl
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
],
|
|
152
|
+
0 // wallet index
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
console.log('\nAvailable chains:', multiChainManager.getChains());
|
|
156
|
+
console.log('EVM chains:', multiChainManager.getEVMChains());
|
|
157
|
+
console.log('SVM chains:', multiChainManager.getSVMChains());
|
|
158
|
+
|
|
159
|
+
// Get pocket 0 address on different chains
|
|
160
|
+
console.log('\nPocket 0 Addresses Across Chains:');
|
|
161
|
+
const baseAddress = multiChainManager.getPocketAddress('base', 0);
|
|
162
|
+
const ethAddress = multiChainManager.getPocketAddress('ethereum', 0);
|
|
163
|
+
const solAddress = multiChainManager.getPocketAddress('solana', 0);
|
|
164
|
+
|
|
165
|
+
console.log(' Base:', baseAddress);
|
|
166
|
+
console.log(' Ethereum:', ethAddress);
|
|
167
|
+
console.log(' Solana:', solAddress);
|
|
168
|
+
|
|
169
|
+
// Verify EVM chains share addresses
|
|
170
|
+
console.log('\n๐ EVM Address Check:');
|
|
171
|
+
console.log(' Base === Ethereum?', baseAddress === ethAddress, 'โ
');
|
|
172
|
+
console.log(' Base === Solana?', baseAddress === solAddress, 'โ (different chain type)');
|
|
173
|
+
|
|
174
|
+
// Get balances across chains
|
|
175
|
+
console.log('\n๐ Getting balances across all chains...');
|
|
176
|
+
try {
|
|
177
|
+
const balances = await multiChainManager.getPocketBalanceAcrossChains(
|
|
178
|
+
0, // pocket index
|
|
179
|
+
new Map([
|
|
180
|
+
['base', []], // No tokens, just native
|
|
181
|
+
['ethereum', []],
|
|
182
|
+
['solana', []]
|
|
183
|
+
])
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
console.log('\nBalances for Pocket 0:');
|
|
187
|
+
balances.forEach(chainBalance => {
|
|
188
|
+
console.log(`\n ${chainBalance.chainId.toUpperCase()} (${chainBalance.chainType}):`);
|
|
189
|
+
console.log(` Address: ${chainBalance.address}`);
|
|
190
|
+
chainBalance.balances.forEach(bal => {
|
|
191
|
+
const tokenName = bal.token === 'native' ? 'Native' : bal.token;
|
|
192
|
+
console.log(` ${tokenName}: ${bal.balance.formatted}`);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
} catch (error) {
|
|
196
|
+
console.log(' โ ๏ธ Balance fetch error (expected for demo RPCs):', (error as Error).message);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Get specific chain managers for advanced operations
|
|
200
|
+
console.log('\n๐ง Advanced: Direct chain manager access');
|
|
201
|
+
const baseManager = multiChainManager.getEVMManager('base');
|
|
202
|
+
const solanaManager = multiChainManager.getSVMManager('solana');
|
|
203
|
+
console.log(' Base manager type:', baseManager.constructor.name);
|
|
204
|
+
console.log(' Solana manager type:', solanaManager.constructor.name);
|
|
205
|
+
|
|
206
|
+
// Cleanup
|
|
207
|
+
multiChainManager.dispose();
|
|
208
|
+
console.log('\nโ
Multi-Chain Savings Test Complete\n');
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**What it tests:**
|
|
213
|
+
- โ
Multi-chain initialization (Base + Ethereum + Solana)
|
|
214
|
+
- โ
Chain listing and filtering (EVM vs SVM)
|
|
215
|
+
- โ
Address sharing across EVM chains
|
|
216
|
+
- โ
Address differences for Solana
|
|
217
|
+
- โ
Cross-chain balance queries
|
|
218
|
+
- โ
Direct chain manager access
|
|
219
|
+
- โ
Memory cleanup for all chains
|
|
220
|
+
|
|
221
|
+
**Key Features Demonstrated**:
|
|
222
|
+
1. **EVM Address Sharing**: Base and Ethereum have the SAME pocket addresses
|
|
223
|
+
2. **SVM Different Addresses**: Solana has DIFFERENT pocket addresses
|
|
224
|
+
3. **Unified API**: Same interface for querying across all chains
|
|
225
|
+
4. **Type Detection**: Automatic handling of EVM vs SVM chains
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## ๐ How to Run Tests
|
|
230
|
+
|
|
231
|
+
### Enable Tests in test.ts
|
|
232
|
+
|
|
233
|
+
Uncomment the test you want to run at the bottom of `utils/test.ts`:
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
// Uncomment to run tests
|
|
237
|
+
testSavingsPocket() // Test EVM only
|
|
238
|
+
// testSavingsPocketSVM() // Test Solana only
|
|
239
|
+
// testSavingsPocketMultiChain() // Test all chains
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Run with Bun
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
cd packages
|
|
246
|
+
bun run utils/test.ts
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## ๐ Expected Output Examples
|
|
252
|
+
|
|
253
|
+
### EVM Test Output
|
|
254
|
+
```
|
|
255
|
+
========== EVM Savings Test ==========
|
|
256
|
+
Mnemonic: abandon abandon abandon...
|
|
257
|
+
|
|
258
|
+
Pocket 0 Info:
|
|
259
|
+
Address: 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb
|
|
260
|
+
Derivation path: m/44'/60'/1'/0/0
|
|
261
|
+
Index: 1
|
|
262
|
+
|
|
263
|
+
Pocket 0 Balance:
|
|
264
|
+
Native balance: 0.1234 ETH
|
|
265
|
+
|
|
266
|
+
Additional Pockets:
|
|
267
|
+
Pocket 1: 0x8F73bE66CA8c79382f72139be03746343Bf5Faa0
|
|
268
|
+
Pocket 2: 0x9D7f9B2b04D0b5b4E8f8c9C5aE4F5e6d7C8B9A0
|
|
269
|
+
|
|
270
|
+
โ
EVM Savings Test Complete
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### SVM Test Output
|
|
274
|
+
```
|
|
275
|
+
========== SVM (Solana) Savings Test ==========
|
|
276
|
+
Mnemonic: abandon abandon abandon...
|
|
277
|
+
|
|
278
|
+
Pocket 0 Info:
|
|
279
|
+
Address: 6j7VkRxPzJdvnfMPvREXAEJfKGV5YdvPzRyTQv3qXxH2
|
|
280
|
+
Derivation path: m/44'/501'/1'/0/0
|
|
281
|
+
Index: 1
|
|
282
|
+
|
|
283
|
+
Pocket 0 Balance:
|
|
284
|
+
Native balance: 0.5678 SOL
|
|
285
|
+
|
|
286
|
+
Additional Pockets:
|
|
287
|
+
Pocket 1: 8k9WlSyQaJewgNGqvMQXBnZPzMdwUhP1aRyTRv4rYyI3
|
|
288
|
+
Pocket 2: 2n3XmToRzKcdvPoYvNRXCpAJfLgV5ZfQaStTSw5sZzJ4
|
|
289
|
+
|
|
290
|
+
๐ก Note: Solana uses coin type 501, so addresses differ from EVM
|
|
291
|
+
|
|
292
|
+
โ
SVM Savings Test Complete
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Multi-Chain Test Output
|
|
296
|
+
```
|
|
297
|
+
========== Multi-Chain Savings Test ==========
|
|
298
|
+
Mnemonic: abandon abandon abandon...
|
|
299
|
+
|
|
300
|
+
Available chains: [ 'base', 'ethereum', 'solana' ]
|
|
301
|
+
EVM chains: [ 'base', 'ethereum' ]
|
|
302
|
+
SVM chains: [ 'solana' ]
|
|
303
|
+
|
|
304
|
+
Pocket 0 Addresses Across Chains:
|
|
305
|
+
Base: 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb
|
|
306
|
+
Ethereum: 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb
|
|
307
|
+
Solana: 6j7VkRxPzJdvnfMPvREXAEJfKGV5YdvPzRyTQv3qXxH2
|
|
308
|
+
|
|
309
|
+
๐ EVM Address Check:
|
|
310
|
+
Base === Ethereum? true โ
|
|
311
|
+
Base === Solana? false โ (different chain type)
|
|
312
|
+
|
|
313
|
+
๐ Getting balances across all chains...
|
|
314
|
+
|
|
315
|
+
Balances for Pocket 0:
|
|
316
|
+
|
|
317
|
+
BASE (EVM):
|
|
318
|
+
Address: 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb
|
|
319
|
+
Native: 0.1234
|
|
320
|
+
|
|
321
|
+
ETHEREUM (EVM):
|
|
322
|
+
Address: 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb
|
|
323
|
+
Native: 0.5678
|
|
324
|
+
|
|
325
|
+
SOLANA (SVM):
|
|
326
|
+
Address: 6j7VkRxPzJdvnfMPvREXAEJfKGV5YdvPzRyTQv3qXxH2
|
|
327
|
+
Native: 1.2345
|
|
328
|
+
|
|
329
|
+
๐ง Advanced: Direct chain manager access
|
|
330
|
+
Base manager type: EVMSavingsManager
|
|
331
|
+
Solana manager type: SVMSavingsManager
|
|
332
|
+
|
|
333
|
+
โ
Multi-Chain Savings Test Complete
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## ๐งช Test Coverage
|
|
339
|
+
|
|
340
|
+
### What's Tested
|
|
341
|
+
|
|
342
|
+
| Feature | EVM | SVM | Multi-Chain |
|
|
343
|
+
|---------|-----|-----|-------------|
|
|
344
|
+
| Pocket derivation | โ
| โ
| โ
|
|
|
345
|
+
| Address generation | โ
| โ
| โ
|
|
|
346
|
+
| Balance queries | โ
| โ
| โ
|
|
|
347
|
+
| Multiple pockets | โ
| โ
| โ
|
|
|
348
|
+
| Memory cleanup | โ
| โ
| โ
|
|
|
349
|
+
| Chain listing | - | - | โ
|
|
|
350
|
+
| Address sharing (EVM) | - | - | โ
|
|
|
351
|
+
| Cross-chain queries | - | - | โ
|
|
|
352
|
+
| Manager access | - | - | โ
|
|
|
353
|
+
|
|
354
|
+
### What's NOT Tested (Yet)
|
|
355
|
+
|
|
356
|
+
- [ ] Token transfers (`transferToPocket`, `transferTokenToPocket`)
|
|
357
|
+
- [ ] Withdrawals (`sendToMainWallet`)
|
|
358
|
+
- [ ] Token balance queries (only native token tested)
|
|
359
|
+
- [ ] Pocket verification (`verifyPocketAddress`)
|
|
360
|
+
- [ ] Multiple wallet indices
|
|
361
|
+
- [ ] Error handling edge cases
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
## ๐ก Key Takeaways
|
|
366
|
+
|
|
367
|
+
### 1. EVM Chains Share Addresses โ
|
|
368
|
+
```typescript
|
|
369
|
+
// Same mnemonic + same pocket index = same address on all EVM chains
|
|
370
|
+
const baseAddr = manager.getPocketAddress('base', 0);
|
|
371
|
+
const ethAddr = manager.getPocketAddress('ethereum', 0);
|
|
372
|
+
// baseAddr === ethAddr โ
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### 2. Solana Has Different Addresses โ
|
|
376
|
+
```typescript
|
|
377
|
+
// Different coin type = different address
|
|
378
|
+
const ethAddr = manager.getPocketAddress('ethereum', 0); // 0x...
|
|
379
|
+
const solAddr = manager.getPocketAddress('solana', 0); // Base58...
|
|
380
|
+
// ethAddr !== solAddr โ
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### 3. Unified API Across Chains โ
|
|
384
|
+
```typescript
|
|
385
|
+
// Same API works for all chain types
|
|
386
|
+
const evmBalance = await manager.getPocketBalance('ethereum', 0, []);
|
|
387
|
+
const svmBalance = await manager.getPocketBalance('solana', 0, []);
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### 4. Type Safety โ
|
|
391
|
+
```typescript
|
|
392
|
+
// TypeScript ensures correct types
|
|
393
|
+
const evmManager: EVMSavingsManager = manager.getEVMManager('ethereum');
|
|
394
|
+
const svmManager: SVMSavingsManager = manager.getSVMManager('solana');
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
## ๐ฏ Next Steps
|
|
400
|
+
|
|
401
|
+
1. **Uncomment and run tests** to verify functionality
|
|
402
|
+
2. **Add token addresses** to test ERC-20/SPL token balances
|
|
403
|
+
3. **Test transfers** by uncommenting transfer code
|
|
404
|
+
4. **Add error handling tests**
|
|
405
|
+
5. **Add performance benchmarks**
|
|
406
|
+
|
|
407
|
+
---
|
|
408
|
+
|
|
409
|
+
**All three test functions are ready to use!** ๐
|
|
410
|
+
|
|
411
|
+
Simply uncomment the one you want to test in `utils/test.ts` and run with `bun run utils/test.ts`.
|
package/dist/evm/evm.js
CHANGED
|
@@ -72,7 +72,7 @@ class EVMVM extends vm_1.VM {
|
|
|
72
72
|
return await (0, utils_1.getTokenBalance)(tokenAddress, address, connection);
|
|
73
73
|
}
|
|
74
74
|
static convertFromEntropyToPrivateKey = (entropy) => {
|
|
75
|
-
return (0, viem_1.toHex)(entropy);
|
|
75
|
+
return (0, viem_1.toHex)(entropy).substring(0, 64);
|
|
76
76
|
};
|
|
77
77
|
async discoverWallets(connection, options) {
|
|
78
78
|
const startTime = Date.now();
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export * from "./walletBip32";
|
|
2
2
|
export * from "./types";
|
|
3
3
|
export * from "./vm";
|
|
4
|
-
export
|
|
5
|
-
export
|
|
4
|
+
export { EVMVM, EVMChainWallet, EVMSmartWallet } from "./evm";
|
|
5
|
+
export { SVMVM, SVMChainWallet, } from "./svm";
|
|
6
|
+
export * as evm from "./evm";
|
|
7
|
+
export * as svm from "./svm";
|
|
6
8
|
export * from "./constant";
|
|
7
9
|
export * from "bs58";
|
|
8
10
|
export * from "@solana/web3.js";
|
package/dist/index.js
CHANGED
|
@@ -10,15 +10,45 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (functi
|
|
|
10
10
|
if (k2 === undefined) k2 = k;
|
|
11
11
|
o[k2] = m[k];
|
|
12
12
|
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
13
18
|
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
19
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
20
|
};
|
|
21
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
22
|
+
var ownKeys = function(o) {
|
|
23
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
24
|
+
var ar = [];
|
|
25
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
26
|
+
return ar;
|
|
27
|
+
};
|
|
28
|
+
return ownKeys(o);
|
|
29
|
+
};
|
|
30
|
+
return function (mod) {
|
|
31
|
+
if (mod && mod.__esModule) return mod;
|
|
32
|
+
var result = {};
|
|
33
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
34
|
+
__setModuleDefault(result, mod);
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
})();
|
|
16
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.svm = exports.evm = exports.SVMChainWallet = exports.SVMVM = exports.EVMSmartWallet = exports.EVMChainWallet = exports.EVMVM = void 0;
|
|
17
40
|
__exportStar(require("./walletBip32"), exports);
|
|
18
41
|
__exportStar(require("./types"), exports);
|
|
19
42
|
__exportStar(require("./vm"), exports);
|
|
20
|
-
|
|
21
|
-
|
|
43
|
+
var evm_1 = require("./evm");
|
|
44
|
+
Object.defineProperty(exports, "EVMVM", { enumerable: true, get: function () { return evm_1.EVMVM; } });
|
|
45
|
+
Object.defineProperty(exports, "EVMChainWallet", { enumerable: true, get: function () { return evm_1.EVMChainWallet; } });
|
|
46
|
+
Object.defineProperty(exports, "EVMSmartWallet", { enumerable: true, get: function () { return evm_1.EVMSmartWallet; } });
|
|
47
|
+
var svm_1 = require("./svm");
|
|
48
|
+
Object.defineProperty(exports, "SVMVM", { enumerable: true, get: function () { return svm_1.SVMVM; } });
|
|
49
|
+
Object.defineProperty(exports, "SVMChainWallet", { enumerable: true, get: function () { return svm_1.SVMChainWallet; } });
|
|
50
|
+
exports.evm = __importStar(require("./evm"));
|
|
51
|
+
exports.svm = __importStar(require("./svm"));
|
|
22
52
|
__exportStar(require("./constant"), exports);
|
|
23
53
|
__exportStar(require("bs58"), exports);
|
|
24
54
|
__exportStar(require("@solana/web3.js"), exports);
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { SavingsManager, Pocket } from "./savings-manager";
|
|
2
|
+
import { Hex, PublicClient, WalletClient } from "viem";
|
|
3
|
+
import { ChainWalletConfig, Balance, TransactionResult } from "../types";
|
|
4
|
+
export declare class EVMSavingsManager extends SavingsManager<Hex, PublicClient, WalletClient> {
|
|
5
|
+
coinType: number;
|
|
6
|
+
derivationPathBase: string;
|
|
7
|
+
private chain;
|
|
8
|
+
private _client?;
|
|
9
|
+
private masterAddress?;
|
|
10
|
+
constructor(mnemonic: string, chain: ChainWalletConfig, walletIndex?: number, masterAddress?: Hex);
|
|
11
|
+
get client(): PublicClient;
|
|
12
|
+
createClient(rpcUrl: string): PublicClient;
|
|
13
|
+
clearClient(): void;
|
|
14
|
+
derivePocket(accountIndex: number): Pocket<Hex>;
|
|
15
|
+
getMainWallet(): {
|
|
16
|
+
privateKey: string;
|
|
17
|
+
address: Hex;
|
|
18
|
+
derivationPath: string;
|
|
19
|
+
};
|
|
20
|
+
getMainWalletAddress(): Hex;
|
|
21
|
+
getPocketBalance(pocketIndex: number, tokens: string[]): Promise<{
|
|
22
|
+
address: Hex | 'native';
|
|
23
|
+
balance: Balance;
|
|
24
|
+
}[]>;
|
|
25
|
+
getTotalTokenBalanceOfAllPockets(tokens: string[], pockets: number[]): Promise<Array<{
|
|
26
|
+
address: Hex | 'native';
|
|
27
|
+
balance: Balance;
|
|
28
|
+
}[]>>;
|
|
29
|
+
transferToPocket(mainWallet: WalletClient, pocketIndex: number, amount: string): Promise<TransactionResult>;
|
|
30
|
+
transferTokenToPocket(mainWallet: WalletClient, tokenAddress: string, pocketIndex: number, amount: bigint): Promise<TransactionResult>;
|
|
31
|
+
accountFromPocketId(pocketIndex: number): {
|
|
32
|
+
address: import("viem").Address;
|
|
33
|
+
nonceManager?: import("viem").NonceManager | undefined;
|
|
34
|
+
sign: (parameters: {
|
|
35
|
+
hash: import("viem").Hash;
|
|
36
|
+
}) => Promise<Hex>;
|
|
37
|
+
signAuthorization: (parameters: import("viem").AuthorizationRequest) => Promise<import("viem/accounts").SignAuthorizationReturnType>;
|
|
38
|
+
signMessage: ({ message }: {
|
|
39
|
+
message: import("viem").SignableMessage;
|
|
40
|
+
}) => Promise<Hex>;
|
|
41
|
+
signTransaction: <serializer extends import("viem").SerializeTransactionFn<import("viem").TransactionSerializable> = import("viem").SerializeTransactionFn<import("viem").TransactionSerializable>, transaction extends Parameters<serializer>[0] = Parameters<serializer>[0]>(transaction: transaction, options?: {
|
|
42
|
+
serializer?: serializer | undefined;
|
|
43
|
+
} | undefined) => Promise<Hex>;
|
|
44
|
+
signTypedData: <const typedData extends import("viem").TypedData | Record<string, unknown>, primaryType extends keyof typedData | "EIP712Domain" = keyof typedData>(parameters: import("viem").TypedDataDefinition<typedData, primaryType>) => Promise<Hex>;
|
|
45
|
+
publicKey: Hex;
|
|
46
|
+
source: "privateKey";
|
|
47
|
+
type: "local";
|
|
48
|
+
};
|
|
49
|
+
verifyPocketAddress(accountIndex: number, storedAddress: string): boolean;
|
|
50
|
+
sendToMainWallet(pocketIndex: number, amount: bigint, token: Hex | "native"): Promise<TransactionResult>;
|
|
51
|
+
dispose(): void;
|
|
52
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EVMSavingsManager = void 0;
|
|
4
|
+
const savings_manager_1 = require("./savings-manager");
|
|
5
|
+
const viem_1 = require("viem");
|
|
6
|
+
const walletBip32_1 = require("../walletBip32");
|
|
7
|
+
const ethers_1 = require("ethers");
|
|
8
|
+
const evm_1 = require("../evm");
|
|
9
|
+
const validation_1 = require("./validation");
|
|
10
|
+
const accounts_1 = require("viem/accounts");
|
|
11
|
+
class EVMSavingsManager extends savings_manager_1.SavingsManager {
|
|
12
|
+
coinType = 60;
|
|
13
|
+
derivationPathBase = "m/44'/60'/";
|
|
14
|
+
chain;
|
|
15
|
+
_client;
|
|
16
|
+
masterAddress;
|
|
17
|
+
constructor(mnemonic, chain, walletIndex = 0, masterAddress) {
|
|
18
|
+
super(mnemonic, walletIndex);
|
|
19
|
+
validation_1.SavingsValidation.validateChainId(chain.chainId);
|
|
20
|
+
if (masterAddress) {
|
|
21
|
+
validation_1.SavingsValidation.validateAddress(masterAddress, 'Master address');
|
|
22
|
+
}
|
|
23
|
+
this.chain = chain;
|
|
24
|
+
this.masterAddress = masterAddress;
|
|
25
|
+
}
|
|
26
|
+
get client() {
|
|
27
|
+
if (!this._client) {
|
|
28
|
+
this._client = this.createClient(this.chain.rpcUrl);
|
|
29
|
+
}
|
|
30
|
+
return this._client;
|
|
31
|
+
}
|
|
32
|
+
createClient(rpcUrl) {
|
|
33
|
+
return (0, viem_1.createPublicClient)({
|
|
34
|
+
chain: (0, evm_1.fromChainToViemChain)(this.chain),
|
|
35
|
+
transport: (0, viem_1.http)(rpcUrl)
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
clearClient() {
|
|
39
|
+
this._client = undefined;
|
|
40
|
+
}
|
|
41
|
+
derivePocket(accountIndex) {
|
|
42
|
+
this.checkNotDisposed();
|
|
43
|
+
validation_1.SavingsValidation.validateAccountIndex(accountIndex);
|
|
44
|
+
const pocketIndex = accountIndex + 1;
|
|
45
|
+
const derivationPath = `${this.derivationPathBase}${pocketIndex}'/0/${this.walletIndex}`;
|
|
46
|
+
const seed = (0, walletBip32_1.mnemonicToSeed)(this.mnemonic);
|
|
47
|
+
const { privateKey } = (0, walletBip32_1.EVMDeriveChildPrivateKey)(seed, this.walletIndex, derivationPath);
|
|
48
|
+
const wallet = new ethers_1.ethers.Wallet(privateKey);
|
|
49
|
+
const pocket = {
|
|
50
|
+
privateKey,
|
|
51
|
+
address: wallet.address,
|
|
52
|
+
derivationPath,
|
|
53
|
+
index: pocketIndex
|
|
54
|
+
};
|
|
55
|
+
this.pockets.set(accountIndex, pocket);
|
|
56
|
+
return pocket;
|
|
57
|
+
}
|
|
58
|
+
getMainWallet() {
|
|
59
|
+
this.checkNotDisposed();
|
|
60
|
+
const derivationPath = `${this.derivationPathBase}0'/0/${this.walletIndex}`;
|
|
61
|
+
const seed = (0, walletBip32_1.mnemonicToSeed)(this.mnemonic);
|
|
62
|
+
const { privateKey } = (0, walletBip32_1.EVMDeriveChildPrivateKey)(seed, this.walletIndex, derivationPath);
|
|
63
|
+
const wallet = new ethers_1.ethers.Wallet(privateKey);
|
|
64
|
+
return {
|
|
65
|
+
privateKey,
|
|
66
|
+
address: wallet.address,
|
|
67
|
+
derivationPath
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
getMainWalletAddress() {
|
|
71
|
+
if (this.masterAddress)
|
|
72
|
+
return this.masterAddress;
|
|
73
|
+
return this.getMainWallet().address;
|
|
74
|
+
}
|
|
75
|
+
async getPocketBalance(pocketIndex, tokens) {
|
|
76
|
+
validation_1.SavingsValidation.validateAccountIndex(pocketIndex);
|
|
77
|
+
if (!Array.isArray(tokens)) {
|
|
78
|
+
throw new Error('Tokens must be an array');
|
|
79
|
+
}
|
|
80
|
+
const pocket = this.getPocket(pocketIndex);
|
|
81
|
+
const balances = [];
|
|
82
|
+
const nativeBalance = await (0, evm_1.getNativeBalance)(pocket.address, this.client);
|
|
83
|
+
balances.push({ address: 'native', balance: nativeBalance });
|
|
84
|
+
await Promise.all(tokens.map(async (token) => {
|
|
85
|
+
validation_1.SavingsValidation.validateAddress(token, 'Token address');
|
|
86
|
+
const tokenBalance = await (0, evm_1.getTokenBalance)(token, pocket.address, this.client);
|
|
87
|
+
balances.push({ address: token, balance: tokenBalance });
|
|
88
|
+
}));
|
|
89
|
+
return balances;
|
|
90
|
+
}
|
|
91
|
+
async getTotalTokenBalanceOfAllPockets(tokens, pockets) {
|
|
92
|
+
if (!Array.isArray(tokens) || tokens.length === 0) {
|
|
93
|
+
throw new Error('Tokens array must be non-empty');
|
|
94
|
+
}
|
|
95
|
+
if (!Array.isArray(pockets) || pockets.length === 0) {
|
|
96
|
+
throw new Error('Pockets array must be non-empty');
|
|
97
|
+
}
|
|
98
|
+
tokens.forEach((token, index) => {
|
|
99
|
+
validation_1.SavingsValidation.validateAddress(token, `Token at index ${index}`);
|
|
100
|
+
});
|
|
101
|
+
pockets.forEach((pocket) => {
|
|
102
|
+
validation_1.SavingsValidation.validateAccountIndex(pocket);
|
|
103
|
+
});
|
|
104
|
+
const allBalances = await Promise.all(pockets.map((p) => this.getPocketBalance(p, tokens)));
|
|
105
|
+
return allBalances;
|
|
106
|
+
}
|
|
107
|
+
async transferToPocket(mainWallet, pocketIndex, amount) {
|
|
108
|
+
validation_1.SavingsValidation.validateAccountIndex(pocketIndex);
|
|
109
|
+
validation_1.SavingsValidation.validateAmountString(amount, 'Transfer amount');
|
|
110
|
+
const pocket = this.getPocket(pocketIndex);
|
|
111
|
+
return await (0, evm_1.sendNativeToken)(mainWallet, this.client, pocket.address, amount, 5);
|
|
112
|
+
}
|
|
113
|
+
async transferTokenToPocket(mainWallet, tokenAddress, pocketIndex, amount) {
|
|
114
|
+
validation_1.SavingsValidation.validateAddress(tokenAddress, 'Token address');
|
|
115
|
+
validation_1.SavingsValidation.validateAccountIndex(pocketIndex);
|
|
116
|
+
validation_1.SavingsValidation.validateAmount(amount, 'Transfer amount');
|
|
117
|
+
const pocket = this.getPocket(pocketIndex);
|
|
118
|
+
return await (0, evm_1.sendERC20Token)(mainWallet, this.client, tokenAddress, pocket.address, amount, 5);
|
|
119
|
+
}
|
|
120
|
+
accountFromPocketId(pocketIndex) {
|
|
121
|
+
const pocket = this.getPocket(pocketIndex);
|
|
122
|
+
return (0, accounts_1.privateKeyToAccount)(`0x${pocket.privateKey}`);
|
|
123
|
+
}
|
|
124
|
+
verifyPocketAddress(accountIndex, storedAddress) {
|
|
125
|
+
validation_1.SavingsValidation.validateAccountIndex(accountIndex);
|
|
126
|
+
validation_1.SavingsValidation.validateAddress(storedAddress, 'Stored address');
|
|
127
|
+
const pocket = this.getPocket(accountIndex);
|
|
128
|
+
return pocket.address.toLowerCase() === storedAddress.toLowerCase();
|
|
129
|
+
}
|
|
130
|
+
async sendToMainWallet(pocketIndex, amount, token) {
|
|
131
|
+
validation_1.SavingsValidation.validateAccountIndex(pocketIndex);
|
|
132
|
+
if (typeof amount !== 'bigint' || amount <= 0n) {
|
|
133
|
+
throw new Error(`Amount must be a positive bigint, got: ${amount}`);
|
|
134
|
+
}
|
|
135
|
+
if (token !== 'native') {
|
|
136
|
+
validation_1.SavingsValidation.validateAddress(token, 'Token address');
|
|
137
|
+
}
|
|
138
|
+
const pocket = this.getPocket(pocketIndex);
|
|
139
|
+
const account = this.accountFromPocketId(pocketIndex);
|
|
140
|
+
const mainWalletAddress = this.getMainWalletAddress();
|
|
141
|
+
const walletClient = (0, viem_1.createWalletClient)({
|
|
142
|
+
account,
|
|
143
|
+
transport: (0, viem_1.http)(this.chain.rpcUrl),
|
|
144
|
+
chain: (0, evm_1.fromChainToViemChain)(this.chain)
|
|
145
|
+
});
|
|
146
|
+
if (token === "native") {
|
|
147
|
+
return await (0, evm_1.sendNativeToken)(walletClient, this.client, mainWalletAddress, amount);
|
|
148
|
+
}
|
|
149
|
+
return await (0, evm_1.sendERC20Token)(walletClient, this.client, token, mainWalletAddress, amount);
|
|
150
|
+
}
|
|
151
|
+
dispose() {
|
|
152
|
+
super.dispose();
|
|
153
|
+
this.clearClient();
|
|
154
|
+
if (this.masterAddress) {
|
|
155
|
+
this.masterAddress = undefined;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
exports.EVMSavingsManager = EVMSavingsManager;
|