@deserialize/multi-vm-wallet 1.4.2 → 1.4.12
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/dist/IChainWallet.js.map +1 -1
- package/dist/evm/evm.d.ts +16 -1
- package/dist/evm/evm.js +26 -2
- package/dist/evm/evm.js.map +1 -1
- package/dist/savings/index.d.ts +0 -1
- package/dist/savings/index.js +1 -16
- package/dist/savings/index.js.map +1 -1
- package/dist/svm/svm.d.ts +16 -0
- package/dist/svm/svm.js +23 -0
- package/dist/svm/svm.js.map +1 -1
- package/dist/test.js +2 -14
- package/dist/test.js.map +1 -1
- package/dist/vm.d.ts +14 -2
- package/dist/vm.js +37 -3
- package/dist/vm.js.map +1 -1
- package/dist/walletBip32.d.ts +0 -2
- package/dist/walletBip32.js +0 -22
- package/dist/walletBip32.js.map +1 -1
- package/package.json +1 -1
- package/utils/IChainWallet.ts +0 -2
- package/utils/evm/evm.ts +33 -8
- package/utils/savings/index.ts +1 -1
- package/utils/savings/saving-actions.ts +92 -0
- package/utils/savings/savings-manager.ts +271 -0
- package/utils/svm/svm.ts +25 -1
- package/utils/test.ts +3 -26
- package/utils/vm.ts +16 -7
- package/utils/walletBip32.ts +0 -24
- package/dist/savings/saving-manager.d.ts +0 -47
- package/dist/savings/saving-manager.js +0 -119
- package/dist/savings/saving-manager.js.map +0 -1
- package/utils/savings/saving-manager.ts +0 -146
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// import { EVMDeriveChildPrivateKey } from "../walletBip32";
|
|
2
|
+
// import { ethers } from "ethers";
|
|
3
|
+
// import { WalletClient, PublicClient, Hex, Chain } from "viem";
|
|
4
|
+
// import { } from "../utils";
|
|
5
|
+
// import { ChainWalletConfig, TransactionResult } from "../types";
|
|
6
|
+
// import { getNativeBalance, sendERC20Token, sendNativeToken } from "../evm";
|
|
7
|
+
// import { fetchPrices } from "../price";
|
|
8
|
+
// import { privateKeyToAccount } from "viem/accounts";
|
|
9
|
+
|
|
10
|
+
// /**
|
|
11
|
+
// * Handles multi-pocket savings accounts for an EVM wallet
|
|
12
|
+
// */
|
|
13
|
+
// export class BaseSavingsManager {
|
|
14
|
+
// private mnemonic: string;
|
|
15
|
+
// private walletIndex: number;
|
|
16
|
+
// chain: ChainWalletConfig
|
|
17
|
+
// private pockets: Map<number, { privateKey: string; address: string; derivationPath: string }> = new Map();
|
|
18
|
+
|
|
19
|
+
// constructor(mnemonic: string, walletIndex: number = 0, chain: ChainWalletConfig) {
|
|
20
|
+
// this.mnemonic = mnemonic;
|
|
21
|
+
// this.chain = chain
|
|
22
|
+
// this.walletIndex = walletIndex;
|
|
23
|
+
// }
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
// /**
|
|
28
|
+
// * Derive a pocket (savings account) at accountIndex
|
|
29
|
+
// */
|
|
30
|
+
// derivePocket(accountIndex: number) {
|
|
31
|
+
// const derivationPath = `m/44'/60'/${accountIndex}'/0/${this.walletIndex}`;
|
|
32
|
+
// const { privateKey } = EVMDeriveChildPrivateKey(this.mnemonic, this.walletIndex, `m/44'/60'/${accountIndex}'/0/`);
|
|
33
|
+
// const wallet = new ethers.Wallet(privateKey);
|
|
34
|
+
|
|
35
|
+
// const pocket = { privateKey, address: wallet.address, derivationPath };
|
|
36
|
+
// this.pockets.set(accountIndex, pocket);
|
|
37
|
+
// return pocket;
|
|
38
|
+
// }
|
|
39
|
+
|
|
40
|
+
// getPocket(accountIndex: number) {
|
|
41
|
+
// if (!this.pockets.has(accountIndex)) {
|
|
42
|
+
// return this.derivePocket(accountIndex);
|
|
43
|
+
// }
|
|
44
|
+
// return this.pockets.get(accountIndex)!;
|
|
45
|
+
// }
|
|
46
|
+
// async transferToPocket(
|
|
47
|
+
// mainWallet: WalletClient,
|
|
48
|
+
// client: PublicClient,
|
|
49
|
+
// accountIndex: number,
|
|
50
|
+
// amount: string
|
|
51
|
+
// ): Promise<TransactionResult> {
|
|
52
|
+
// const pocket = this.getPocket(accountIndex);
|
|
53
|
+
// return await sendNativeToken(mainWallet, client, pocket.address as Hex, amount, 5);
|
|
54
|
+
// }
|
|
55
|
+
|
|
56
|
+
// async transferTokenToPocket(
|
|
57
|
+
// mainWallet: WalletClient,
|
|
58
|
+
// client: PublicClient,
|
|
59
|
+
// tokenAddress: string,
|
|
60
|
+
// accountIndex: number,
|
|
61
|
+
// amount: bigint
|
|
62
|
+
// ): Promise<TransactionResult> {
|
|
63
|
+
// const pocket = this.getPocket(accountIndex);
|
|
64
|
+
// return await sendERC20Token(mainWallet, client, tokenAddress as Hex, pocket.address as Hex, amount, 5);
|
|
65
|
+
// }
|
|
66
|
+
|
|
67
|
+
// verifyPocketAddress(accountIndex: number, storedAddress: string) {
|
|
68
|
+
// const pocket = this.getPocket(accountIndex);
|
|
69
|
+
// return pocket.address.toLowerCase() === storedAddress.toLowerCase();
|
|
70
|
+
// }
|
|
71
|
+
// }
|
|
72
|
+
|
|
73
|
+
// export class SavingsManager extends BaseSavingsManager {
|
|
74
|
+
|
|
75
|
+
// constructor(mnemonic: string, walletIndex: number = 0, chain: ChainWalletConfig) {
|
|
76
|
+
// super(mnemonic, walletIndex, chain)
|
|
77
|
+
// }
|
|
78
|
+
// getTotalTokenBalanceOfAllPockets(tokens: string[], pockets: number[]) {
|
|
79
|
+
|
|
80
|
+
// const balances = pockets.map((p: number) => { })
|
|
81
|
+
// }
|
|
82
|
+
|
|
83
|
+
// getPocketTokenBalance(tokens: string[], pocket: number) {
|
|
84
|
+
// const account = privateKeyToAccount(`0x${this.derivePocket(pocket).privateKey}`)
|
|
85
|
+
// const balances = tokens.map((t: string) => {
|
|
86
|
+
// const nativeBalance = getNativeBalance(account.address)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
// })
|
|
90
|
+
// }
|
|
91
|
+
|
|
92
|
+
// }
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Savings Manager
|
|
3
|
+
*
|
|
4
|
+
* Core logic for managing BIP-44 derived savings accounts.
|
|
5
|
+
* Provides security-first operations that always derive addresses from account indices.
|
|
6
|
+
*
|
|
7
|
+
* Security Model:
|
|
8
|
+
* - NEVER trust stored addresses without verification
|
|
9
|
+
* - ALWAYS derive addresses from accountIndex for transactions
|
|
10
|
+
* - Provide verification and audit tools for tamper detection
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { Hex } from "viem";
|
|
14
|
+
import { EVMVM } from "../evm/evm";
|
|
15
|
+
import { SVMVM } from "../svm/svm";
|
|
16
|
+
import { VM } from "../vm";
|
|
17
|
+
import {
|
|
18
|
+
SavingsAccount,
|
|
19
|
+
AddressVerificationResult,
|
|
20
|
+
SavingsAuditResult,
|
|
21
|
+
TransferToSavingsOptions,
|
|
22
|
+
WithdrawFromSavingsOptions
|
|
23
|
+
} from "./types";
|
|
24
|
+
|
|
25
|
+
// ============================================
|
|
26
|
+
// SavingsManager Class
|
|
27
|
+
// ============================================
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Manages BIP-44 derived savings accounts
|
|
31
|
+
*
|
|
32
|
+
* This class provides:
|
|
33
|
+
* 1. Account derivation from BIP-44 account indices
|
|
34
|
+
* 2. Address verification to prevent database tampering
|
|
35
|
+
* 3. Transaction builders for deposits and withdrawals
|
|
36
|
+
* 4. Batch auditing of stored addresses
|
|
37
|
+
*/
|
|
38
|
+
export class SavingsManagerOld {
|
|
39
|
+
private vm: VM<any, any, any>;
|
|
40
|
+
|
|
41
|
+
constructor(vm: VM<any, any, any>) {
|
|
42
|
+
this.vm = vm;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Create a new savings account by deriving from account index
|
|
47
|
+
*
|
|
48
|
+
* @param accountIndex - BIP-44 account index (1+ for savings, 0 is main wallet)
|
|
49
|
+
* @returns SavingsAccount with derived private key and address
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* const savingsAccount = manager.createSavingsAccount(1); // First savings account
|
|
53
|
+
* console.log(savingsAccount.address); // Derived address
|
|
54
|
+
* console.log(savingsAccount.derivationPath); // m/44'/60'/1'/0/0
|
|
55
|
+
*/
|
|
56
|
+
createSavingsAccount(accountIndex: number): SavingsAccount {
|
|
57
|
+
if (accountIndex < 0) {
|
|
58
|
+
throw new Error("Account index must be non-negative");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const derived = this.vm.deriveSavingsAccount(accountIndex);
|
|
62
|
+
|
|
63
|
+
// For EVM, the address is a string (Hex)
|
|
64
|
+
// For SVM, the address is a PublicKey
|
|
65
|
+
let addressHex: Hex;
|
|
66
|
+
let privateKeyHex: Hex;
|
|
67
|
+
|
|
68
|
+
if (this.vm instanceof EVMVM) {
|
|
69
|
+
addressHex = derived.address as Hex;
|
|
70
|
+
privateKeyHex = (derived.privateKey as string).startsWith('0x')
|
|
71
|
+
? derived.privateKey as Hex
|
|
72
|
+
: `0x${derived.privateKey}` as Hex;
|
|
73
|
+
} else if (this.vm instanceof SVMVM) {
|
|
74
|
+
// For SVM, convert PublicKey to base58 string
|
|
75
|
+
addressHex = derived.address.toBase58() as Hex;
|
|
76
|
+
// For SVM, convert Keypair secret key to hex
|
|
77
|
+
const keypair = derived.privateKey as any;
|
|
78
|
+
privateKeyHex = `0x${Buffer.from(keypair.secretKey).toString('hex')}` as Hex;
|
|
79
|
+
} else {
|
|
80
|
+
throw new Error("Unsupported VM type");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
accountIndex,
|
|
85
|
+
privateKey: privateKeyHex,
|
|
86
|
+
address: addressHex,
|
|
87
|
+
derivationPath: derived.derivationPath
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Verify a stored address matches the derived address for an account index
|
|
93
|
+
*
|
|
94
|
+
* Security: This prevents database tampering attacks where an attacker
|
|
95
|
+
* modifies stored addresses to redirect funds.
|
|
96
|
+
*
|
|
97
|
+
* @param accountIndex - The account index to verify
|
|
98
|
+
* @param storedAddress - The address from storage/database
|
|
99
|
+
* @returns Verification result with validity flag and details
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* const result = manager.verifySavingsAddress(1, storedAddress);
|
|
103
|
+
* if (!result.isValid) {
|
|
104
|
+
* console.error("Address mismatch! Possible tampering:", result.error);
|
|
105
|
+
* }
|
|
106
|
+
*/
|
|
107
|
+
verifySavingsAddress(accountIndex: number, storedAddress: Hex): AddressVerificationResult {
|
|
108
|
+
try {
|
|
109
|
+
const derived = this.createSavingsAccount(accountIndex);
|
|
110
|
+
|
|
111
|
+
const isValid = derived.address.toLowerCase() === storedAddress.toLowerCase();
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
accountIndex,
|
|
115
|
+
storedAddress,
|
|
116
|
+
derivedAddress: derived.address,
|
|
117
|
+
isValid,
|
|
118
|
+
error: isValid ? undefined : "Address mismatch - stored address does not match derived address"
|
|
119
|
+
};
|
|
120
|
+
} catch (error: any) {
|
|
121
|
+
return {
|
|
122
|
+
accountIndex,
|
|
123
|
+
storedAddress,
|
|
124
|
+
derivedAddress: "0x0" as Hex,
|
|
125
|
+
isValid: false,
|
|
126
|
+
error: `Verification failed: ${error.message}`
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Audit multiple savings addresses at once
|
|
133
|
+
*
|
|
134
|
+
* Useful for batch verification of all stored savings addresses.
|
|
135
|
+
*
|
|
136
|
+
* @param addresses - Map of accountIndex to stored address
|
|
137
|
+
* @returns Audit result with summary and per-address details
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* const addresses = new Map([
|
|
141
|
+
* [1, "0xabc..."],
|
|
142
|
+
* [2, "0xdef..."]
|
|
143
|
+
* ]);
|
|
144
|
+
* const audit = manager.auditSavingsAddresses(addresses);
|
|
145
|
+
* console.log(`Valid: ${audit.valid}/${audit.total}`);
|
|
146
|
+
*/
|
|
147
|
+
auditSavingsAddresses(addresses: Map<number, Hex>): SavingsAuditResult {
|
|
148
|
+
const results: AddressVerificationResult[] = [];
|
|
149
|
+
|
|
150
|
+
for (const [accountIndex, storedAddress] of addresses.entries()) {
|
|
151
|
+
const result = this.verifySavingsAddress(accountIndex, storedAddress);
|
|
152
|
+
results.push(result);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const valid = results.filter(r => r.isValid).length;
|
|
156
|
+
const invalid = results.filter(r => !r.isValid).length;
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
total: results.length,
|
|
160
|
+
valid,
|
|
161
|
+
invalid,
|
|
162
|
+
allValid: invalid === 0,
|
|
163
|
+
results,
|
|
164
|
+
timestamp: new Date()
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Build transaction parameters for depositing to savings
|
|
170
|
+
*
|
|
171
|
+
* Security: Always derives the destination address from accountIndex.
|
|
172
|
+
* Optional expectedAddress parameter allows tamper detection.
|
|
173
|
+
*
|
|
174
|
+
* @param accountIndex - Savings account to deposit to
|
|
175
|
+
* @param amount - Amount to transfer (in base units)
|
|
176
|
+
* @param options - Optional security and priority settings
|
|
177
|
+
* @returns Transaction parameters (to, value, data)
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* const txParams = manager.buildDepositTransaction(1, parseEther("1.0"), {
|
|
181
|
+
* expectedAddress: storedAddress // Verify it matches derived
|
|
182
|
+
* });
|
|
183
|
+
*/
|
|
184
|
+
buildDepositTransaction(
|
|
185
|
+
accountIndex: number,
|
|
186
|
+
amount: bigint,
|
|
187
|
+
options?: TransferToSavingsOptions
|
|
188
|
+
): { to: Hex; value: bigint; data?: Hex } {
|
|
189
|
+
const savingsAccount = this.createSavingsAccount(accountIndex);
|
|
190
|
+
|
|
191
|
+
// Security check: if expectedAddress provided, verify it matches
|
|
192
|
+
if (options?.expectedAddress) {
|
|
193
|
+
const verification = this.verifySavingsAddress(accountIndex, options.expectedAddress);
|
|
194
|
+
if (!verification.isValid) {
|
|
195
|
+
throw new Error(
|
|
196
|
+
`Security check failed: Expected address ${options.expectedAddress} ` +
|
|
197
|
+
`does not match derived address ${savingsAccount.address}. ` +
|
|
198
|
+
`Possible database tampering detected.`
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
to: savingsAccount.address,
|
|
205
|
+
value: amount,
|
|
206
|
+
data: undefined // Simple native transfer
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Build transaction parameters for withdrawing from savings
|
|
212
|
+
*
|
|
213
|
+
* Security: Uses the derived private key for signing.
|
|
214
|
+
* Optional expectedAddress parameter verifies the source address.
|
|
215
|
+
*
|
|
216
|
+
* @param accountIndex - Savings account to withdraw from
|
|
217
|
+
* @param to - Destination address
|
|
218
|
+
* @param amount - Amount to transfer (in base units)
|
|
219
|
+
* @param options - Optional security and priority settings
|
|
220
|
+
* @returns Transaction parameters and private key for signing
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* const { privateKey, tx } = manager.buildWithdrawalTransaction(
|
|
224
|
+
* 1, destinationAddress, parseEther("0.5")
|
|
225
|
+
* );
|
|
226
|
+
* // Use privateKey to sign the transaction
|
|
227
|
+
*/
|
|
228
|
+
buildWithdrawalTransaction(
|
|
229
|
+
accountIndex: number,
|
|
230
|
+
to: Hex,
|
|
231
|
+
amount: bigint,
|
|
232
|
+
options?: WithdrawFromSavingsOptions
|
|
233
|
+
): { privateKey: Hex; from: Hex; to: Hex; value: bigint; data?: Hex } {
|
|
234
|
+
const savingsAccount = this.createSavingsAccount(accountIndex);
|
|
235
|
+
|
|
236
|
+
// Security check: if expectedAddress provided, verify it matches
|
|
237
|
+
if (options?.expectedAddress) {
|
|
238
|
+
const verification = this.verifySavingsAddress(accountIndex, options.expectedAddress);
|
|
239
|
+
if (!verification.isValid) {
|
|
240
|
+
throw new Error(
|
|
241
|
+
`Security check failed: Expected address ${options.expectedAddress} ` +
|
|
242
|
+
`does not match derived address ${savingsAccount.address}. ` +
|
|
243
|
+
`Possible database tampering detected.`
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return {
|
|
249
|
+
privateKey: savingsAccount.privateKey,
|
|
250
|
+
from: savingsAccount.address,
|
|
251
|
+
to,
|
|
252
|
+
value: amount,
|
|
253
|
+
data: undefined // Simple native transfer
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Get savings account details without exposing private key
|
|
259
|
+
*
|
|
260
|
+
* @param accountIndex - The account index
|
|
261
|
+
* @returns Public account information (address and derivation path only)
|
|
262
|
+
*/
|
|
263
|
+
getSavingsAccountInfo(accountIndex: number): Omit<SavingsAccount, 'privateKey'> {
|
|
264
|
+
const account = this.createSavingsAccount(accountIndex);
|
|
265
|
+
return {
|
|
266
|
+
accountIndex: account.accountIndex,
|
|
267
|
+
address: account.address,
|
|
268
|
+
derivationPath: account.derivationPath
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
}
|
package/utils/svm/svm.ts
CHANGED
|
@@ -83,7 +83,31 @@ export class SVMVM extends VM<PublicKey, Keypair, Connection> {
|
|
|
83
83
|
return { privateKey, index };
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
/**
|
|
87
|
+
* Derive a savings account using BIP-44 account index
|
|
88
|
+
*
|
|
89
|
+
* Uses the pattern: m/44'/501'/accountIndex'/0/0
|
|
90
|
+
* - Main wallet: m/44'/501'/0'/0/0 (account index 0)
|
|
91
|
+
* - Savings 1: m/44'/501'/1'/0/0 (account index 1)
|
|
92
|
+
* - Savings 2: m/44'/501'/2'/0/0 (account index 2)
|
|
93
|
+
*
|
|
94
|
+
* @param accountIndex - The BIP-44 account index (0 for main, 1+ for savings)
|
|
95
|
+
* @returns Object containing privateKey (Keypair), address (PublicKey), and derivation path
|
|
96
|
+
*/
|
|
97
|
+
deriveSavingsAccount(accountIndex: number): { privateKey: Keypair; address: PublicKey; derivationPath: string } {
|
|
98
|
+
// Use fixed address index 0 for savings accounts
|
|
99
|
+
// Vary only the account index (3rd position in BIP-44)
|
|
100
|
+
const derivationPath = `m/44'/501'/${accountIndex}'/0/0`;
|
|
101
|
+
|
|
102
|
+
// Derive the keypair using the account index in the path
|
|
103
|
+
const keypair = SVMDeriveChildPrivateKey(this.seed, 0, `m/44'/501'/${accountIndex}'/0/`);
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
privateKey: keypair,
|
|
107
|
+
address: keypair.publicKey,
|
|
108
|
+
derivationPath
|
|
109
|
+
};
|
|
110
|
+
}
|
|
87
111
|
|
|
88
112
|
static fromMnemonic(mnemonic: string): VM<PublicKey, Keypair, Connection> {
|
|
89
113
|
const seed = VM.mnemonicToSeed(mnemonic)
|
package/utils/test.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { Connection, PublicKey, Keypair } from "@solana/web3.js";
|
|
|
8
8
|
import { SVMChainWallet, SVMVM, } from "./svm";
|
|
9
9
|
import { VM } from "./vm";
|
|
10
10
|
import { ChainWalletConfig } from "./types";
|
|
11
|
-
import { discoverNFTs, EVMChainWallet
|
|
11
|
+
import { discoverNFTs, EVMChainWallet } from "./evm";
|
|
12
12
|
import { } from "./svm/transactionParsing";
|
|
13
13
|
import { getEVMTransactionHistory } from "./evm/transactionParsing";
|
|
14
14
|
import { createPublicClient, encodeFunctionData, Hex, http, parseAbi, parseUnits, PublicClient, zeroAddress } from "viem";
|
|
@@ -22,7 +22,6 @@ import { entryPoint07Address } from "viem/account-abstraction";
|
|
|
22
22
|
import { KERNEL_V3_3, KernelVersionToAddressesMap } from '@zerodev/sdk/constants';
|
|
23
23
|
import { deserializeSessionKey } from "./evm/aa-service";
|
|
24
24
|
import { toSpendingLimitHook } from "@zerodev/hooks"
|
|
25
|
-
import { SavingsManager } from "./savings";
|
|
26
25
|
// const mnemonic = GenerateNewMnemonic()
|
|
27
26
|
|
|
28
27
|
|
|
@@ -34,8 +33,8 @@ const pKey = ""
|
|
|
34
33
|
// const testUserKeyPair = Keypair.fromSecretKey(base58.decode(pKey));
|
|
35
34
|
// const x = testUserKeyPair instanceof Keypair;
|
|
36
35
|
|
|
37
|
-
const evmPrivKey = ""
|
|
38
|
-
const evmPrivateKeyExposed = ""
|
|
36
|
+
const evmPrivKey = "0xc9ca95aa5f40bae8b0f741ec89acc07b837590b2f9f818d3439df98c6e4f8dbe"
|
|
37
|
+
const evmPrivateKeyExposed = "0x92cccc7792024dcac5992e5d2986dade41770acfd3dab0fe98ee953ed1bf0c3a"
|
|
39
38
|
// const vm = new SVMVM(seed)
|
|
40
39
|
|
|
41
40
|
|
|
@@ -212,28 +211,6 @@ const testPrice = async () => {
|
|
|
212
211
|
walletA.getPrices(['0x98d0baa52b2D063E780DE12F615f963Fe8537553']).then(console.log)
|
|
213
212
|
}
|
|
214
213
|
|
|
215
|
-
const testSavingsPocket = async () => {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const mnemonic = EVMVM.generateMnemonicFromPrivateKey(evmPrivateKeyExposed)
|
|
219
|
-
console.log('mnemonic: ', mnemonic);
|
|
220
|
-
|
|
221
|
-
const savingsManager = new SavingsManager(mnemonic, evmChainConfig, wallet.index)
|
|
222
|
-
|
|
223
|
-
const pocket0 = savingsManager.getPocket(0)
|
|
224
|
-
|
|
225
|
-
// const deposited = await savingsManager.transferToPocket(walletA.wallet, pocket0.index, "0.00001")
|
|
226
|
-
|
|
227
|
-
const balance = await savingsManager.getPocketTokenBalance([], 0)
|
|
228
|
-
console.log('balance: ', balance);
|
|
229
|
-
|
|
230
|
-
// console.log('deposited: ', deposited);
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// testSavingsPocket()
|
|
236
|
-
|
|
237
214
|
// testPrice()
|
|
238
215
|
|
|
239
216
|
|
package/utils/vm.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { Balance, ChainWalletConfig, TokenInfo, vmTypes } from "./types";
|
|
|
2
2
|
import * as bip39 from "@scure/bip39";
|
|
3
3
|
import CryptoJS from "crypto-js";
|
|
4
4
|
import { PriceResponse } from "./price.types";
|
|
5
|
-
import { EntropyToMnemonic, mnemonicToSeed } from "./walletBip32";
|
|
6
5
|
|
|
7
6
|
// Abstract Base Classes
|
|
8
7
|
export abstract class VM<AddressType, PrivateKeyType, ConnectionType> {
|
|
@@ -13,13 +12,13 @@ export abstract class VM<AddressType, PrivateKeyType, ConnectionType> {
|
|
|
13
12
|
this.type = vm;
|
|
14
13
|
this.seed = seed
|
|
15
14
|
}
|
|
16
|
-
static mnemonicToSeed =
|
|
15
|
+
static mnemonicToSeed = (mnemonic: string) => {
|
|
16
|
+
return Buffer.from(bip39.mnemonicToSeedSync(mnemonic)).toString("hex");
|
|
17
|
+
}
|
|
17
18
|
|
|
18
19
|
static generateSalt(): string {
|
|
19
20
|
return CryptoJS.lib.WordArray.random(16).toString(); // 128-bit salt
|
|
20
21
|
}
|
|
21
|
-
|
|
22
|
-
static getMnemonicFromEntropy = EntropyToMnemonic
|
|
23
22
|
static deriveKey(
|
|
24
23
|
password: string,
|
|
25
24
|
salt: string,
|
|
@@ -66,8 +65,6 @@ export abstract class VM<AddressType, PrivateKeyType, ConnectionType> {
|
|
|
66
65
|
decryptSeedPhrase = VM.decryptSeedPhrase
|
|
67
66
|
|
|
68
67
|
|
|
69
|
-
|
|
70
|
-
|
|
71
68
|
abstract derivationPath: string
|
|
72
69
|
|
|
73
70
|
|
|
@@ -75,5 +72,17 @@ export abstract class VM<AddressType, PrivateKeyType, ConnectionType> {
|
|
|
75
72
|
abstract generatePrivateKey(index: number, mnemonic?: string, derivationPath?: string): { privateKey: PrivateKeyType, index: number };
|
|
76
73
|
abstract getTokenInfo(tokenAddress: AddressType, connection: ConnectionType): Promise<TokenInfo>
|
|
77
74
|
|
|
78
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Derive a savings account using BIP-44 account index
|
|
77
|
+
*
|
|
78
|
+
* Main wallet uses: m/44'/60'/0'/0/0 (account index 0)
|
|
79
|
+
* Savings accounts use: m/44'/60'/N'/0/0 (account index N)
|
|
80
|
+
*
|
|
81
|
+
* @param accountIndex - The BIP-44 account index (1 for first savings, 2 for second, etc.)
|
|
82
|
+
* @returns Object containing privateKey, address, and derivation path
|
|
83
|
+
*/
|
|
84
|
+
abstract deriveSavingsAccount(accountIndex: number): { privateKey: PrivateKeyType; address: AddressType; derivationPath: string };
|
|
85
|
+
|
|
86
|
+
// abstract validateAddress(address: AddressType): boolean;
|
|
87
|
+
// abstract getNativeBalance(address: AddressType, connection: ConnectionType): Promise<Balance>;
|
|
79
88
|
}
|
package/utils/walletBip32.ts
CHANGED
|
@@ -84,30 +84,6 @@ export function GenerateNewMnemonic() {
|
|
|
84
84
|
const mnemonic = bip39.generateMnemonic(wordlist);
|
|
85
85
|
return mnemonic;
|
|
86
86
|
}
|
|
87
|
-
|
|
88
|
-
export const mnemonicToSeed = (mnemonic: string) => {
|
|
89
|
-
return Buffer.from(bip39.mnemonicToSeedSync(mnemonic)).toString("hex");
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export function EntropyToMnemonic(entropy: string) {
|
|
93
|
-
const entropyBytes = Buffer.from(entropy);
|
|
94
|
-
|
|
95
|
-
// Truncate to valid BIP39 entropy length (16, 20, 24, 28, or 32 bytes)
|
|
96
|
-
// Default to 16 bytes (12 words) for shortest valid mnemonic
|
|
97
|
-
const validLength = 16;
|
|
98
|
-
const truncatedBytes = entropyBytes.length >= validLength
|
|
99
|
-
? entropyBytes.slice(0, validLength)
|
|
100
|
-
: entropyBytes; // If shorter, pad with zeros
|
|
101
|
-
|
|
102
|
-
// Pad if necessary
|
|
103
|
-
if (truncatedBytes.length < validLength) {
|
|
104
|
-
const padded = Buffer.alloc(validLength, 0);
|
|
105
|
-
truncatedBytes.copy(padded);
|
|
106
|
-
return bip39.entropyToMnemonic(padded, wordlist);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return bip39.entropyToMnemonic(truncatedBytes, wordlist);
|
|
110
|
-
}
|
|
111
87
|
export function ValidateMnemonic(mnemonic: string) {
|
|
112
88
|
const isValid = bip39.validateMnemonic(mnemonic, wordlist);
|
|
113
89
|
if (!isValid) {
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { WalletClient, PublicClient, Hex } from "viem";
|
|
2
|
-
import { Balance, ChainWalletConfig, TransactionResult } from "../types";
|
|
3
|
-
import { Account } from "viem/accounts";
|
|
4
|
-
/**
|
|
5
|
-
* Handles multi-pocket savings accounts for an EVM wallet
|
|
6
|
-
* @param masterAddress is the address of the wallet that owns this pocket
|
|
7
|
-
* this is for when the mnemonic of this saving manager is created deterministically from the private key
|
|
8
|
-
*
|
|
9
|
-
*/
|
|
10
|
-
export declare class BaseSavingsManager {
|
|
11
|
-
private mnemonic;
|
|
12
|
-
private walletIndex;
|
|
13
|
-
chain: ChainWalletConfig;
|
|
14
|
-
client: PublicClient;
|
|
15
|
-
private pockets;
|
|
16
|
-
masterAddress: Hex | undefined;
|
|
17
|
-
constructor(mnemonic: string, walletIndex: number | undefined, chain: ChainWalletConfig, masterAddress?: Hex);
|
|
18
|
-
/**
|
|
19
|
-
* Derive a pocket (savings account) at accountIndex
|
|
20
|
-
*/
|
|
21
|
-
private derivePocket;
|
|
22
|
-
getMainWallet(): {
|
|
23
|
-
privateKey: string;
|
|
24
|
-
address: string;
|
|
25
|
-
derivationPath: string;
|
|
26
|
-
};
|
|
27
|
-
getMainWalletAddress(): Hex;
|
|
28
|
-
getPocket(accountIndex: number): {
|
|
29
|
-
privateKey: string;
|
|
30
|
-
address: string;
|
|
31
|
-
derivationPath: string;
|
|
32
|
-
index: number;
|
|
33
|
-
};
|
|
34
|
-
transferToPocket(mainWallet: WalletClient, pocketIndex: number, amount: string): Promise<TransactionResult>;
|
|
35
|
-
transferTokenToPocket(mainWallet: WalletClient, tokenAddress: string, pocketIndex: number, amount: bigint): Promise<TransactionResult>;
|
|
36
|
-
verifyPocketAddress(accountIndex: number, storedAddress: string): boolean;
|
|
37
|
-
accountFromPocketId(p: number): Account;
|
|
38
|
-
}
|
|
39
|
-
export declare class SavingsManager extends BaseSavingsManager {
|
|
40
|
-
constructor(mnemonic: string, chain: ChainWalletConfig, walletIndex?: number);
|
|
41
|
-
getTotalTokenBalanceOfAllPockets(tokens: string[], pockets: number[]): void;
|
|
42
|
-
getPocketTokenBalance(tokens: string[], pocket: number): Promise<{
|
|
43
|
-
address: Hex | 'native';
|
|
44
|
-
balance: Balance;
|
|
45
|
-
}[]>;
|
|
46
|
-
sendToMainWallet(pocketIndex: number, amountInEther: number, token: Hex | "native"): Promise<TransactionResult>;
|
|
47
|
-
}
|