@deserialize/multi-vm-wallet 1.2.441 → 1.3.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/.claude/settings.local.json +12 -0
- package/SMART_WALLET_GUIDE.md +746 -0
- package/SMART_WALLET_IMPLEMENTATION.md +460 -0
- package/dist/IChainWallet.d.ts +3 -3
- package/dist/IChainWallet.js +5 -0
- package/dist/IChainWallet.js.map +1 -1
- package/dist/evm/SMART_WALLET_EXAMPLES.d.ts +20 -0
- package/dist/evm/SMART_WALLET_EXAMPLES.js +451 -0
- package/dist/evm/SMART_WALLET_EXAMPLES.js.map +1 -0
- package/dist/evm/aa-service/index.d.ts +16 -0
- package/dist/evm/aa-service/index.js +69 -0
- package/dist/evm/aa-service/index.js.map +1 -0
- package/dist/evm/aa-service/lib/account-adapter.d.ts +26 -0
- package/dist/evm/aa-service/lib/account-adapter.js +53 -0
- package/dist/evm/aa-service/lib/account-adapter.js.map +1 -0
- package/dist/evm/aa-service/lib/kernel-account.d.ts +91 -0
- package/dist/evm/aa-service/lib/kernel-account.js +251 -0
- package/dist/evm/aa-service/lib/kernel-account.js.map +1 -0
- package/dist/evm/aa-service/lib/kernel-modules.d.ts +240 -0
- package/dist/evm/aa-service/lib/kernel-modules.js +409 -0
- package/dist/evm/aa-service/lib/kernel-modules.js.map +1 -0
- package/dist/evm/aa-service/lib/session-keys.d.ts +170 -0
- package/dist/evm/aa-service/lib/session-keys.js +297 -0
- package/dist/evm/aa-service/lib/session-keys.js.map +1 -0
- package/dist/evm/aa-service/lib/type.d.ts +167 -0
- package/dist/evm/aa-service/lib/type.js +43 -0
- package/dist/evm/aa-service/lib/type.js.map +1 -0
- package/dist/evm/aa-service/services/account-abstraction.d.ts +614 -0
- package/dist/evm/aa-service/services/account-abstraction.js +754 -0
- package/dist/evm/aa-service/services/account-abstraction.js.map +1 -0
- package/dist/evm/aa-service/services/bundler.d.ts +29 -0
- package/dist/evm/aa-service/services/bundler.js +168 -0
- package/dist/evm/aa-service/services/bundler.js.map +1 -0
- package/dist/evm/evm.d.ts +67 -3
- package/dist/evm/evm.js +212 -7
- package/dist/evm/evm.js.map +1 -1
- package/dist/evm/index.d.ts +1 -0
- package/dist/evm/index.js +3 -0
- package/dist/evm/index.js.map +1 -1
- package/dist/evm/smartWallet.d.ts +265 -0
- package/dist/evm/smartWallet.js +675 -0
- package/dist/evm/smartWallet.js.map +1 -0
- package/dist/evm/smartWallet.types.d.ts +10 -0
- package/dist/evm/smartWallet.types.js +16 -0
- package/dist/evm/smartWallet.types.js.map +1 -0
- package/dist/evm/transaction.utils.d.ts +10 -10
- package/dist/evm/transaction.utils.js +12 -8
- package/dist/evm/transaction.utils.js.map +1 -1
- package/dist/evm/transactionParsing.js +77 -1
- package/dist/evm/transactionParsing.js.map +1 -1
- package/dist/helpers/index.d.ts +1 -0
- package/dist/helpers/index.js +15 -0
- package/dist/helpers/index.js.map +1 -1
- package/dist/helpers/routeScan.d.ts +191 -0
- package/dist/helpers/routeScan.js +114 -0
- package/dist/helpers/routeScan.js.map +1 -0
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -2
- package/dist/index.js.map +1 -1
- package/dist/svm/svm.d.ts +4 -3
- package/dist/svm/svm.js +29 -18
- package/dist/svm/svm.js.map +1 -1
- package/dist/svm/transactionSender.js +2 -2
- package/dist/svm/transactionSender.js.map +1 -1
- package/dist/svm/utils.d.ts +4 -3
- package/dist/svm/utils.js +19 -6
- package/dist/svm/utils.js.map +1 -1
- package/dist/test.js +7 -0
- package/dist/test.js.map +1 -1
- package/dist/types.d.ts +19 -2
- package/dist/types.js.map +1 -1
- package/dist/vm.js +9 -7
- package/dist/vm.js.map +1 -1
- package/package.json +2 -2
- package/tsconfig.json +4 -3
- package/utils/IChainWallet.ts +3 -3
- package/utils/evm/SMART_WALLET_EXAMPLES.ts.bak +591 -0
- package/utils/evm/aa-service/index.ts +85 -0
- package/utils/evm/aa-service/lib/account-adapter.ts +60 -0
- package/utils/evm/aa-service/lib/kernel-account.ts +367 -0
- package/utils/evm/aa-service/lib/kernel-modules.ts +598 -0
- package/utils/evm/aa-service/lib/session-keys.ts +389 -0
- package/utils/evm/aa-service/lib/type.ts +236 -0
- package/utils/evm/aa-service/services/account-abstraction.ts +1015 -0
- package/utils/evm/aa-service/services/bundler.ts +217 -0
- package/utils/evm/evm.ts +268 -11
- package/utils/evm/index.ts +5 -1
- package/utils/evm/smartWallet.ts +797 -0
- package/utils/evm/smartWallet.types.ts +33 -0
- package/utils/evm/transaction.utils.ts +12 -10
- package/utils/evm/transactionParsing.ts +100 -14
- package/utils/helpers/index.ts +1 -0
- package/utils/helpers/routeScan.ts +397 -0
- package/utils/index.ts +0 -2
- package/utils/svm/svm.ts +50 -9
- package/utils/svm/utils.ts +52 -7
- package/utils/test.ts +7 -0
- package/utils/types.ts +24 -2
|
@@ -0,0 +1,797 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EVM Smart Wallet Implementation
|
|
3
|
+
*
|
|
4
|
+
* Provides Account Abstraction (EIP-4337) and EIP-7702 capabilities to EVMChainWallet.
|
|
5
|
+
* This class wraps the AA service and provides a clean API for smart wallet features.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Chain, Hex, parseEther, parseUnits, createPublicClient, http, PublicClient } from "viem";
|
|
9
|
+
import { privateKeyToAccount, PrivateKeyAccount } from "viem/accounts";
|
|
10
|
+
import { ModularSigner } from "@zerodev/permissions";
|
|
11
|
+
import { Balance, TransactionResult } from "../types";
|
|
12
|
+
import {
|
|
13
|
+
SmartWalletOptions,
|
|
14
|
+
SmartWalletTransactionResult,
|
|
15
|
+
Call,
|
|
16
|
+
SessionKeyInfo,
|
|
17
|
+
SessionKeyPermissionRule,
|
|
18
|
+
SessionKeyApprovalOptions,
|
|
19
|
+
SessionKeyUsageOptions,
|
|
20
|
+
ModuleInstallOptions,
|
|
21
|
+
ModuleUninstallOptions,
|
|
22
|
+
MultiSigConfig,
|
|
23
|
+
RecoveryConfig,
|
|
24
|
+
PaymasterConfig,
|
|
25
|
+
SmartAccountInfo,
|
|
26
|
+
SmartWalletError,
|
|
27
|
+
SessionKeyError,
|
|
28
|
+
ModuleError,
|
|
29
|
+
TransactionError,
|
|
30
|
+
ModuleType
|
|
31
|
+
} from "./smartWallet.types";
|
|
32
|
+
|
|
33
|
+
// Import AA service from local aa-service
|
|
34
|
+
import { AccountAbstractionService } from "./aa-service/services/account-abstraction";
|
|
35
|
+
import {
|
|
36
|
+
createKernelAuthorization,
|
|
37
|
+
sendSponsoredBatchTransaction
|
|
38
|
+
} from "./aa-service/lib/kernel-account";
|
|
39
|
+
import type {
|
|
40
|
+
KernelAccountInstance,
|
|
41
|
+
Call as AACall
|
|
42
|
+
} from "./aa-service/lib/kernel-account";
|
|
43
|
+
import type { SessionKeyPermission } from "./aa-service/lib/kernel-modules";
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* EVMSmartWallet - Smart wallet capabilities for EVM chains
|
|
47
|
+
*
|
|
48
|
+
* Provides:
|
|
49
|
+
* - EIP-7702 account delegation
|
|
50
|
+
* - Batch transactions (pay gas once for multiple operations)
|
|
51
|
+
* - Session keys with granular permissions
|
|
52
|
+
* - Module management (validators, hooks, executors)
|
|
53
|
+
* - Gas sponsorship (paymasters)
|
|
54
|
+
* - Multi-signature support
|
|
55
|
+
* - Account recovery
|
|
56
|
+
*/
|
|
57
|
+
export class EVMSmartWallet {
|
|
58
|
+
private aaService: AccountAbstractionService | null = null;
|
|
59
|
+
private kernelAccount: KernelAccountInstance | null = null;
|
|
60
|
+
private ownerAccount: PrivateKeyAccount;
|
|
61
|
+
private chain: Chain;
|
|
62
|
+
private options: SmartWalletOptions;
|
|
63
|
+
private sessionAccount: any = null;
|
|
64
|
+
private paymasterConfig: PaymasterConfig | null = null;
|
|
65
|
+
private bundlerUrl: string;
|
|
66
|
+
|
|
67
|
+
constructor(
|
|
68
|
+
privateKey: string,
|
|
69
|
+
chain: Chain,
|
|
70
|
+
bundlerUrl: string,
|
|
71
|
+
options: SmartWalletOptions = {}
|
|
72
|
+
) {
|
|
73
|
+
this.ownerAccount = privateKeyToAccount(privateKey as Hex);
|
|
74
|
+
this.chain = chain;
|
|
75
|
+
this.bundlerUrl = bundlerUrl;
|
|
76
|
+
this.options = {
|
|
77
|
+
entryPointVersion: '0.7',
|
|
78
|
+
autoInitialize: true,
|
|
79
|
+
...options
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// Set paymaster if provided
|
|
83
|
+
if (options.paymasterUrl) {
|
|
84
|
+
this.paymasterConfig = {
|
|
85
|
+
paymasterUrl: options.paymasterUrl
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ============================================
|
|
91
|
+
// Core Methods
|
|
92
|
+
// ============================================
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Initialize the smart wallet
|
|
96
|
+
* Creates the Kernel account and sets up delegation if needed
|
|
97
|
+
*/
|
|
98
|
+
async initialize(): Promise<void> {
|
|
99
|
+
try {
|
|
100
|
+
// Initialize AA service singleton with custom bundler URL
|
|
101
|
+
this.aaService = AccountAbstractionService.getInstance({
|
|
102
|
+
bundlerProvider: 'custom',
|
|
103
|
+
customBundlerUrl: this.bundlerUrl
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// Create Kernel account
|
|
107
|
+
this.kernelAccount = await this.aaService.createAccount({
|
|
108
|
+
chain: this.chain,
|
|
109
|
+
owner: this.ownerAccount,
|
|
110
|
+
entryPointVersion: this.options.entryPointVersion
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
console.log(`Smart wallet initialized: ${this.kernelAccount.address}`);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
throw new SmartWalletError(
|
|
116
|
+
`Failed to initialize smart wallet: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
117
|
+
'INIT_ERROR'
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Get the smart account address
|
|
124
|
+
*/
|
|
125
|
+
getAddress(): Hex {
|
|
126
|
+
if (!this.kernelAccount) {
|
|
127
|
+
throw new SmartWalletError('Smart wallet not initialized', 'NOT_INITIALIZED');
|
|
128
|
+
}
|
|
129
|
+
return this.kernelAccount.address as Hex;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Get smart account information
|
|
134
|
+
*/
|
|
135
|
+
async getAccountInfo(): Promise<SmartAccountInfo> {
|
|
136
|
+
if (!this.kernelAccount || !this.aaService) {
|
|
137
|
+
throw new SmartWalletError('Smart wallet not initialized', 'NOT_INITIALIZED');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const balance = await this.getBalance();
|
|
141
|
+
const isDelegated = await this.aaService.isAccountDelegated(
|
|
142
|
+
this.ownerAccount,
|
|
143
|
+
this.chain
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
address: this.kernelAccount.address as Hex,
|
|
148
|
+
ownerAddress: this.ownerAccount.address as Hex,
|
|
149
|
+
chain: this.chain,
|
|
150
|
+
entryPointVersion: this.options.entryPointVersion!,
|
|
151
|
+
isDelegated,
|
|
152
|
+
balance: BigInt(balance.balance.toString())
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Get smart account balance
|
|
158
|
+
*/
|
|
159
|
+
async getBalance(): Promise<Balance> {
|
|
160
|
+
if (!this.kernelAccount || !this.aaService) {
|
|
161
|
+
throw new SmartWalletError('Smart wallet not initialized', 'NOT_INITIALIZED');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const balance = await this.aaService.getBalance(this.kernelAccount);
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
balance: balance as any,
|
|
168
|
+
decimal: 18,
|
|
169
|
+
formatted: Number(balance) / 1e18
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Check if account is delegated
|
|
175
|
+
*/
|
|
176
|
+
async isAccountDelegated(): Promise<boolean> {
|
|
177
|
+
if (!this.aaService) {
|
|
178
|
+
throw new SmartWalletError('Smart wallet not initialized', 'NOT_INITIALIZED');
|
|
179
|
+
}
|
|
180
|
+
return await this.aaService.isAccountDelegated(this.ownerAccount, this.chain);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ============================================
|
|
184
|
+
// Transaction Methods
|
|
185
|
+
// ============================================
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Send a single transaction via UserOperation
|
|
189
|
+
*
|
|
190
|
+
* @param to - Recipient address
|
|
191
|
+
* @param value - ETH value in wei
|
|
192
|
+
* @param data - Optional calldata
|
|
193
|
+
* @returns Transaction result with UserOp hash
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* await smartWallet.sendTransaction(
|
|
197
|
+
* '0xRecipient',
|
|
198
|
+
* parseEther('0.1'),
|
|
199
|
+
* '0x'
|
|
200
|
+
* );
|
|
201
|
+
*/
|
|
202
|
+
async sendTransaction(
|
|
203
|
+
to: Hex,
|
|
204
|
+
value: bigint = 0n,
|
|
205
|
+
data: Hex = '0x'
|
|
206
|
+
): Promise<SmartWalletTransactionResult> {
|
|
207
|
+
if (!this.kernelAccount || !this.aaService) {
|
|
208
|
+
throw new SmartWalletError('Smart wallet not initialized', 'NOT_INITIALIZED');
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
try {
|
|
212
|
+
// Use session account if available, otherwise use kernel account
|
|
213
|
+
const account = this.sessionAccount || this.kernelAccount;
|
|
214
|
+
|
|
215
|
+
const userOpHash = await this.aaService.sendTransaction({
|
|
216
|
+
account,
|
|
217
|
+
to,
|
|
218
|
+
value,
|
|
219
|
+
data
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// Wait for receipt
|
|
223
|
+
const receipt = await this.aaService.waitForReceipt({
|
|
224
|
+
userOpHash,
|
|
225
|
+
chain: this.chain
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
return {
|
|
229
|
+
success: true,
|
|
230
|
+
userOpHash,
|
|
231
|
+
transactionHash: receipt.transactionHash
|
|
232
|
+
};
|
|
233
|
+
} catch (error) {
|
|
234
|
+
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
|
|
235
|
+
return {
|
|
236
|
+
success: false,
|
|
237
|
+
userOpHash: '0x' as Hex,
|
|
238
|
+
error: errorMsg
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Send multiple transactions in a single UserOperation
|
|
245
|
+
* This is one of the main advantages of smart accounts - pay gas ONLY ONCE!
|
|
246
|
+
*
|
|
247
|
+
* @param calls - Array of calls to execute
|
|
248
|
+
* @returns Transaction result
|
|
249
|
+
*
|
|
250
|
+
* @example
|
|
251
|
+
* await smartWallet.sendBatchTransaction([
|
|
252
|
+
* { to: recipient1, value: parseEther('0.1'), data: '0x' },
|
|
253
|
+
* { to: recipient2, value: parseEther('0.2'), data: '0x' },
|
|
254
|
+
* { to: usdcAddress, value: 0n, data: transferCalldata }
|
|
255
|
+
* ]);
|
|
256
|
+
*/
|
|
257
|
+
async sendBatchTransaction(calls: Call[]): Promise<SmartWalletTransactionResult> {
|
|
258
|
+
if (!this.kernelAccount || !this.aaService) {
|
|
259
|
+
throw new SmartWalletError('Smart wallet not initialized', 'NOT_INITIALIZED');
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (calls.length === 0) {
|
|
263
|
+
throw new TransactionError('Batch transaction must have at least one call');
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
try {
|
|
267
|
+
// Convert to AA service Call format
|
|
268
|
+
const aaCalls: AACall[] = calls.map(call => ({
|
|
269
|
+
to: call.to,
|
|
270
|
+
value: call.value,
|
|
271
|
+
data: call.data
|
|
272
|
+
}));
|
|
273
|
+
|
|
274
|
+
// Use session account if available, otherwise use kernel account
|
|
275
|
+
const account = this.sessionAccount || this.kernelAccount;
|
|
276
|
+
|
|
277
|
+
const userOpHash = await this.aaService.sendBatchTransaction({
|
|
278
|
+
account,
|
|
279
|
+
calls: aaCalls
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
// Wait for receipt
|
|
283
|
+
const receipt = await this.aaService.waitForReceipt({
|
|
284
|
+
userOpHash,
|
|
285
|
+
chain: this.chain
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
return {
|
|
289
|
+
success: true,
|
|
290
|
+
userOpHash,
|
|
291
|
+
transactionHash: receipt.transactionHash
|
|
292
|
+
};
|
|
293
|
+
} catch (error) {
|
|
294
|
+
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
|
|
295
|
+
return {
|
|
296
|
+
success: false,
|
|
297
|
+
userOpHash: '0x' as Hex,
|
|
298
|
+
error: errorMsg
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Prepare a call for batching
|
|
305
|
+
* Helper method to create Call objects
|
|
306
|
+
*/
|
|
307
|
+
prepareCall(to: Hex, value: bigint = 0n, data: Hex = '0x'): Call {
|
|
308
|
+
return { to, value, data };
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// ============================================
|
|
312
|
+
// Session Key Methods
|
|
313
|
+
// ============================================
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Generate a new session key
|
|
317
|
+
* The private key should be stored securely by the agent
|
|
318
|
+
*
|
|
319
|
+
* @returns Session key info with private key, address, and signer
|
|
320
|
+
*
|
|
321
|
+
* @example
|
|
322
|
+
* const sessionKey = await smartWallet.generateSessionKey();
|
|
323
|
+
* console.log('Address:', sessionKey.address);
|
|
324
|
+
* // Store sessionKey.privateKey securely
|
|
325
|
+
*/
|
|
326
|
+
async generateSessionKey(): Promise<SessionKeyInfo> {
|
|
327
|
+
if (!this.aaService) {
|
|
328
|
+
throw new SmartWalletError('Smart wallet not initialized', 'NOT_INITIALIZED');
|
|
329
|
+
}
|
|
330
|
+
try {
|
|
331
|
+
return await this.aaService.generateSessionKey();
|
|
332
|
+
} catch (error) {
|
|
333
|
+
throw new SessionKeyError(
|
|
334
|
+
`Failed to generate session key: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Recreate session key from stored private key
|
|
341
|
+
*
|
|
342
|
+
* @param privateKey - Session key private key
|
|
343
|
+
* @returns Session key info
|
|
344
|
+
*
|
|
345
|
+
* @example
|
|
346
|
+
* const sessionKey = await smartWallet.recreateSessionKey(storedPrivateKey);
|
|
347
|
+
*/
|
|
348
|
+
async recreateSessionKey(privateKey: Hex): Promise<SessionKeyInfo> {
|
|
349
|
+
if (!this.aaService) {
|
|
350
|
+
throw new SmartWalletError('Smart wallet not initialized', 'NOT_INITIALIZED');
|
|
351
|
+
}
|
|
352
|
+
try {
|
|
353
|
+
return await this.aaService.recreateSessionKey(privateKey);
|
|
354
|
+
} catch (error) {
|
|
355
|
+
throw new SessionKeyError(
|
|
356
|
+
`Failed to recreate session key: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Create session key approval (Owner side)
|
|
363
|
+
* Owner approves a session key with specific permissions
|
|
364
|
+
*
|
|
365
|
+
* @param options - Approval options with session key address and permissions
|
|
366
|
+
* @returns Serialized approval string to share with agent
|
|
367
|
+
*
|
|
368
|
+
* @example
|
|
369
|
+
* const approval = await smartWallet.approveSessionKey({
|
|
370
|
+
* sessionKeyAddress: '0x...',
|
|
371
|
+
* permissions: [
|
|
372
|
+
* smartWallet.createUSDCPermission(USDC_ADDRESS, '100')
|
|
373
|
+
* ]
|
|
374
|
+
* });
|
|
375
|
+
*/
|
|
376
|
+
async approveSessionKey(options: SessionKeyApprovalOptions): Promise<string> {
|
|
377
|
+
if (!this.aaService) {
|
|
378
|
+
throw new SmartWalletError('Smart wallet not initialized', 'NOT_INITIALIZED');
|
|
379
|
+
}
|
|
380
|
+
try {
|
|
381
|
+
return await this.aaService.createSessionKeyApproval({
|
|
382
|
+
sessionKeyAddress: options.sessionKeyAddress,
|
|
383
|
+
owner: this.ownerAccount,
|
|
384
|
+
chain: this.chain,
|
|
385
|
+
entryPointVersion: this.options.entryPointVersion,
|
|
386
|
+
useSudoPolicy: options.useSudoPolicy,
|
|
387
|
+
permissions: options.permissions
|
|
388
|
+
});
|
|
389
|
+
} catch (error) {
|
|
390
|
+
throw new SessionKeyError(
|
|
391
|
+
`Failed to approve session key: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Use session key for transactions (Agent side)
|
|
398
|
+
* Agent deserializes the approval and can send transactions with session key
|
|
399
|
+
*
|
|
400
|
+
* @param options - Session key usage options with approval and signer
|
|
401
|
+
*
|
|
402
|
+
* @example
|
|
403
|
+
* const sessionKey = await smartWallet.recreateSessionKey(privateKey);
|
|
404
|
+
* await smartWallet.useSessionKey({
|
|
405
|
+
* approval,
|
|
406
|
+
* sessionKeySigner: sessionKey.signer
|
|
407
|
+
* });
|
|
408
|
+
* // Now can send transactions with session key permissions
|
|
409
|
+
*/
|
|
410
|
+
async useSessionKey(options: SessionKeyUsageOptions): Promise<void> {
|
|
411
|
+
if (!this.aaService) {
|
|
412
|
+
throw new SmartWalletError('Smart wallet not initialized', 'NOT_INITIALIZED');
|
|
413
|
+
}
|
|
414
|
+
try {
|
|
415
|
+
this.sessionAccount = await this.aaService.deserializeSessionKey({
|
|
416
|
+
approval: options.approval,
|
|
417
|
+
sessionKeySigner: options.sessionKeySigner,
|
|
418
|
+
chain: this.chain,
|
|
419
|
+
entryPointVersion: this.options.entryPointVersion
|
|
420
|
+
});
|
|
421
|
+
} catch (error) {
|
|
422
|
+
throw new SessionKeyError(
|
|
423
|
+
`Failed to use session key: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Clear session key (revert to owner account)
|
|
430
|
+
*/
|
|
431
|
+
clearSessionKey(): void {
|
|
432
|
+
this.sessionAccount = null;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Check if using session key
|
|
437
|
+
*/
|
|
438
|
+
isUsingSessionKey(): boolean {
|
|
439
|
+
return this.sessionAccount !== null;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Create USDC transfer permission
|
|
444
|
+
*
|
|
445
|
+
* @param usdcAddress - USDC contract address
|
|
446
|
+
* @param maxAmount - Maximum USDC amount (in USDC units, e.g., "10" for 10 USDC)
|
|
447
|
+
* @returns Permission rule
|
|
448
|
+
*/
|
|
449
|
+
createUSDCPermission(usdcAddress: Hex, maxAmount: string): SessionKeyPermissionRule {
|
|
450
|
+
if (!this.aaService) {
|
|
451
|
+
throw new SmartWalletError('Smart wallet not initialized', 'NOT_INITIALIZED');
|
|
452
|
+
}
|
|
453
|
+
return this.aaService.createUSDCTransferPermission(usdcAddress, maxAmount);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Create ETH transfer permission
|
|
458
|
+
*
|
|
459
|
+
* @param maxValue - Maximum ETH value (in ether units, e.g., "0.1" for 0.1 ETH)
|
|
460
|
+
* @returns Permission rule
|
|
461
|
+
*/
|
|
462
|
+
createETHPermission(maxValue: string): SessionKeyPermissionRule {
|
|
463
|
+
if (!this.aaService) {
|
|
464
|
+
throw new SmartWalletError('Smart wallet not initialized', 'NOT_INITIALIZED');
|
|
465
|
+
}
|
|
466
|
+
return this.aaService.createETHTransferPermission(maxValue);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// ============================================
|
|
470
|
+
// Module Management Methods
|
|
471
|
+
// ============================================
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Install a module on the smart account
|
|
475
|
+
*
|
|
476
|
+
* @param options - Module installation options
|
|
477
|
+
* @returns Transaction result
|
|
478
|
+
*
|
|
479
|
+
* @example
|
|
480
|
+
* await smartWallet.installModule({
|
|
481
|
+
* moduleType: 'validator',
|
|
482
|
+
* moduleAddress: '0x...',
|
|
483
|
+
* initData: '0x...'
|
|
484
|
+
* });
|
|
485
|
+
*/
|
|
486
|
+
async installModule(options: ModuleInstallOptions): Promise<SmartWalletTransactionResult> {
|
|
487
|
+
if (!this.kernelAccount || !this.aaService) {
|
|
488
|
+
throw new SmartWalletError('Smart wallet not initialized', 'NOT_INITIALIZED');
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
try {
|
|
492
|
+
const userOpHash = await this.aaService.installModule({
|
|
493
|
+
account: this.kernelAccount,
|
|
494
|
+
moduleType: options.moduleType,
|
|
495
|
+
moduleAddress: options.moduleAddress,
|
|
496
|
+
initData: options.initData
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
const receipt = await this.aaService.waitForReceipt({
|
|
500
|
+
userOpHash,
|
|
501
|
+
chain: this.chain
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
return {
|
|
505
|
+
success: true,
|
|
506
|
+
userOpHash,
|
|
507
|
+
transactionHash: receipt.transactionHash
|
|
508
|
+
};
|
|
509
|
+
} catch (error) {
|
|
510
|
+
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
|
|
511
|
+
throw new ModuleError(`Failed to install module: ${errorMsg}`);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Uninstall a module from the smart account
|
|
517
|
+
*
|
|
518
|
+
* @param options - Module uninstallation options
|
|
519
|
+
* @returns Transaction result
|
|
520
|
+
*/
|
|
521
|
+
async uninstallModule(options: ModuleUninstallOptions): Promise<SmartWalletTransactionResult> {
|
|
522
|
+
if (!this.kernelAccount || !this.aaService) {
|
|
523
|
+
throw new SmartWalletError('Smart wallet not initialized', 'NOT_INITIALIZED');
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
try {
|
|
527
|
+
const userOpHash = await this.aaService.uninstallModule({
|
|
528
|
+
account: this.kernelAccount,
|
|
529
|
+
moduleType: options.moduleType,
|
|
530
|
+
moduleAddress: options.moduleAddress,
|
|
531
|
+
deInitData: options.deInitData
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
const receipt = await this.aaService.waitForReceipt({
|
|
535
|
+
userOpHash,
|
|
536
|
+
chain: this.chain
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
return {
|
|
540
|
+
success: true,
|
|
541
|
+
userOpHash,
|
|
542
|
+
transactionHash: receipt.transactionHash
|
|
543
|
+
};
|
|
544
|
+
} catch (error) {
|
|
545
|
+
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
|
|
546
|
+
throw new ModuleError(`Failed to uninstall module: ${errorMsg}`);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Check if a module is installed
|
|
552
|
+
*
|
|
553
|
+
* @param moduleType - Type of module
|
|
554
|
+
* @param moduleAddress - Module contract address
|
|
555
|
+
* @returns Whether the module is installed
|
|
556
|
+
*/
|
|
557
|
+
async isModuleInstalled(moduleType: ModuleType, moduleAddress: Hex): Promise<boolean> {
|
|
558
|
+
if (!this.kernelAccount || !this.aaService) {
|
|
559
|
+
throw new SmartWalletError('Smart wallet not initialized', 'NOT_INITIALIZED');
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
try {
|
|
563
|
+
return await this.aaService.isModuleInstalled({
|
|
564
|
+
account: this.kernelAccount,
|
|
565
|
+
moduleType,
|
|
566
|
+
moduleAddress
|
|
567
|
+
});
|
|
568
|
+
} catch (error) {
|
|
569
|
+
return false;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Prepare module installation call (for batching)
|
|
575
|
+
*
|
|
576
|
+
* @param options - Module installation options
|
|
577
|
+
* @returns Call object for batch transaction
|
|
578
|
+
*/
|
|
579
|
+
prepareInstallModule(options: ModuleInstallOptions): Call {
|
|
580
|
+
if (!this.kernelAccount || !this.aaService) {
|
|
581
|
+
throw new SmartWalletError('Smart wallet not initialized', 'NOT_INITIALIZED');
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
const aaCall = this.aaService.prepareInstallModule({
|
|
585
|
+
account: this.kernelAccount,
|
|
586
|
+
moduleType: options.moduleType,
|
|
587
|
+
moduleAddress: options.moduleAddress,
|
|
588
|
+
initData: options.initData
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
return {
|
|
592
|
+
to: aaCall.to,
|
|
593
|
+
value: aaCall.value,
|
|
594
|
+
data: aaCall.data
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Prepare module uninstallation call (for batching)
|
|
600
|
+
*
|
|
601
|
+
* @param options - Module uninstallation options
|
|
602
|
+
* @returns Call object for batch transaction
|
|
603
|
+
*/
|
|
604
|
+
prepareUninstallModule(options: ModuleUninstallOptions): Call {
|
|
605
|
+
if (!this.kernelAccount || !this.aaService) {
|
|
606
|
+
throw new SmartWalletError('Smart wallet not initialized', 'NOT_INITIALIZED');
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
const aaCall = this.aaService.prepareUninstallModule({
|
|
610
|
+
account: this.kernelAccount,
|
|
611
|
+
moduleType: options.moduleType,
|
|
612
|
+
moduleAddress: options.moduleAddress,
|
|
613
|
+
deInitData: options.deInitData
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
return {
|
|
617
|
+
to: aaCall.to,
|
|
618
|
+
value: aaCall.value,
|
|
619
|
+
data: aaCall.data
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// ============================================
|
|
624
|
+
// Advanced Features
|
|
625
|
+
// ============================================
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* Enable multi-signature validation
|
|
629
|
+
*
|
|
630
|
+
* @param config - Multi-sig configuration with owners and threshold
|
|
631
|
+
* @returns Transaction result
|
|
632
|
+
*
|
|
633
|
+
* @example
|
|
634
|
+
* await smartWallet.enableMultiSig({
|
|
635
|
+
* owners: [owner1, owner2, owner3],
|
|
636
|
+
* threshold: 2 // 2 of 3 required
|
|
637
|
+
* });
|
|
638
|
+
*/
|
|
639
|
+
async enableMultiSig(config: MultiSigConfig): Promise<SmartWalletTransactionResult> {
|
|
640
|
+
if (!this.kernelAccount || !this.aaService) {
|
|
641
|
+
throw new SmartWalletError('Smart wallet not initialized', 'NOT_INITIALIZED');
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
try {
|
|
645
|
+
const userOpHash = await this.aaService.installMultiSigValidator({
|
|
646
|
+
account: this.kernelAccount,
|
|
647
|
+
owners: config.owners,
|
|
648
|
+
threshold: config.threshold
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
const receipt = await this.aaService.waitForReceipt({
|
|
652
|
+
userOpHash,
|
|
653
|
+
chain: this.chain
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
return {
|
|
657
|
+
success: true,
|
|
658
|
+
userOpHash,
|
|
659
|
+
transactionHash: receipt.transactionHash
|
|
660
|
+
};
|
|
661
|
+
} catch (error) {
|
|
662
|
+
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
|
|
663
|
+
throw new SmartWalletError(`Failed to enable multi-sig: ${errorMsg}`, 'MULTISIG_ERROR');
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Set paymaster for gas sponsorship
|
|
669
|
+
*
|
|
670
|
+
* @param paymasterUrl - Paymaster service URL
|
|
671
|
+
*
|
|
672
|
+
* @example
|
|
673
|
+
* smartWallet.setPaymaster('https://api.pimlico.io/v2/sepolia/paymaster');
|
|
674
|
+
*/
|
|
675
|
+
setPaymaster(paymasterUrl: string, context?: any): void {
|
|
676
|
+
this.paymasterConfig = {
|
|
677
|
+
paymasterUrl,
|
|
678
|
+
context
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/**
|
|
683
|
+
* Clear paymaster (user pays gas)
|
|
684
|
+
*/
|
|
685
|
+
clearPaymaster(): void {
|
|
686
|
+
this.paymasterConfig = null;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* Check if paymaster is configured
|
|
691
|
+
*/
|
|
692
|
+
hasPaymaster(): boolean {
|
|
693
|
+
return this.paymasterConfig !== null;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
/**
|
|
697
|
+
* Get bundler information
|
|
698
|
+
*/
|
|
699
|
+
getBundlerInfo(): { provider: string; url: string } {
|
|
700
|
+
if (!this.aaService) {
|
|
701
|
+
return {
|
|
702
|
+
provider: 'custom',
|
|
703
|
+
url: this.bundlerUrl
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
return this.aaService.getBundlerInfo(this.chain);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// ============================================
|
|
710
|
+
// Sponsored Transactions (EIP-7702)
|
|
711
|
+
// ============================================
|
|
712
|
+
|
|
713
|
+
/**
|
|
714
|
+
* Send sponsored batch transaction where another wallet pays gas fees
|
|
715
|
+
* Requires smart wallet to be initialized
|
|
716
|
+
*
|
|
717
|
+
* @param calls - Array of calls to execute
|
|
718
|
+
* @param feePayerPrivateKey - Private key of the wallet paying gas fees
|
|
719
|
+
* @returns Transaction result with userOpHash and transactionHash
|
|
720
|
+
*
|
|
721
|
+
* @example
|
|
722
|
+
* // Send ETH transfer with sponsor paying gas
|
|
723
|
+
* const result = await smartWallet.sendSponsoredBatchTransaction(
|
|
724
|
+
* [{ to: recipient, value: parseEther('0.1') }],
|
|
725
|
+
* sponsorPrivateKey
|
|
726
|
+
* );
|
|
727
|
+
*/
|
|
728
|
+
async sendSponsoredBatchTransaction(
|
|
729
|
+
calls: Call[],
|
|
730
|
+
feePayerPrivateKey: Hex
|
|
731
|
+
): Promise<SmartWalletTransactionResult> {
|
|
732
|
+
if (!this.aaService) {
|
|
733
|
+
throw new SmartWalletError(
|
|
734
|
+
'Smart wallet not initialized. Call initialize() first.',
|
|
735
|
+
'NOT_INITIALIZED'
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
if (!this.kernelAccount) {
|
|
740
|
+
throw new SmartWalletError(
|
|
741
|
+
'Kernel account not found. Smart wallet may not be properly initialized.',
|
|
742
|
+
'ACCOUNT_NOT_FOUND'
|
|
743
|
+
);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
try {
|
|
747
|
+
// Create authorization for owner wallet
|
|
748
|
+
const authorization = await createKernelAuthorization({
|
|
749
|
+
owner: this.ownerAccount,
|
|
750
|
+
chain: this.chain,
|
|
751
|
+
bundlerManager: this.aaService.getBundlerManager(this.chain)
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
if (!authorization) {
|
|
755
|
+
throw new SmartWalletError(
|
|
756
|
+
'Failed to create authorization for sponsored transaction',
|
|
757
|
+
'AUTHORIZATION_FAILED'
|
|
758
|
+
);
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// Send sponsored transaction with feePayer
|
|
762
|
+
const result = await sendSponsoredBatchTransaction({
|
|
763
|
+
ownerAuthorization: authorization,
|
|
764
|
+
calls,
|
|
765
|
+
feePayerPrivateKey,
|
|
766
|
+
bundlerUrl: this.bundlerUrl,
|
|
767
|
+
paymasterUrl: this.paymasterConfig?.paymasterUrl,
|
|
768
|
+
chain: this.chain,
|
|
769
|
+
entryPointVersion: this.options.entryPointVersion
|
|
770
|
+
});
|
|
771
|
+
|
|
772
|
+
return {
|
|
773
|
+
success: true,
|
|
774
|
+
userOpHash: result.userOpHash,
|
|
775
|
+
transactionHash: result.transactionHash
|
|
776
|
+
};
|
|
777
|
+
} catch (error) {
|
|
778
|
+
console.error('Sponsored batch transaction failed:', error);
|
|
779
|
+
return {
|
|
780
|
+
success: false,
|
|
781
|
+
userOpHash: '0x' as Hex,
|
|
782
|
+
error: error instanceof Error ? error.message : 'Unknown error occurred'
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
/**
|
|
788
|
+
* Clear all caches
|
|
789
|
+
*/
|
|
790
|
+
clearCache(): void {
|
|
791
|
+
if (this.aaService) {
|
|
792
|
+
this.aaService.clearCache();
|
|
793
|
+
}
|
|
794
|
+
this.kernelAccount = null;
|
|
795
|
+
this.sessionAccount = null;
|
|
796
|
+
}
|
|
797
|
+
}
|