@deserialize/multi-vm-wallet 1.5.35 → 1.6.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/utils/constant.ts CHANGED
@@ -15,7 +15,21 @@ export const DefaultChains: ChainWalletConfig[] = [{
15
15
  logoUrl: "https://solana.com/src/img/branding/solanaLogoMark.svg",
16
16
  vmType: "SVM",
17
17
  savings: {
18
- tokens: []
18
+ supported: true,
19
+ tokens: [
20
+ {
21
+ name: "USDC",
22
+ symbol: "USDC",
23
+ decimals: 6,
24
+ address: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
25
+ },
26
+ {
27
+ name: "USDT",
28
+ symbol: "USDT",
29
+ decimals: 6,
30
+ address: "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"
31
+ }
32
+ ]
19
33
  }
20
34
  }
21
35
 
@@ -34,6 +48,7 @@ export const DefaultChains: ChainWalletConfig[] = [{
34
48
  logoUrl: "https://push.org/assets/website/favicons/favicon.svg",
35
49
  vmType: "EVM",
36
50
  savings: {
51
+ supported: false,
37
52
  tokens: []
38
53
  }
39
54
  }
@@ -53,7 +68,24 @@ export const DefaultChains: ChainWalletConfig[] = [{
53
68
  },
54
69
  testnet: false,
55
70
  logoUrl: "https://etherscan.io/images/svg/brands/ethereum-original-light.svg",
56
- vmType: "EVM"
71
+ vmType: "EVM",
72
+ savings: {
73
+ supported: true,
74
+ tokens: [{
75
+ name: "USDC",
76
+ symbol: "USDC",
77
+ decimals: 6,
78
+ address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
79
+ },
80
+ {
81
+ name: "USDT",
82
+ symbol: "USDT",
83
+ decimals: 6,
84
+ address: "0xdac17f958d2ee523a2206206994597c13d831ec7"
85
+ }
86
+
87
+ ]
88
+ }
57
89
  }, {
58
90
  chainId: 56,
59
91
  name: "BSC",
@@ -68,7 +100,18 @@ export const DefaultChains: ChainWalletConfig[] = [{
68
100
  logoUrl: "https://bscscan.com/assets/bsc/images/svg/logos/token-light.svg?v=25.10.5.0",
69
101
  vmType: "EVM",
70
102
  savings: {
71
- tokens: []
103
+ supported: true,
104
+ tokens: [
105
+
106
+ {
107
+ name: "BUSD",
108
+ symbol: "BUSD",
109
+ decimals: 6,
110
+ address: "0x55d398326f99059fF775485246999027B3197955"
111
+ }
112
+
113
+
114
+ ]
72
115
  }
73
116
  },
74
117
 
@@ -101,7 +144,15 @@ export const DefaultChains: ChainWalletConfig[] = [{
101
144
  logoUrl: "https://avatars.githubusercontent.com/u/108554348?s=200&v=4",
102
145
  vmType: "EVM",
103
146
  savings: {
104
- tokens: []
147
+ supported: true,
148
+ tokens: [
149
+ {
150
+ name: "USDC",
151
+ symbol: "USDC",
152
+ decimals: 6,
153
+ address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
154
+ }
155
+ ]
105
156
  }
106
157
  },
107
158
 
@@ -119,6 +170,7 @@ export const DefaultChains: ChainWalletConfig[] = [{
119
170
  logoUrl: "https://avatars.githubusercontent.com/u/119917794?s=280&v=4",
120
171
  vmType: "EVM",
121
172
  savings: {
173
+ supported: false,
122
174
  tokens: []
123
175
  }
124
176
  },
@@ -136,6 +188,7 @@ export const DefaultChains: ChainWalletConfig[] = [{
136
188
  logoUrl: "https://avatars.githubusercontent.com/u/58791460?s=280&v=4",
137
189
  vmType: "EVM",
138
190
  savings: {
191
+ supported: false,
139
192
  tokens: []
140
193
  }
141
194
  },
@@ -155,6 +208,7 @@ export const DefaultChains: ChainWalletConfig[] = [{
155
208
  logoUrl: "https://polygonscan.com/images/svg/brands/polygon-light.svg?v=0.0.36",
156
209
  vmType: "EVM",
157
210
  savings: {
211
+ supported: false,
158
212
  tokens: []
159
213
  }
160
214
  }
package/utils/evm/evm.ts CHANGED
@@ -806,7 +806,7 @@ export class EVMVM extends VM<string, string, PublicClient> {
806
806
  try {
807
807
  // Pocket derivation: m/44'/60'/{accountIndex+1}'/0/{walletIndex}
808
808
  const pocketIndex = accountIndex + 1;
809
- const derivationPath = `m/44'/60'/${pocketIndex}'/0/${walletIndex}'`;
809
+ const derivationPath = `m/44'/60'/${pocketIndex}'/0/${walletIndex}`;
810
810
 
811
811
  // Derive pocket
812
812
  const { privateKey } = EVMDeriveChildPrivateKey(this.seed, walletIndex, `m/44'/60'/${pocketIndex}'/0/`);
@@ -119,9 +119,10 @@ export class EVMSavingsManager extends SavingsManager<Hex, PublicClient, WalletC
119
119
 
120
120
  // Add 1 to preserve index 0 for main wallet
121
121
  const pocketIndex = accountIndex + 1;
122
- const derivationPath = `${this.derivationPathBase}${pocketIndex}'/0/${this.walletIndex}`;
122
+ const derivationPathBase = `${this.derivationPathBase}${pocketIndex}'/0/`;
123
+ const derivationPath = `${derivationPathBase}${this.walletIndex}'`;
123
124
  const seed = mnemonicToSeed(this.mnemonic);
124
- const { privateKey } = EVMDeriveChildPrivateKey(seed, this.walletIndex, derivationPath);
125
+ const { privateKey } = EVMDeriveChildPrivateKey(seed, this.walletIndex, derivationPathBase);
125
126
  const wallet = new ethers.Wallet(privateKey);
126
127
 
127
128
  const pocket: Pocket<Hex> = {
@@ -142,9 +143,10 @@ export class EVMSavingsManager extends SavingsManager<Hex, PublicClient, WalletC
142
143
  */
143
144
  getMainWallet() {
144
145
  this.checkNotDisposed();
145
- const derivationPath = `${this.derivationPathBase}0'/0/${this.walletIndex}`;
146
+ const derivationPathBase = `${this.derivationPathBase}0'/0/`;
147
+ const derivationPath = `${derivationPathBase}${this.walletIndex}'`;
146
148
  const seed = mnemonicToSeed(this.mnemonic);
147
- const { privateKey } = EVMDeriveChildPrivateKey(seed, this.walletIndex, derivationPath);
149
+ const { privateKey } = EVMDeriveChildPrivateKey(seed, this.walletIndex, derivationPathBase);
148
150
  const wallet = new ethers.Wallet(privateKey);
149
151
 
150
152
  return {
@@ -132,14 +132,13 @@ export class MultiChainSavingsManager {
132
132
  throw new Error(`Chain with id '${chain.id}' already exists`);
133
133
  }
134
134
 
135
- this.chainConfigs.set(chain.id, chain);
136
-
137
135
  if (chain.type === 'EVM') {
138
136
  const manager = new EVMSavingsManager(
139
137
  this.mnemonic,
140
138
  chain.config as ChainWalletConfig,
141
139
  this.walletIndex
142
140
  );
141
+ this.chainConfigs.set(chain.id, chain);
143
142
  this.evmManagers.set(chain.id, manager);
144
143
  } else if (chain.type === 'SVM') {
145
144
  const config = chain.config as { rpcUrl: string };
@@ -151,6 +150,7 @@ export class MultiChainSavingsManager {
151
150
  config.rpcUrl,
152
151
  this.walletIndex
153
152
  );
153
+ this.chainConfigs.set(chain.id, chain);
154
154
  this.svmManagers.set(chain.id, manager);
155
155
  } else {
156
156
  throw new Error(`Unknown chain type: ${chain.type}`);
@@ -1,4 +1,4 @@
1
- import { EVMDeriveChildPrivateKey, GenerateSeed, mnemonicToSeed } from "../walletBip32";
1
+ import { EVMDeriveChildPrivateKey, mnemonicToSeed } from "../walletBip32";
2
2
  import { ethers } from "ethers";
3
3
  import { WalletClient, PublicClient, Hex, Chain, createWalletClient, createPublicClient, http } from "viem";
4
4
  import { } from "../utils";
@@ -141,11 +141,12 @@ export class BaseSavingsManager {
141
141
 
142
142
  //? for the sake of derivation we will add one to the index of the pocket that was passed so as to preserve the index 0 as the main wallet index
143
143
  const pocketIndex = accountIndex + 1
144
- const derivationPath = `m/44'/60'/${pocketIndex}'/0/${this.walletIndex}`;
145
- const { privateKey } = EVMDeriveChildPrivateKey(mnemonicToSeed(this.mnemonic), this.walletIndex, derivationPath);
144
+ const derivationPathBase = `m/44'/60'/${pocketIndex}'/0/`;
145
+ const derivationPath = `${derivationPathBase}${this.walletIndex}'`;
146
+ const { privateKey } = EVMDeriveChildPrivateKey(mnemonicToSeed(this.mnemonic), this.walletIndex, derivationPathBase);
146
147
  const wallet = new ethers.Wallet(privateKey);
147
148
  const pocket = { privateKey, address: wallet.address, derivationPath, index: pocketIndex };
148
- this.pockets.set(pocketIndex, pocket);
149
+ this.pockets.set(accountIndex, pocket);
149
150
  return pocket;
150
151
  }
151
152
 
@@ -164,8 +165,13 @@ export class BaseSavingsManager {
164
165
  * ```
165
166
  */
166
167
  getMainWallet() {
167
- const mainWalletDerivationPath = `m/44'/60'/0'/0/${this.walletIndex}`;
168
- const { privateKey } = EVMDeriveChildPrivateKey(this.mnemonic, this.walletIndex, mainWalletDerivationPath);
168
+ const mainWalletDerivationPathBase = `m/44'/60'/0'/0/`;
169
+ const mainWalletDerivationPath = `${mainWalletDerivationPathBase}${this.walletIndex}'`;
170
+ const { privateKey } = EVMDeriveChildPrivateKey(
171
+ mnemonicToSeed(this.mnemonic),
172
+ this.walletIndex,
173
+ mainWalletDerivationPathBase
174
+ );
169
175
  const wallet = new ethers.Wallet(privateKey);
170
176
  return { privateKey, address: wallet.address, derivationPath: mainWalletDerivationPath };
171
177
  }
@@ -185,8 +191,7 @@ export class BaseSavingsManager {
185
191
  */
186
192
  getMainWalletAddress(): Hex {
187
193
  if (this.masterAddress) return this.masterAddress;
188
- const { privateKey } = this.getMainWallet()
189
- return new ethers.Wallet(privateKey).address as Hex
194
+ return this.getMainWallet().address as Hex
190
195
  }
191
196
 
192
197
  /**
@@ -342,8 +347,8 @@ export class BaseSavingsManager {
342
347
  * ```
343
348
  */
344
349
  accountFromPocketId(p: number): Account {
345
- // Validation is done in derivePocket
346
- return privateKeyToAccount(`0x${this.derivePocket(p).privateKey}`)
350
+ // Validation is done in getPocket
351
+ return privateKeyToAccount(`0x${this.getPocket(p).privateKey}`)
347
352
  }
348
353
 
349
354
  /**
@@ -599,16 +604,13 @@ export class SavingsManager extends BaseSavingsManager {
599
604
  * Withdraws either native tokens or ERC-20 tokens from a pocket to the main wallet.
600
605
  *
601
606
  * @param pocketIndex - Index of the pocket to withdraw from
602
- * @param amountInEther - Amount to send (interpreted as bigint, not actual ether units)
607
+ * @param amount - Amount to send in base units
603
608
  * @param token - Token address or "native" for native blockchain token
604
609
  * @returns Transaction result containing hash and success status
605
610
  *
606
611
  * @throws Error if validation fails
607
612
  *
608
613
  * @remarks
609
- * Despite the parameter name, amountInEther is actually interpreted as a raw bigint value,
610
- * not converted from ether units. Consider renaming to `amount` for clarity.
611
- *
612
614
  * @example
613
615
  * ```typescript
614
616
  * // Send native token from pocket to main wallet
@@ -626,13 +628,11 @@ export class SavingsManager extends BaseSavingsManager {
626
628
  * );
627
629
  * ```
628
630
  */
629
- async sendToMainWallet(pocketIndex: number, amountInEther: number, token: Hex | "native"): Promise<TransactionResult> {
631
+ async sendToMainWallet(pocketIndex: number, amount: bigint, token: Hex | "native"): Promise<TransactionResult> {
630
632
  // Validate inputs
631
633
  SavingsValidation.validateAccountIndex(pocketIndex);
632
634
 
633
- if (typeof amountInEther !== 'number' || amountInEther <= 0) {
634
- throw new Error(`Amount must be a positive number, got: ${amountInEther}`);
635
- }
635
+ SavingsValidation.validateAmount(amount, 'Transfer amount');
636
636
 
637
637
  if (token !== 'native') {
638
638
  SavingsValidation.validateAddress(token, 'Token address');
@@ -648,9 +648,9 @@ export class SavingsManager extends BaseSavingsManager {
648
648
  }
649
649
  )
650
650
  if (token === "native") {
651
- return await sendNativeToken(walletClient, this.client, mainWalletAddress, BigInt(amountInEther))
651
+ return await sendNativeToken(walletClient, this.client, mainWalletAddress, amount)
652
652
  }
653
- const res = await sendERC20Token(walletClient, this.client, token, mainWalletAddress as Hex, BigInt(amountInEther))
653
+ const res = await sendERC20Token(walletClient, this.client, token, mainWalletAddress as Hex, amount)
654
654
  return res
655
655
  }
656
656
  }
@@ -44,7 +44,7 @@ import { EVMDeriveChildPrivateKey, mnemonicToSeed } from "../walletBip32";
44
44
  export interface TransferFromPocketOptions {
45
45
  /** Wallet index in derivation path (default: 0) */
46
46
  walletIndex?: number;
47
- /** Account/pocket index to transfer from (1-based, 0 is main wallet) */
47
+ /** Pocket index to transfer from (0-based) */
48
48
  accountIndex: number;
49
49
  /** Destination address */
50
50
  to: string;
@@ -76,7 +76,7 @@ export interface TransferTokenFromPocketOptions extends Omit<TransferFromPocketO
76
76
  export interface GetPocketBalanceOptions {
77
77
  /** Wallet index in derivation path (default: 0) */
78
78
  walletIndex?: number;
79
- /** Account/pocket index (1-based, 0 is main wallet) */
79
+ /** Pocket index (0-based) */
80
80
  accountIndex: number;
81
81
  }
82
82
 
@@ -129,7 +129,7 @@ export class SavingsOperations {
129
129
  * Derives a savings pocket wallet from mnemonic
130
130
  *
131
131
  * @param mnemonic - BIP-39 mnemonic phrase
132
- * @param accountIndex - Account/pocket index (1-based, 0 is main wallet)
132
+ * @param accountIndex - Pocket index (0-based)
133
133
  * @param walletIndex - Wallet index (default: 0)
134
134
  * @param provider - RPC provider to connect wallet to
135
135
  * @returns Derived wallet, address, and cleanup function
@@ -137,7 +137,7 @@ export class SavingsOperations {
137
137
  * @remarks
138
138
  * IMPORTANT: Always call cleanup() when done with the wallet to zero out private key
139
139
  *
140
- * Derivation path: m/44'/60'/{accountIndex}'/0/{walletIndex}'
140
+ * Derivation path: m/44'/60'/{accountIndex + 1}'/0/{walletIndex}'
141
141
  *
142
142
  * @throws Error if validation fails
143
143
  *
@@ -154,9 +154,10 @@ export class SavingsOperations {
154
154
  SavingsValidation.validateAccountIndex(accountIndex);
155
155
  SavingsValidation.validateWalletIndex(walletIndex);
156
156
 
157
- // Derive pocket private key
157
+ // Derive pocket private key (account 0 is main wallet, so pockets start at +1)
158
+ const pocketIndex = accountIndex + 1;
158
159
  const seed = mnemonicToSeed(mnemonic);
159
- const { privateKey } = EVMDeriveChildPrivateKey(seed, walletIndex, `m/44'/60'/${accountIndex}'/`);
160
+ const { privateKey } = EVMDeriveChildPrivateKey(seed, walletIndex, `m/44'/60'/${pocketIndex}'/0/`);
160
161
 
161
162
  // Create wallet
162
163
  const wallet = new Wallet(privateKey, provider);
@@ -473,7 +474,7 @@ export class SavingsOperations {
473
474
  * Get the address of a savings pocket without storing credentials
474
475
  *
475
476
  * @param mnemonic - BIP-39 mnemonic phrase
476
- * @param accountIndex - Account/pocket index
477
+ * @param accountIndex - Pocket index (0-based)
477
478
  * @param walletIndex - Wallet index (default: 0)
478
479
  * @returns Pocket address
479
480
  *
@@ -495,9 +496,10 @@ export class SavingsOperations {
495
496
  SavingsValidation.validateAccountIndex(accountIndex);
496
497
  SavingsValidation.validateWalletIndex(walletIndex);
497
498
 
498
- // Derive address
499
+ // Derive address (account 0 is main wallet, so pockets start at +1)
500
+ const pocketIndex = accountIndex + 1;
499
501
  const seed = mnemonicToSeed(mnemonic);
500
- const { privateKey } = EVMDeriveChildPrivateKey(seed, walletIndex, `m/44'/60'/${accountIndex}'/`);
502
+ const { privateKey } = EVMDeriveChildPrivateKey(seed, walletIndex, `m/44'/60'/${pocketIndex}'/0/`);
501
503
  const wallet = new Wallet(privateKey);
502
504
  const address = wallet.address;
503
505
 
@@ -98,6 +98,13 @@ export class SVMSavingsManager extends SavingsManager<PublicKey, Connection, Key
98
98
  this._client = undefined;
99
99
  }
100
100
 
101
+ private toSafeNumberAmount(amount: bigint, label: string): number {
102
+ if (amount > BigInt(Number.MAX_SAFE_INTEGER)) {
103
+ throw new Error(`${label} exceeds Number.MAX_SAFE_INTEGER and cannot be represented safely: ${amount}`);
104
+ }
105
+ return Number(amount);
106
+ }
107
+
101
108
  /**
102
109
  * Derive a savings pocket at the specified account index
103
110
  *
@@ -110,9 +117,10 @@ export class SVMSavingsManager extends SavingsManager<PublicKey, Connection, Key
110
117
 
111
118
  // Add 1 to preserve index 0 for main wallet
112
119
  const pocketIndex = accountIndex + 1;
113
- const derivationPath = `${this.derivationPathBase}${pocketIndex}'/0/${this.walletIndex}`;
120
+ const derivationPathBase = `${this.derivationPathBase}${pocketIndex}'/0/`;
121
+ const derivationPath = `${derivationPathBase}${this.walletIndex}'`;
114
122
  const seed = mnemonicToSeed(this.mnemonic);
115
- const keypair = SVMDeriveChildPrivateKey(seed, this.walletIndex, derivationPath);
123
+ const keypair = SVMDeriveChildPrivateKey(seed, this.walletIndex, derivationPathBase);
116
124
 
117
125
  const pocket: Pocket<PublicKey> = {
118
126
  privateKey: keypair,
@@ -132,9 +140,10 @@ export class SVMSavingsManager extends SavingsManager<PublicKey, Connection, Key
132
140
  */
133
141
  getMainWallet() {
134
142
  this.checkNotDisposed();
135
- const derivationPath = `${this.derivationPathBase}0'/0/${this.walletIndex}`;
143
+ const derivationPathBase = `${this.derivationPathBase}0'/0/`;
144
+ const derivationPath = `${derivationPathBase}${this.walletIndex}'`;
136
145
  const seed = mnemonicToSeed(this.mnemonic);
137
- const keypair = SVMDeriveChildPrivateKey(seed, this.walletIndex, derivationPath);
146
+ const keypair = SVMDeriveChildPrivateKey(seed, this.walletIndex, derivationPathBase);
138
147
 
139
148
  return {
140
149
  privateKey: keypair,
@@ -262,7 +271,7 @@ export class SVMSavingsManager extends SavingsManager<PublicKey, Connection, Key
262
271
  const tx = await getTransferNativeTransaction(
263
272
  mainWallet,
264
273
  pocket.address,
265
- Number(amount),
274
+ this.toSafeNumberAmount(amount, 'Native transfer amount'),
266
275
  this.client
267
276
  );
268
277
 
@@ -297,7 +306,7 @@ export class SVMSavingsManager extends SavingsManager<PublicKey, Connection, Key
297
306
  mainWallet,
298
307
  pocket.address,
299
308
  tokenInfo as any, // TokenInfo type
300
- Number(amount),
309
+ this.toSafeNumberAmount(amount, 'Token transfer amount'),
301
310
  this.client
302
311
  );
303
312
 
@@ -365,7 +374,7 @@ export class SVMSavingsManager extends SavingsManager<PublicKey, Connection, Key
365
374
  const tx = await getTransferNativeTransaction(
366
375
  pocket.privateKey,
367
376
  mainWalletAddress,
368
- Number(amount),
377
+ this.toSafeNumberAmount(amount, 'Native withdrawal amount'),
369
378
  this.client
370
379
  );
371
380
  const hash = await signAndSendTransaction(tx, this.client, pocket.privateKey);
@@ -376,7 +385,7 @@ export class SVMSavingsManager extends SavingsManager<PublicKey, Connection, Key
376
385
  pocket.privateKey,
377
386
  mainWalletAddress,
378
387
  token as any, // TokenInfo type
379
- Number(amount),
388
+ this.toSafeNumberAmount(amount, 'Token withdrawal amount'),
380
389
  this.client
381
390
  );
382
391
 
package/utils/test.ts CHANGED
@@ -399,7 +399,7 @@ const testAddressClass = async () => {
399
399
  const evmAddressClass = new EVMChainAddress(evmChainConfig, "0xC9C1D854b82BA9b4FB6f6D58E9EF3d1fAEd601AA", 0)
400
400
  const res = await evmAddressClass.getNativeBalance()
401
401
  console.log('res: ', res);
402
-
402
+ SVMVM.generateMnemonicFromPrivateKey
403
403
  // const svmAddressClass = new SVMChainAddress( )
404
404
  }
405
405
  testAddressClass()
package/utils/types.ts CHANGED
@@ -25,7 +25,8 @@ export interface ChainWalletConfig {
25
25
  }
26
26
 
27
27
  export interface ChainSavingConfig {
28
- tokens: string[]; // List of token addresses to track for savings
28
+ supported: boolean; // Whether savings tracking is supported on this chain
29
+ tokens: TokenInfo[]; // List of token addresses to track for savings
29
30
 
30
31
  }
31
32