@deserialize/multi-vm-wallet 1.3.1 → 1.3.3
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/.claude/settings.local.json +7 -1
- package/dist/IChainWallet.d.ts +3 -3
- package/dist/constant.js +60 -16
- package/dist/constant.js.map +1 -1
- package/dist/evm/aa-service/index.d.ts +2 -2
- package/dist/evm/aa-service/index.js +1 -3
- package/dist/evm/aa-service/index.js.map +1 -1
- package/dist/evm/aa-service/lib/kernel-account.d.ts +2 -44
- package/dist/evm/aa-service/lib/kernel-account.js +42 -93
- package/dist/evm/aa-service/lib/kernel-account.js.map +1 -1
- package/dist/evm/aa-service/lib/session-keys.d.ts +14 -17
- package/dist/evm/aa-service/lib/session-keys.js +40 -58
- package/dist/evm/aa-service/lib/session-keys.js.map +1 -1
- package/dist/evm/aa-service/lib/type.d.ts +18 -1
- package/dist/evm/aa-service/lib/type.js.map +1 -1
- package/dist/evm/aa-service/services/account-abstraction.d.ts +7 -29
- package/dist/evm/aa-service/services/account-abstraction.js +12 -68
- package/dist/evm/aa-service/services/account-abstraction.js.map +1 -1
- package/dist/evm/aa-service/services/bundler.d.ts +4 -1
- package/dist/evm/aa-service/services/bundler.js +41 -7
- package/dist/evm/aa-service/services/bundler.js.map +1 -1
- package/dist/evm/evm.d.ts +164 -14
- package/dist/evm/evm.js +259 -80
- package/dist/evm/evm.js.map +1 -1
- package/dist/evm/smartWallet.d.ts +12 -61
- package/dist/evm/smartWallet.js +143 -184
- package/dist/evm/smartWallet.js.map +1 -1
- package/dist/evm/utils.js +1 -1
- package/dist/evm/utils.js.map +1 -1
- package/dist/helpers/index.d.ts +6 -1
- package/dist/helpers/index.js +116 -0
- package/dist/helpers/index.js.map +1 -1
- package/dist/privacy/artifact-manager.d.ts +117 -0
- package/dist/privacy/artifact-manager.js +251 -0
- package/dist/privacy/artifact-manager.js.map +1 -0
- package/dist/privacy/broadcaster-client.d.ts +166 -0
- package/dist/privacy/broadcaster-client.js +261 -0
- package/dist/privacy/broadcaster-client.js.map +1 -0
- package/dist/privacy/index.d.ts +34 -0
- package/dist/privacy/index.js +56 -0
- package/dist/privacy/index.js.map +1 -0
- package/dist/privacy/network-config.d.ts +57 -0
- package/dist/privacy/network-config.js +118 -0
- package/dist/privacy/network-config.js.map +1 -0
- package/dist/privacy/poi-helper.d.ts +161 -0
- package/dist/privacy/poi-helper.js +249 -0
- package/dist/privacy/poi-helper.js.map +1 -0
- package/dist/privacy/railgun-engine.d.ts +135 -0
- package/dist/privacy/railgun-engine.js +205 -0
- package/dist/privacy/railgun-engine.js.map +1 -0
- package/dist/privacy/railgun-privacy-wallet.d.ts +288 -0
- package/dist/privacy/railgun-privacy-wallet.js +539 -0
- package/dist/privacy/railgun-privacy-wallet.js.map +1 -0
- package/dist/privacy/types.d.ts +229 -0
- package/dist/privacy/types.js +26 -0
- package/dist/privacy/types.js.map +1 -0
- package/dist/savings/savings-manager.d.ts +126 -0
- package/dist/savings/savings-manager.js +234 -0
- package/dist/savings/savings-manager.js.map +1 -0
- package/dist/savings/smart-savings.d.ts +74 -0
- package/dist/savings/smart-savings.js +152 -0
- package/dist/savings/smart-savings.js.map +1 -0
- package/dist/savings/types.d.ts +125 -0
- package/dist/savings/types.js +9 -0
- package/dist/savings/types.js.map +1 -0
- package/dist/svm/svm.d.ts +19 -3
- package/dist/svm/svm.js +29 -17
- package/dist/svm/svm.js.map +1 -1
- package/dist/svm/utils.d.ts +2 -2
- package/dist/svm/utils.js +4 -4
- package/dist/svm/utils.js.map +1 -1
- package/dist/test.js +81 -22
- package/dist/test.js.map +1 -1
- package/dist/types.d.ts +14 -16
- package/dist/types.js.map +1 -1
- package/dist/vm.d.ts +14 -0
- package/dist/vm.js.map +1 -1
- package/package.json +6 -1
- package/utils/IChainWallet.ts +3 -3
- package/utils/constant.ts +63 -16
- package/utils/evm/aa-service/index.ts +1 -3
- package/utils/evm/aa-service/lib/kernel-account.ts +45 -136
- package/utils/evm/aa-service/lib/session-keys.ts +58 -60
- package/utils/evm/aa-service/lib/type.ts +19 -1
- package/utils/evm/aa-service/services/account-abstraction.ts +24 -76
- package/utils/evm/aa-service/services/bundler.ts +54 -11
- package/utils/evm/evm.ts +313 -110
- package/utils/evm/smartWallet.ts +124 -214
- package/utils/evm/utils.ts +2 -1
- package/utils/helpers/index.ts +138 -1
- package/utils/savings/savings-manager.ts +271 -0
- package/utils/savings/smart-savings.ts +184 -0
- package/utils/savings/types.ts +135 -0
- package/utils/svm/svm.ts +34 -25
- package/utils/svm/utils.ts +4 -10
- package/utils/test.ts +112 -24
- package/utils/types.ts +15 -17
- package/utils/vm.ts +10 -0
|
@@ -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 SavingsManager {
|
|
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
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smart Savings Manager
|
|
3
|
+
*
|
|
4
|
+
* Handles upgrading savings accounts to EIP-7702 smart accounts.
|
|
5
|
+
* Enables advanced features like locked savings, spend & save, and periodic savings.
|
|
6
|
+
*
|
|
7
|
+
* Architecture:
|
|
8
|
+
* 1. Take a basic SavingsAccount (BIP-44 derived)
|
|
9
|
+
* 2. Create an EVMSmartWallet instance from its private key
|
|
10
|
+
* 3. Initialize with EIP-7702 delegation
|
|
11
|
+
* 4. Return a SmartSavingsAccount with full AA capabilities
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { Chain } from "viem/chains";
|
|
15
|
+
import { EVMSmartWallet } from "../evm/smartWallet";
|
|
16
|
+
import { SmartWalletOptions } from "../evm/aa-service";
|
|
17
|
+
import { ChainWalletConfig } from "../types";
|
|
18
|
+
import { SavingsAccount, SmartSavingsAccount } from "./types";
|
|
19
|
+
|
|
20
|
+
// ============================================
|
|
21
|
+
// SmartSavingsManager Class
|
|
22
|
+
// ============================================
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Manages EIP-7702 smart savings accounts
|
|
26
|
+
*
|
|
27
|
+
* This class provides:
|
|
28
|
+
* 1. Upgrade from basic savings account to smart account
|
|
29
|
+
* 2. Full Account Abstraction capabilities
|
|
30
|
+
* 3. Access to Kernel modules (lock, hooks, session keys)
|
|
31
|
+
* 4. Sponsored transactions via paymaster
|
|
32
|
+
*/
|
|
33
|
+
export class SmartSavingsManager {
|
|
34
|
+
private chainConfig: ChainWalletConfig;
|
|
35
|
+
|
|
36
|
+
constructor(chainConfig: ChainWalletConfig) {
|
|
37
|
+
this.chainConfig = chainConfig;
|
|
38
|
+
|
|
39
|
+
// Validate AA support
|
|
40
|
+
if (!chainConfig.aaSupport?.enabled) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
"Smart savings requires Account Abstraction (AA) support.\n" +
|
|
43
|
+
"Your chain config must include aaSupport configuration with:\n" +
|
|
44
|
+
"- bundlerUrl\n" +
|
|
45
|
+
"- entryPoints\n" +
|
|
46
|
+
"- kernelImplementations"
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Upgrade a savings account to a smart account with EIP-7702 delegation
|
|
53
|
+
*
|
|
54
|
+
* This enables advanced features:
|
|
55
|
+
* - Lock modules for time-locked savings
|
|
56
|
+
* - Hooks for spend & save
|
|
57
|
+
* - Session keys for periodic savings
|
|
58
|
+
* - Sponsored transactions via paymaster
|
|
59
|
+
*
|
|
60
|
+
* @param savingsAccount - The basic savings account to upgrade
|
|
61
|
+
* @param options - Optional smart wallet configuration
|
|
62
|
+
* @returns SmartSavingsAccount with EVMSmartWallet instance
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* const basicSavings = wallet.deriveSavingsAccount(1);
|
|
66
|
+
* const smartSavings = await smartSavingsManager.upgradeSavingsToSmartAccount(basicSavings);
|
|
67
|
+
* await smartSavings.smartWallet.installModule({ ... });
|
|
68
|
+
*/
|
|
69
|
+
async upgradeSavingsToSmartAccount(
|
|
70
|
+
savingsAccount: SavingsAccount,
|
|
71
|
+
options?: SmartWalletOptions
|
|
72
|
+
): Promise<SmartSavingsAccount> {
|
|
73
|
+
// Merge options with chain config
|
|
74
|
+
const mergedOptions: SmartWalletOptions = {
|
|
75
|
+
...options,
|
|
76
|
+
aaConfig: this.chainConfig.aaSupport,
|
|
77
|
+
bundlerUrl: options?.bundlerUrl || this.chainConfig.aaSupport?.bundlerUrl,
|
|
78
|
+
paymasterUrl: options?.paymasterUrl || this.chainConfig.aaSupport?.paymasterUrl,
|
|
79
|
+
entryPointVersion: options?.entryPointVersion || this.chainConfig.aaSupport?.entryPoints?.[0]?.version
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// Validate bundlerUrl
|
|
83
|
+
if (!mergedOptions.bundlerUrl) {
|
|
84
|
+
throw new Error(
|
|
85
|
+
"bundlerUrl is required for smart savings.\n" +
|
|
86
|
+
"Provide it via upgradeSavingsToSmartAccount options or chain config."
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Create viem chain object
|
|
91
|
+
const chain: Chain = {
|
|
92
|
+
id: this.chainConfig.chainId,
|
|
93
|
+
name: this.chainConfig.name,
|
|
94
|
+
nativeCurrency: {
|
|
95
|
+
name: this.chainConfig.nativeToken.name,
|
|
96
|
+
symbol: this.chainConfig.nativeToken.symbol,
|
|
97
|
+
decimals: this.chainConfig.nativeToken.decimals
|
|
98
|
+
},
|
|
99
|
+
rpcUrls: {
|
|
100
|
+
default: {
|
|
101
|
+
http: [this.chainConfig.rpcUrl]
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
blockExplorers: {
|
|
105
|
+
default: {
|
|
106
|
+
name: this.chainConfig.name + " Explorer",
|
|
107
|
+
url: this.chainConfig.explorerUrl
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
testnet: this.chainConfig.testnet || false
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// Create EVMSmartWallet instance from savings account private key
|
|
114
|
+
const smartWallet = new EVMSmartWallet(
|
|
115
|
+
savingsAccount.privateKey,
|
|
116
|
+
chain,
|
|
117
|
+
mergedOptions
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
// Note: Initialization (createAuthorization) is optional
|
|
121
|
+
// User can call smartWallet.initialize() separately if needed
|
|
122
|
+
// For now, we return uninitialized smart wallet
|
|
123
|
+
const isInitialized = false;
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
...savingsAccount,
|
|
127
|
+
smartWallet,
|
|
128
|
+
isInitialized
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Upgrade and initialize a savings account in one step
|
|
134
|
+
*
|
|
135
|
+
* This is a convenience method that both upgrades and initializes.
|
|
136
|
+
*
|
|
137
|
+
* @param savingsAccount - The basic savings account to upgrade
|
|
138
|
+
* @param options - Optional smart wallet configuration
|
|
139
|
+
* @returns Initialized SmartSavingsAccount ready for transactions
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* const basicSavings = wallet.deriveSavingsAccount(1);
|
|
143
|
+
* const smartSavings = await smartSavingsManager.upgradeSavingsAndInitialize(basicSavings);
|
|
144
|
+
* // smartSavings is now ready for smart transactions
|
|
145
|
+
*/
|
|
146
|
+
async upgradeSavingsAndInitialize(
|
|
147
|
+
savingsAccount: SavingsAccount,
|
|
148
|
+
options?: SmartWalletOptions
|
|
149
|
+
): Promise<SmartSavingsAccount> {
|
|
150
|
+
// First upgrade
|
|
151
|
+
const smartSavings = await this.upgradeSavingsToSmartAccount(savingsAccount, options);
|
|
152
|
+
|
|
153
|
+
// Then initialize
|
|
154
|
+
await smartSavings.smartWallet.initialize();
|
|
155
|
+
|
|
156
|
+
// Update initialization status
|
|
157
|
+
smartSavings.isInitialized = true;
|
|
158
|
+
|
|
159
|
+
return smartSavings;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Check if a savings account can be upgraded to smart account
|
|
164
|
+
*
|
|
165
|
+
* @returns true if chain supports AA and has required configuration
|
|
166
|
+
*/
|
|
167
|
+
canUpgradeToSmartAccount(): boolean {
|
|
168
|
+
return !!(
|
|
169
|
+
this.chainConfig.aaSupport?.enabled &&
|
|
170
|
+
this.chainConfig.aaSupport?.bundlerUrl &&
|
|
171
|
+
this.chainConfig.aaSupport?.entryPoints &&
|
|
172
|
+
this.chainConfig.aaSupport?.entryPoints.length > 0
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Get the chain configuration
|
|
178
|
+
*
|
|
179
|
+
* @returns ChainWalletConfig
|
|
180
|
+
*/
|
|
181
|
+
getChainConfig(): ChainWalletConfig {
|
|
182
|
+
return this.chainConfig;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Savings Pocket Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for the savings pocket feature that allows users to create
|
|
5
|
+
* multiple savings accounts derived from their seed phrase using BIP-44 account indices.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Hex } from "viem";
|
|
9
|
+
import { EVMSmartWallet } from "../evm/smartWallet";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Savings account derived from BIP-44 account index
|
|
13
|
+
*/
|
|
14
|
+
export interface SavingsAccount {
|
|
15
|
+
/** Account index used for derivation (m/44'/60'/accountIndex'/0/0) */
|
|
16
|
+
accountIndex: number;
|
|
17
|
+
/** Derived private key for this savings account */
|
|
18
|
+
privateKey: Hex;
|
|
19
|
+
/** Derived address for this savings account */
|
|
20
|
+
address: Hex;
|
|
21
|
+
/** Full BIP-44 derivation path */
|
|
22
|
+
derivationPath: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Savings account upgraded to EIP-7702 smart account
|
|
27
|
+
*/
|
|
28
|
+
export interface SmartSavingsAccount extends SavingsAccount {
|
|
29
|
+
/** Smart wallet instance for advanced features */
|
|
30
|
+
smartWallet: EVMSmartWallet;
|
|
31
|
+
/** Whether the account has been initialized on-chain */
|
|
32
|
+
isInitialized: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Options for transferring to savings
|
|
37
|
+
*/
|
|
38
|
+
export interface TransferToSavingsOptions {
|
|
39
|
+
/** Expected address for verification (prevents tampering) */
|
|
40
|
+
expectedAddress?: Hex;
|
|
41
|
+
/** Transaction priority for gas estimation */
|
|
42
|
+
priority?: 'low' | 'medium' | 'high';
|
|
43
|
+
/** Whether to use smart account features (requires 7702 upgrade) */
|
|
44
|
+
useSmartAccount?: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Options for withdrawing from savings
|
|
49
|
+
*/
|
|
50
|
+
export interface WithdrawFromSavingsOptions {
|
|
51
|
+
/** Expected source address for verification */
|
|
52
|
+
expectedAddress?: Hex;
|
|
53
|
+
/** Transaction priority */
|
|
54
|
+
priority?: 'low' | 'medium' | 'high';
|
|
55
|
+
/** Whether to use smart account features */
|
|
56
|
+
useSmartAccount?: boolean;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Result of address verification
|
|
61
|
+
*/
|
|
62
|
+
export interface AddressVerificationResult {
|
|
63
|
+
/** Account index that was verified */
|
|
64
|
+
accountIndex: number;
|
|
65
|
+
/** Stored address that was checked */
|
|
66
|
+
storedAddress: Hex;
|
|
67
|
+
/** Derived address from account index */
|
|
68
|
+
derivedAddress: Hex;
|
|
69
|
+
/** Whether addresses match */
|
|
70
|
+
isValid: boolean;
|
|
71
|
+
/** Error message if invalid */
|
|
72
|
+
error?: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Result of batch address audit
|
|
77
|
+
*/
|
|
78
|
+
export interface SavingsAuditResult {
|
|
79
|
+
/** Total number of addresses audited */
|
|
80
|
+
total: number;
|
|
81
|
+
/** Number of valid addresses */
|
|
82
|
+
valid: number;
|
|
83
|
+
/** Number of invalid addresses */
|
|
84
|
+
invalid: number;
|
|
85
|
+
/** Whether all addresses are valid */
|
|
86
|
+
allValid: boolean;
|
|
87
|
+
/** Detailed results per address */
|
|
88
|
+
results: AddressVerificationResult[];
|
|
89
|
+
/** Timestamp of audit */
|
|
90
|
+
timestamp: Date;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Lock module configuration for locked savings
|
|
95
|
+
*/
|
|
96
|
+
export interface LockModuleConfig {
|
|
97
|
+
/** Lock duration in seconds */
|
|
98
|
+
lockDuration: number;
|
|
99
|
+
/** Lock end timestamp (computed) */
|
|
100
|
+
lockedUntil: number;
|
|
101
|
+
/** Whether early withdrawal is allowed with penalty */
|
|
102
|
+
allowEarlyWithdrawal?: boolean;
|
|
103
|
+
/** Penalty percentage for early withdrawal (0-100) */
|
|
104
|
+
earlyWithdrawalPenalty?: number;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Periodic savings session configuration
|
|
109
|
+
*/
|
|
110
|
+
export interface PeriodicSavingsConfig {
|
|
111
|
+
/** Amount to save each period */
|
|
112
|
+
amount: number;
|
|
113
|
+
/** Savings frequency */
|
|
114
|
+
frequency: 'daily' | 'weekly' | 'monthly';
|
|
115
|
+
/** Destination account index */
|
|
116
|
+
savingsAccountIndex: number;
|
|
117
|
+
/** Optional end date for recurring savings */
|
|
118
|
+
endDate?: Date;
|
|
119
|
+
/** Maximum number of executions */
|
|
120
|
+
maxExecutions?: number;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Spend and save hook configuration
|
|
125
|
+
*/
|
|
126
|
+
export interface SpendAndSaveConfig {
|
|
127
|
+
/** Percentage of spending to save (0-100) */
|
|
128
|
+
savePercentage: number;
|
|
129
|
+
/** Destination savings account index */
|
|
130
|
+
savingsAccountIndex: number;
|
|
131
|
+
/** Minimum transaction amount to trigger saving */
|
|
132
|
+
minTransactionAmount?: number;
|
|
133
|
+
/** Maximum amount to save per transaction */
|
|
134
|
+
maxSaveAmount?: number;
|
|
135
|
+
}
|
package/utils/svm/svm.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { Connection, Keypair, PublicKey, Transaction, VersionedTransaction } fro
|
|
|
2
2
|
import { SVMDeriveChildPrivateKey } from "../walletBip32";
|
|
3
3
|
import { VM } from "../vm";
|
|
4
4
|
import { ChainWallet } from "../IChainWallet";
|
|
5
|
-
import { Balance, ChainWalletConfig, UserTokenBalance, TokenInfo, TransactionResult, NFT
|
|
5
|
+
import { Balance, ChainWalletConfig, UserTokenBalance, TokenInfo, TransactionResult, NFT } from "../types";
|
|
6
6
|
import {
|
|
7
7
|
getSvmNativeBalance,
|
|
8
8
|
getTokenBalance,
|
|
@@ -76,6 +76,33 @@ export class SVMVM extends VM<PublicKey, Keypair, Connection> {
|
|
|
76
76
|
const privateKey = SVMDeriveChildPrivateKey(_seed, index, derivationPath);
|
|
77
77
|
return { privateKey, index };
|
|
78
78
|
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Derive a savings account using BIP-44 account index
|
|
82
|
+
*
|
|
83
|
+
* Uses the pattern: m/44'/501'/accountIndex'/0/0
|
|
84
|
+
* - Main wallet: m/44'/501'/0'/0/0 (account index 0)
|
|
85
|
+
* - Savings 1: m/44'/501'/1'/0/0 (account index 1)
|
|
86
|
+
* - Savings 2: m/44'/501'/2'/0/0 (account index 2)
|
|
87
|
+
*
|
|
88
|
+
* @param accountIndex - The BIP-44 account index (0 for main, 1+ for savings)
|
|
89
|
+
* @returns Object containing privateKey (Keypair), address (PublicKey), and derivation path
|
|
90
|
+
*/
|
|
91
|
+
deriveSavingsAccount(accountIndex: number): { privateKey: Keypair; address: PublicKey; derivationPath: string } {
|
|
92
|
+
// Use fixed address index 0 for savings accounts
|
|
93
|
+
// Vary only the account index (3rd position in BIP-44)
|
|
94
|
+
const derivationPath = `m/44'/501'/${accountIndex}'/0/0`;
|
|
95
|
+
|
|
96
|
+
// Derive the keypair using the account index in the path
|
|
97
|
+
const keypair = SVMDeriveChildPrivateKey(this.seed, 0, `m/44'/501'/${accountIndex}'/0/`);
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
privateKey: keypair,
|
|
101
|
+
address: keypair.publicKey,
|
|
102
|
+
derivationPath
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
79
106
|
static fromMnemonic(mnemonic: string): VM<PublicKey, Keypair, Connection> {
|
|
80
107
|
const seed = VM.mnemonicToSeed(mnemonic)
|
|
81
108
|
return new SVMVM(seed)
|
|
@@ -117,56 +144,38 @@ export class SVMChainWallet extends ChainWallet<PublicKey, Keypair, Connection>
|
|
|
117
144
|
return nfts
|
|
118
145
|
}
|
|
119
146
|
|
|
120
|
-
async transferNative(to: PublicKey, amount: number
|
|
147
|
+
async transferNative(to: PublicKey, amount: number): Promise<TransactionResult> {
|
|
121
148
|
// Implement native transfer logic here
|
|
122
|
-
let feePayerKeypair: Keypair | undefined;
|
|
123
|
-
|
|
124
|
-
if (feePayerOptions) {
|
|
125
|
-
// Convert feePayer private key to Keypair
|
|
126
|
-
feePayerKeypair = Keypair.fromSecretKey(base58.decode(feePayerOptions.privateKey));
|
|
127
|
-
}
|
|
128
|
-
|
|
129
149
|
const transaction = await getTransferNativeTransaction(
|
|
130
150
|
this.privateKey,
|
|
131
151
|
to,
|
|
132
152
|
amount,
|
|
133
|
-
this.connection
|
|
134
|
-
feePayerKeypair
|
|
153
|
+
this.connection!
|
|
135
154
|
);
|
|
136
155
|
|
|
137
156
|
const hash = await SVMVM.signAndSendTransaction(
|
|
138
157
|
transaction,
|
|
139
158
|
this.connection!,
|
|
140
|
-
this.privateKey
|
|
141
|
-
feePayerKeypair ? { feePayerSigner: feePayerKeypair } : undefined
|
|
159
|
+
this.privateKey
|
|
142
160
|
);
|
|
143
161
|
|
|
144
162
|
return { success: true, hash };
|
|
145
163
|
}
|
|
146
164
|
|
|
147
|
-
async transferToken(token: TokenInfo, to: PublicKey, amount: number
|
|
165
|
+
async transferToken(token: TokenInfo, to: PublicKey, amount: number): Promise<TransactionResult> {
|
|
148
166
|
// Implement token transfer logic here
|
|
149
|
-
let feePayerKeypair: Keypair | undefined;
|
|
150
|
-
|
|
151
|
-
if (feePayerOptions) {
|
|
152
|
-
// Convert feePayer private key to Keypair
|
|
153
|
-
feePayerKeypair = Keypair.fromSecretKey(base58.decode(feePayerOptions.privateKey));
|
|
154
|
-
}
|
|
155
|
-
|
|
156
167
|
const transaction = await getTransferTokenTransaction(
|
|
157
168
|
this.privateKey,
|
|
158
169
|
new PublicKey(to),
|
|
159
170
|
token,
|
|
160
171
|
amount,
|
|
161
|
-
this.connection
|
|
162
|
-
feePayerKeypair
|
|
172
|
+
this.connection!
|
|
163
173
|
);
|
|
164
174
|
|
|
165
175
|
const hash = await SVMVM.signAndSendTransaction(
|
|
166
176
|
transaction,
|
|
167
177
|
this.connection!,
|
|
168
|
-
this.privateKey
|
|
169
|
-
feePayerKeypair ? { feePayerSigner: feePayerKeypair } : undefined
|
|
178
|
+
this.privateKey
|
|
170
179
|
);
|
|
171
180
|
|
|
172
181
|
return { success: true, hash };
|