@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.
Files changed (98) hide show
  1. package/.claude/settings.local.json +12 -0
  2. package/SMART_WALLET_GUIDE.md +746 -0
  3. package/SMART_WALLET_IMPLEMENTATION.md +460 -0
  4. package/dist/IChainWallet.d.ts +3 -3
  5. package/dist/IChainWallet.js +5 -0
  6. package/dist/IChainWallet.js.map +1 -1
  7. package/dist/evm/SMART_WALLET_EXAMPLES.d.ts +20 -0
  8. package/dist/evm/SMART_WALLET_EXAMPLES.js +451 -0
  9. package/dist/evm/SMART_WALLET_EXAMPLES.js.map +1 -0
  10. package/dist/evm/aa-service/index.d.ts +16 -0
  11. package/dist/evm/aa-service/index.js +69 -0
  12. package/dist/evm/aa-service/index.js.map +1 -0
  13. package/dist/evm/aa-service/lib/account-adapter.d.ts +26 -0
  14. package/dist/evm/aa-service/lib/account-adapter.js +53 -0
  15. package/dist/evm/aa-service/lib/account-adapter.js.map +1 -0
  16. package/dist/evm/aa-service/lib/kernel-account.d.ts +91 -0
  17. package/dist/evm/aa-service/lib/kernel-account.js +251 -0
  18. package/dist/evm/aa-service/lib/kernel-account.js.map +1 -0
  19. package/dist/evm/aa-service/lib/kernel-modules.d.ts +240 -0
  20. package/dist/evm/aa-service/lib/kernel-modules.js +409 -0
  21. package/dist/evm/aa-service/lib/kernel-modules.js.map +1 -0
  22. package/dist/evm/aa-service/lib/session-keys.d.ts +170 -0
  23. package/dist/evm/aa-service/lib/session-keys.js +297 -0
  24. package/dist/evm/aa-service/lib/session-keys.js.map +1 -0
  25. package/dist/evm/aa-service/lib/type.d.ts +167 -0
  26. package/dist/evm/aa-service/lib/type.js +43 -0
  27. package/dist/evm/aa-service/lib/type.js.map +1 -0
  28. package/dist/evm/aa-service/services/account-abstraction.d.ts +614 -0
  29. package/dist/evm/aa-service/services/account-abstraction.js +754 -0
  30. package/dist/evm/aa-service/services/account-abstraction.js.map +1 -0
  31. package/dist/evm/aa-service/services/bundler.d.ts +29 -0
  32. package/dist/evm/aa-service/services/bundler.js +168 -0
  33. package/dist/evm/aa-service/services/bundler.js.map +1 -0
  34. package/dist/evm/evm.d.ts +67 -3
  35. package/dist/evm/evm.js +212 -7
  36. package/dist/evm/evm.js.map +1 -1
  37. package/dist/evm/index.d.ts +1 -0
  38. package/dist/evm/index.js +3 -0
  39. package/dist/evm/index.js.map +1 -1
  40. package/dist/evm/smartWallet.d.ts +265 -0
  41. package/dist/evm/smartWallet.js +675 -0
  42. package/dist/evm/smartWallet.js.map +1 -0
  43. package/dist/evm/smartWallet.types.d.ts +10 -0
  44. package/dist/evm/smartWallet.types.js +16 -0
  45. package/dist/evm/smartWallet.types.js.map +1 -0
  46. package/dist/evm/transaction.utils.d.ts +10 -10
  47. package/dist/evm/transaction.utils.js +12 -8
  48. package/dist/evm/transaction.utils.js.map +1 -1
  49. package/dist/evm/transactionParsing.js +77 -1
  50. package/dist/evm/transactionParsing.js.map +1 -1
  51. package/dist/helpers/index.d.ts +1 -0
  52. package/dist/helpers/index.js +15 -0
  53. package/dist/helpers/index.js.map +1 -1
  54. package/dist/helpers/routeScan.d.ts +191 -0
  55. package/dist/helpers/routeScan.js +114 -0
  56. package/dist/helpers/routeScan.js.map +1 -0
  57. package/dist/index.d.ts +0 -2
  58. package/dist/index.js +0 -2
  59. package/dist/index.js.map +1 -1
  60. package/dist/svm/svm.d.ts +4 -3
  61. package/dist/svm/svm.js +29 -18
  62. package/dist/svm/svm.js.map +1 -1
  63. package/dist/svm/transactionSender.js +2 -2
  64. package/dist/svm/transactionSender.js.map +1 -1
  65. package/dist/svm/utils.d.ts +4 -3
  66. package/dist/svm/utils.js +19 -6
  67. package/dist/svm/utils.js.map +1 -1
  68. package/dist/test.js +7 -0
  69. package/dist/test.js.map +1 -1
  70. package/dist/types.d.ts +19 -2
  71. package/dist/types.js.map +1 -1
  72. package/dist/vm.js +9 -7
  73. package/dist/vm.js.map +1 -1
  74. package/package.json +2 -2
  75. package/tsconfig.json +4 -3
  76. package/utils/IChainWallet.ts +3 -3
  77. package/utils/evm/SMART_WALLET_EXAMPLES.ts.bak +591 -0
  78. package/utils/evm/aa-service/index.ts +85 -0
  79. package/utils/evm/aa-service/lib/account-adapter.ts +60 -0
  80. package/utils/evm/aa-service/lib/kernel-account.ts +367 -0
  81. package/utils/evm/aa-service/lib/kernel-modules.ts +598 -0
  82. package/utils/evm/aa-service/lib/session-keys.ts +389 -0
  83. package/utils/evm/aa-service/lib/type.ts +236 -0
  84. package/utils/evm/aa-service/services/account-abstraction.ts +1015 -0
  85. package/utils/evm/aa-service/services/bundler.ts +217 -0
  86. package/utils/evm/evm.ts +268 -11
  87. package/utils/evm/index.ts +5 -1
  88. package/utils/evm/smartWallet.ts +797 -0
  89. package/utils/evm/smartWallet.types.ts +33 -0
  90. package/utils/evm/transaction.utils.ts +12 -10
  91. package/utils/evm/transactionParsing.ts +100 -14
  92. package/utils/helpers/index.ts +1 -0
  93. package/utils/helpers/routeScan.ts +397 -0
  94. package/utils/index.ts +0 -2
  95. package/utils/svm/svm.ts +50 -9
  96. package/utils/svm/utils.ts +52 -7
  97. package/utils/test.ts +7 -0
  98. 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
+ }