@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,428 @@
|
|
|
1
|
+
# Multi-Chain Savings Manager - Usage Guide
|
|
2
|
+
|
|
3
|
+
**Status**: โ
**Implementation Complete**
|
|
4
|
+
**Date**: 2026-02-02
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## ๐ What's Been Implemented
|
|
9
|
+
|
|
10
|
+
Following the same pattern as EVM/SVM VM classes, we've implemented a complete multi-chain savings manager system:
|
|
11
|
+
|
|
12
|
+
### Files Created
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
utils/savings/
|
|
16
|
+
โโโ savings-manager.ts # Abstract base class
|
|
17
|
+
โโโ evm-savings.ts # EVM implementation (Ethereum, Polygon, BSC, etc.)
|
|
18
|
+
โโโ svm-savings.ts # Solana implementation
|
|
19
|
+
โโโ multi-chain-savings.ts # Multi-chain orchestrator
|
|
20
|
+
โโโ index.ts # Updated exports
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Architecture
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
// Abstract base (like VM class)
|
|
27
|
+
abstract class SavingsManager<AddressType, ClientType, WalletClientType>
|
|
28
|
+
|
|
29
|
+
โ
|
|
30
|
+
|
|
31
|
+
// EVM implementation
|
|
32
|
+
EVMSavingsManager extends SavingsManager<Hex, PublicClient, WalletClient>
|
|
33
|
+
|
|
34
|
+
// Solana implementation
|
|
35
|
+
SVMSavingsManager extends SavingsManager<PublicKey, Connection, Keypair>
|
|
36
|
+
|
|
37
|
+
โ
|
|
38
|
+
|
|
39
|
+
// Multi-chain orchestrator
|
|
40
|
+
MultiChainSavingsManager (manages both EVM and SVM)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## ๐ Usage Examples
|
|
46
|
+
|
|
47
|
+
### Example 1: Single EVM Chain (Ethereum)
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { EVMSavingsManager } from './utils/savings';
|
|
51
|
+
|
|
52
|
+
const manager = new EVMSavingsManager(
|
|
53
|
+
mnemonic,
|
|
54
|
+
{
|
|
55
|
+
chainId: 1,
|
|
56
|
+
name: 'ethereum',
|
|
57
|
+
rpcUrl: 'https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY'
|
|
58
|
+
},
|
|
59
|
+
0 // wallet index
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
// Get pocket 0
|
|
63
|
+
const pocket = manager.getPocket(0);
|
|
64
|
+
console.log('Pocket address:', pocket.address);
|
|
65
|
+
console.log('Derivation path:', pocket.derivationPath);
|
|
66
|
+
|
|
67
|
+
// Get balances for pocket 0
|
|
68
|
+
const balances = await manager.getPocketBalance(0, [
|
|
69
|
+
'0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' // USDC
|
|
70
|
+
]);
|
|
71
|
+
|
|
72
|
+
console.log('Native ETH:', balances[0].balance.formatted);
|
|
73
|
+
console.log('USDC:', balances[1].balance.formatted);
|
|
74
|
+
|
|
75
|
+
// Transfer 0.1 ETH to pocket 0
|
|
76
|
+
await manager.transferToPocket(walletClient, 0, '0.1');
|
|
77
|
+
|
|
78
|
+
// Transfer from pocket back to main wallet
|
|
79
|
+
await manager.sendToMainWallet(0, 100000000n, 'native');
|
|
80
|
+
|
|
81
|
+
// Cleanup
|
|
82
|
+
manager.dispose();
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
### Example 2: Solana (SVM)
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import { SVMSavingsManager } from './utils/savings';
|
|
91
|
+
import { Keypair } from '@solana/web3.js';
|
|
92
|
+
|
|
93
|
+
const manager = new SVMSavingsManager(
|
|
94
|
+
mnemonic,
|
|
95
|
+
'https://api.mainnet-beta.solana.com',
|
|
96
|
+
0 // wallet index
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
// Get pocket 0
|
|
100
|
+
const pocket = manager.getPocket(0);
|
|
101
|
+
console.log('Pocket address:', pocket.address.toBase58());
|
|
102
|
+
console.log('Derivation path:', pocket.derivationPath);
|
|
103
|
+
|
|
104
|
+
// Get balances for pocket 0
|
|
105
|
+
const balances = await manager.getPocketBalance(0, [
|
|
106
|
+
'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' // USDC on Solana
|
|
107
|
+
]);
|
|
108
|
+
|
|
109
|
+
console.log('Native SOL:', balances[0].balance.formatted);
|
|
110
|
+
console.log('USDC:', balances[1].balance.formatted);
|
|
111
|
+
|
|
112
|
+
// Transfer 1 SOL to pocket 0
|
|
113
|
+
const mainWalletKeypair = manager.getMainWallet().privateKey;
|
|
114
|
+
await manager.transferToPocket(mainWalletKeypair, 0, 1000000000n); // 1 SOL
|
|
115
|
+
|
|
116
|
+
// Transfer SPL token
|
|
117
|
+
await manager.transferTokenToPocket(
|
|
118
|
+
mainWalletKeypair,
|
|
119
|
+
{ address: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', decimals: 6 },
|
|
120
|
+
0,
|
|
121
|
+
1000000n // 1 USDC
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
// Cleanup
|
|
125
|
+
manager.dispose();
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
### Example 3: Multi-Chain (The Full Power!)
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
import { MultiChainSavingsManager } from './utils/savings';
|
|
134
|
+
|
|
135
|
+
// Create manager with multiple chains
|
|
136
|
+
const manager = new MultiChainSavingsManager(
|
|
137
|
+
mnemonic,
|
|
138
|
+
[
|
|
139
|
+
// EVM chains
|
|
140
|
+
{
|
|
141
|
+
id: 'ethereum',
|
|
142
|
+
type: 'EVM',
|
|
143
|
+
config: {
|
|
144
|
+
chainId: 1,
|
|
145
|
+
name: 'ethereum',
|
|
146
|
+
rpcUrl: 'https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY'
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
id: 'polygon',
|
|
151
|
+
type: 'EVM',
|
|
152
|
+
config: {
|
|
153
|
+
chainId: 137,
|
|
154
|
+
name: 'polygon',
|
|
155
|
+
rpcUrl: 'https://polygon-mainnet.g.alchemy.com/v2/YOUR_KEY'
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
id: 'bsc',
|
|
160
|
+
type: 'EVM',
|
|
161
|
+
config: {
|
|
162
|
+
chainId: 56,
|
|
163
|
+
name: 'bsc',
|
|
164
|
+
rpcUrl: 'https://bsc-dataseed1.binance.org'
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
// Solana
|
|
168
|
+
{
|
|
169
|
+
id: 'solana',
|
|
170
|
+
type: 'SVM',
|
|
171
|
+
config: {
|
|
172
|
+
rpcUrl: 'https://api.mainnet-beta.solana.com'
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
],
|
|
176
|
+
0 // wallet index
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
// Get pocket addresses (notice EVM chains share addresses!)
|
|
180
|
+
const ethAddress = manager.getPocketAddress('ethereum', 0);
|
|
181
|
+
const polyAddress = manager.getPocketAddress('polygon', 0);
|
|
182
|
+
const bscAddress = manager.getPocketAddress('bsc', 0);
|
|
183
|
+
const solAddress = manager.getPocketAddress('solana', 0);
|
|
184
|
+
|
|
185
|
+
console.log('Ethereum pocket 0:', ethAddress);
|
|
186
|
+
console.log('Polygon pocket 0:', polyAddress);
|
|
187
|
+
console.log('BSC pocket 0:', bscAddress);
|
|
188
|
+
console.log('Solana pocket 0:', solAddress);
|
|
189
|
+
|
|
190
|
+
console.log('ETH === Polygon?', ethAddress === polyAddress); // true!
|
|
191
|
+
console.log('ETH === BSC?', ethAddress === bscAddress); // true!
|
|
192
|
+
console.log('ETH === Solana?', ethAddress === solAddress); // false (different chain)
|
|
193
|
+
|
|
194
|
+
// Get balances across all chains for pocket 0
|
|
195
|
+
const balances = await manager.getPocketBalanceAcrossChains(
|
|
196
|
+
0, // pocket index
|
|
197
|
+
new Map([
|
|
198
|
+
['ethereum', ['0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48']], // USDC on Ethereum
|
|
199
|
+
['polygon', ['0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174']], // USDC on Polygon
|
|
200
|
+
['bsc', ['0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d']], // USDC on BSC
|
|
201
|
+
['solana', ['EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v']] // USDC on Solana
|
|
202
|
+
])
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
// Display balances by chain
|
|
206
|
+
balances.forEach(chainBalance => {
|
|
207
|
+
console.log(`\n${chainBalance.chainId.toUpperCase()}:`);
|
|
208
|
+
console.log(` Address: ${chainBalance.address}`);
|
|
209
|
+
chainBalance.balances.forEach(bal => {
|
|
210
|
+
const tokenName = bal.token === 'native' ? 'Native' : 'USDC';
|
|
211
|
+
console.log(` ${tokenName}: ${bal.balance.formatted}`);
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// Get all pockets (0, 1, 2) balances across all chains
|
|
216
|
+
const allBalances = await manager.getAllPocketsBalanceAcrossChains(
|
|
217
|
+
[0, 1, 2], // pocket indices
|
|
218
|
+
new Map([
|
|
219
|
+
['ethereum', ['0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48']],
|
|
220
|
+
['polygon', ['0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174']],
|
|
221
|
+
['solana', ['EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v']]
|
|
222
|
+
])
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
// Display total across all pockets and chains
|
|
226
|
+
for (const [pocketIndex, chainBalances] of allBalances) {
|
|
227
|
+
console.log(`\nPocket ${pocketIndex}:`);
|
|
228
|
+
chainBalances.forEach(cb => {
|
|
229
|
+
console.log(` ${cb.chainId}: ${cb.balances.length} tokens`);
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Advanced: Get specific chain manager for direct operations
|
|
234
|
+
const ethManager = manager.getEVMManager('ethereum');
|
|
235
|
+
await ethManager.transferToPocket(ethWalletClient, 0, '0.1');
|
|
236
|
+
|
|
237
|
+
const solManager = manager.getSVMManager('solana');
|
|
238
|
+
await solManager.transferToPocket(solKeypair, 0, 1000000000n);
|
|
239
|
+
|
|
240
|
+
// Cleanup all chains
|
|
241
|
+
manager.dispose();
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## ๐ Key Features
|
|
247
|
+
|
|
248
|
+
### 1. EVM Address Sharing โ
|
|
249
|
+
All EVM-compatible chains (Ethereum, Polygon, BSC, Arbitrum, Optimism, etc.) share the same addresses because they all use BIP-44 coin type 60.
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
// Same pocket, same address across all EVM chains
|
|
253
|
+
const ethPocket = manager.getPocketAddress('ethereum', 0);
|
|
254
|
+
const polyPocket = manager.getPocketAddress('polygon', 0);
|
|
255
|
+
// ethPocket === polyPocket โ
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### 2. Solana Has Different Addresses โ
|
|
259
|
+
Solana uses BIP-44 coin type 501, so it generates different addresses.
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
const ethPocket = manager.getPocketAddress('ethereum', 0); // 0x...
|
|
263
|
+
const solPocket = manager.getPocketAddress('solana', 0); // Different!
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### 3. Memory Management โ
|
|
267
|
+
Following the VM pattern, all managers support proper cleanup:
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
// Clear specific pocket
|
|
271
|
+
manager.clearPocket(0);
|
|
272
|
+
|
|
273
|
+
// Clear all pockets
|
|
274
|
+
manager.clearAllPockets();
|
|
275
|
+
|
|
276
|
+
// Clear all RPC clients
|
|
277
|
+
manager.clearAllClients();
|
|
278
|
+
|
|
279
|
+
// Dispose everything (clear mnemonic + all data)
|
|
280
|
+
manager.dispose();
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### 4. Lazy RPC Client Creation โ
|
|
284
|
+
RPC clients are created on-demand and can be garbage collected:
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
// Client created when first accessed
|
|
288
|
+
const balance = await manager.getPocketBalance(...);
|
|
289
|
+
|
|
290
|
+
// Clear client to free memory
|
|
291
|
+
manager.clearClient(); // Single chain
|
|
292
|
+
manager.clearAllClients(); // Multi-chain
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## ๐๏ธ Architecture Highlights
|
|
298
|
+
|
|
299
|
+
### Follows VM Pattern Exactly
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
// VM Pattern
|
|
303
|
+
abstract class VM<AddressType, PrivateKeyType, ConnectionType> { }
|
|
304
|
+
class EVMVM extends VM<string, string, PublicClient> { }
|
|
305
|
+
class SVMVM extends VM<PublicKey, Keypair, Connection> { }
|
|
306
|
+
|
|
307
|
+
// Savings Pattern (Same Structure!)
|
|
308
|
+
abstract class SavingsManager<AddressType, ClientType, WalletClientType> { }
|
|
309
|
+
class EVMSavingsManager extends SavingsManager<Hex, PublicClient, WalletClient> { }
|
|
310
|
+
class SVMSavingsManager extends SavingsManager<PublicKey, Connection, Keypair> { }
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Type Safety
|
|
314
|
+
|
|
315
|
+
All managers are fully typed with generics:
|
|
316
|
+
- **AddressType**: `Hex` for EVM, `PublicKey` for Solana
|
|
317
|
+
- **ClientType**: `PublicClient` for EVM, `Connection` for Solana
|
|
318
|
+
- **WalletClientType**: `WalletClient` for EVM, `Keypair` for Solana
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## ๐ Comparison: Single-Chain vs Multi-Chain
|
|
323
|
+
|
|
324
|
+
### Before (Old Pattern)
|
|
325
|
+
```typescript
|
|
326
|
+
// Old: Single chain only
|
|
327
|
+
const manager = new BaseSavingsManager(mnemonic, 0, ethConfig);
|
|
328
|
+
const pocket = manager.getPocket(0);
|
|
329
|
+
const balances = await manager.getPocketTokenBalance([usdc], 0);
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### After (New Pattern)
|
|
333
|
+
```typescript
|
|
334
|
+
// New: Single chain (still works the same)
|
|
335
|
+
const manager = new EVMSavingsManager(mnemonic, ethConfig, 0);
|
|
336
|
+
const pocket = manager.getPocket(0);
|
|
337
|
+
const balances = await manager.getPocketBalance(0, [usdc]);
|
|
338
|
+
|
|
339
|
+
// OR Multi-chain (new capability!)
|
|
340
|
+
const multiManager = new MultiChainSavingsManager(mnemonic, [
|
|
341
|
+
{ id: 'ethereum', type: 'EVM', config: ethConfig },
|
|
342
|
+
{ id: 'polygon', type: 'EVM', config: polyConfig },
|
|
343
|
+
{ id: 'solana', type: 'SVM', config: { rpcUrl: '...' } }
|
|
344
|
+
], 0);
|
|
345
|
+
|
|
346
|
+
// Query all chains at once
|
|
347
|
+
const allBalances = await multiManager.getPocketBalanceAcrossChains(0, tokensByChain);
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## ๐ฏ Migration Guide
|
|
353
|
+
|
|
354
|
+
### Old Code (Legacy Manager)
|
|
355
|
+
```typescript
|
|
356
|
+
import { SavingsManager } from './utils/savings';
|
|
357
|
+
|
|
358
|
+
const manager = new SavingsManager(mnemonic, ethConfig, 0);
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
**Status**: โ
Still works! Legacy exports maintained for backward compatibility.
|
|
362
|
+
|
|
363
|
+
### New Code (Multi-Chain)
|
|
364
|
+
```typescript
|
|
365
|
+
import { EVMSavingsManager, SVMSavingsManager, MultiChainSavingsManager } from './utils/savings';
|
|
366
|
+
|
|
367
|
+
// Single EVM chain
|
|
368
|
+
const evmManager = new EVMSavingsManager(mnemonic, ethConfig, 0);
|
|
369
|
+
|
|
370
|
+
// Single Solana chain
|
|
371
|
+
const svmManager = new SVMSavingsManager(mnemonic, rpcUrl, 0);
|
|
372
|
+
|
|
373
|
+
// Multi-chain
|
|
374
|
+
const multiManager = new MultiChainSavingsManager(mnemonic, chains, 0);
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
## ๐งช Testing Checklist
|
|
380
|
+
|
|
381
|
+
- [x] Abstract base class created
|
|
382
|
+
- [x] EVM implementation complete
|
|
383
|
+
- [x] Solana implementation complete
|
|
384
|
+
- [x] Multi-chain orchestrator complete
|
|
385
|
+
- [x] Exports updated
|
|
386
|
+
- [x] Build successful (no TypeScript errors in savings module)
|
|
387
|
+
- [ ] Unit tests (TODO)
|
|
388
|
+
- [ ] Integration tests (TODO)
|
|
389
|
+
- [ ] End-to-end tests (TODO)
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
## ๐ Next Steps (Optional)
|
|
394
|
+
|
|
395
|
+
1. **Add Unit Tests**
|
|
396
|
+
- Test pocket derivation
|
|
397
|
+
- Test balance queries
|
|
398
|
+
- Test transfers
|
|
399
|
+
|
|
400
|
+
2. **Add Integration Tests**
|
|
401
|
+
- Test with real RPC endpoints (testnet)
|
|
402
|
+
- Test cross-chain operations
|
|
403
|
+
|
|
404
|
+
3. **Performance Optimizations**
|
|
405
|
+
- Implement balance caching
|
|
406
|
+
- Batch RPC requests
|
|
407
|
+
- Add rate limiting helpers
|
|
408
|
+
|
|
409
|
+
4. **Additional Features**
|
|
410
|
+
- Pocket discovery (scan for pockets with balances)
|
|
411
|
+
- Transaction history per pocket
|
|
412
|
+
- USD value aggregation (with price feeds)
|
|
413
|
+
|
|
414
|
+
---
|
|
415
|
+
|
|
416
|
+
## ๐ Summary
|
|
417
|
+
|
|
418
|
+
โ
**Complete multi-chain savings manager implemented**
|
|
419
|
+
- Follows EVM/SVM VM pattern exactly
|
|
420
|
+
- Supports all EVM chains + Solana
|
|
421
|
+
- EVM chains share addresses (coin type 60)
|
|
422
|
+
- Solana has different addresses (coin type 501)
|
|
423
|
+
- Full type safety with generics
|
|
424
|
+
- Memory management and disposal patterns
|
|
425
|
+
- Lazy RPC client creation
|
|
426
|
+
- Backward compatible with legacy code
|
|
427
|
+
|
|
428
|
+
**Ready to use!** ๐
|