@deserialize/multi-vm-wallet 1.3.3 → 1.4.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 (50) hide show
  1. package/dist/IChainWallet.d.ts +2 -0
  2. package/dist/IChainWallet.js.map +1 -1
  3. package/dist/evm/evm.d.ts +14 -172
  4. package/dist/evm/evm.js +38 -504
  5. package/dist/evm/evm.js.map +1 -1
  6. package/dist/evm/transaction.utils.d.ts +3 -3
  7. package/dist/evm/utils.d.ts +115 -80
  8. package/dist/evm/utils.js +272 -497
  9. package/dist/evm/utils.js.map +1 -1
  10. package/dist/helpers/index.d.ts +1 -0
  11. package/dist/helpers/index.js +5 -0
  12. package/dist/helpers/index.js.map +1 -1
  13. package/dist/index.d.ts +1 -0
  14. package/dist/index.js +1 -0
  15. package/dist/index.js.map +1 -1
  16. package/dist/price.d.ts +2 -0
  17. package/dist/price.js +33 -0
  18. package/dist/price.js.map +1 -0
  19. package/dist/price.types.d.ts +38 -0
  20. package/dist/price.types.js +4 -0
  21. package/dist/price.types.js.map +1 -0
  22. package/dist/savings/index.d.ts +0 -0
  23. package/dist/savings/index.js +25 -0
  24. package/dist/savings/index.js.map +1 -0
  25. package/dist/savings/saving-actions.d.ts +29 -0
  26. package/dist/savings/saving-actions.js +56 -0
  27. package/dist/savings/saving-actions.js.map +1 -0
  28. package/dist/savings/savings-manager.d.ts +1 -1
  29. package/dist/savings/savings-manager.js +3 -3
  30. package/dist/savings/savings-manager.js.map +1 -1
  31. package/dist/svm/svm.d.ts +2 -0
  32. package/dist/svm/svm.js +12 -0
  33. package/dist/svm/svm.js.map +1 -1
  34. package/dist/test.js +7 -7
  35. package/dist/test.js.map +1 -1
  36. package/dist/vm.js.map +1 -1
  37. package/package.json +1 -1
  38. package/utils/IChainWallet.ts +2 -0
  39. package/utils/evm/evm.ts +326 -681
  40. package/utils/evm/utils.ts +438 -662
  41. package/utils/helpers/index.ts +6 -0
  42. package/utils/index.ts +1 -0
  43. package/utils/price.ts +37 -0
  44. package/utils/price.types.ts +45 -0
  45. package/utils/savings/index.ts +28 -0
  46. package/utils/savings/saving-actions.ts +77 -0
  47. package/utils/savings/savings-manager.ts +1 -1
  48. package/utils/svm/svm.ts +16 -2
  49. package/utils/test.ts +13 -4
  50. package/utils/vm.ts +2 -1
package/utils/evm/evm.ts CHANGED
@@ -8,36 +8,32 @@ import { EVMDeriveChildPrivateKey } from "../walletBip32";
8
8
  import { ChainWallet } from "../IChainWallet";
9
9
  import { Balance, ChainWalletConfig, NFTInfo, UserTokenBalance, TokenInfo, TransactionResult, NFT } from "../types";
10
10
  import { VM } from "../vm";
11
- import { ethers, JsonRpcProvider, Wallet, formatUnits, Interface } from "ethers";
11
+ import { ethers, formatUnits, Interface } from "ethers";
12
12
  import BN from "bn.js";
13
13
  import {
14
14
  getNativeBalance,
15
15
  getTokenBalance,
16
16
  sendERC20Token,
17
17
  sendNativeToken,
18
- performSwap,
18
+
19
19
  checkAndApprove,
20
20
  signSendAndConfirm,
21
- getNativeTokenAddress,
22
- prepareSwapParams,
23
- formatAmountToWei,
24
- isChainSupportedByKyber,
25
- isChainSupportedByDebonk,
26
- // normalizeTokenAddressForDebonk,
27
- convertSlippageForDebonk,
21
+
22
+
28
23
  TransactionParams,
29
24
  approveToken,
30
- executeContractMethod,
25
+
31
26
  getTokenInfo,
32
27
  DESERIALIZED_SUPPORTED_CHAINS,
33
28
  discoverTokens,
34
- discoverNFTs
29
+ discoverNFTs,
30
+ fromChainToViemChain
35
31
  } from "./utils";
36
32
  import { EVMTransactionHistoryItem, getEVMTransactionHistory } from "./transactionParsing";
37
- import { createPublicClient, Hex, http, PublicClient, parseEther, parseUnits, Call } from "viem";
33
+ import { createPublicClient, Hex, http, parseEther, parseUnits, Call, walletActions, WalletClient, createWalletClient, Chain, ClientConfig, EIP1193RequestFn, TransportConfig, PublicClient, ChainConfig } from "viem";
38
34
  import { EVMSmartWallet } from "./smartWallet";
39
35
  import { SmartWalletOptions } from "./aa-service";
40
- import { SavingsManager } from "../savings/savings-manager";
36
+
41
37
  import { SmartSavingsManager } from "../savings/smart-savings";
42
38
  import {
43
39
  SavingsAccount,
@@ -47,6 +43,11 @@ import {
47
43
  TransferToSavingsOptions,
48
44
  WithdrawFromSavingsOptions
49
45
  } from "../savings/types";
46
+ import { fetchPrices } from "../price";
47
+ import { PriceResponse } from "../price.types";
48
+ import { Account, privateKeyToAccount } from "viem/accounts";
49
+ // import { extendWalletClientWithSavings } from "../savings";
50
+ import { SavingsManager } from "../savings/saving-actions";
50
51
 
51
52
 
52
53
  interface DebonkQuoteResponse {
@@ -85,7 +86,8 @@ interface DebonkSwapResult {
85
86
  }
86
87
 
87
88
 
88
- export class EVMVM extends VM<string, string, JsonRpcProvider> {
89
+ export class EVMVM extends VM<string, string, PublicClient> {
90
+
89
91
  derivationPath = "m/44'/60'/0'/0/"; // Default EVM derivation path
90
92
 
91
93
  constructor(seed: string) {
@@ -139,7 +141,7 @@ export class EVMVM extends VM<string, string, JsonRpcProvider> {
139
141
  };
140
142
  }
141
143
 
142
- static fromMnemonic(mnemonic: string): VM<string, string, JsonRpcProvider> {
144
+ static fromMnemonic(mnemonic: string): VM<string, string, PublicClient> {
143
145
  const seed = VM.mnemonicToSeed(mnemonic)
144
146
  return new EVMVM(seed)
145
147
  }
@@ -148,63 +150,40 @@ export class EVMVM extends VM<string, string, JsonRpcProvider> {
148
150
  return ethers.isAddress(address);
149
151
  }
150
152
 
151
- static async getNativeBalance(address: string, connection: JsonRpcProvider): Promise<Balance> {
153
+ static async getNativeBalance(address: string, connection: PublicClient): Promise<Balance> {
152
154
  // Implement native balance retrieval logic here
153
- return await getNativeBalance(address, connection)
155
+ return await getNativeBalance(address as Hex, connection)
154
156
  }
155
157
 
156
- static async getTokenBalance(address: string, tokenAddress: string, connection: JsonRpcProvider): Promise<Balance> {
158
+ static async getTokenBalance(address: string, tokenAddress: string, connection: PublicClient): Promise<Balance> {
157
159
  // Implement token balance retrieval logic here
158
- return await getTokenBalance(tokenAddress, address, connection)
160
+ return await getTokenBalance(tokenAddress as Hex, address as Hex, connection)
159
161
  }
160
162
  }
161
163
 
162
- export class EVMChainWallet extends ChainWallet<string, string, JsonRpcProvider> {
163
- client: PublicClient
164
- wallet: Wallet
164
+ export class EVMChainWallet extends ChainWallet<string, string, PublicClient> {
165
+ wallet: WalletClient
165
166
  private smartWallet?: EVMSmartWallet
166
167
  private savingsManager?: SavingsManager
167
168
  private smartSavingsManager?: SmartSavingsManager
168
169
 
169
170
  constructor(config: ChainWalletConfig, privateKey: string, index: number) {
170
171
  super(config, privateKey, index);
171
- this.connection = new JsonRpcProvider(config.rpcUrl)
172
- const wallet = new Wallet(privateKey, this.connection);
173
- this.wallet = wallet
174
- this.address = wallet.address;
175
- this.privateKey = privateKey;
176
-
177
- //client for viem
178
- this.client = createPublicClient(
172
+ this.connection = createPublicClient(
179
173
  {
180
- chain: {
181
- rpcUrls: {
182
- default: {
183
- http: [config.rpcUrl]
184
- }
185
- },
186
- id: config.chainId,
187
- name: config.name,
188
- nativeCurrency: {
189
- name: config.nativeToken.name,
190
- symbol: config.nativeToken.symbol,
191
- decimals: config.nativeToken.decimals
192
- },
193
- blockExplorers: {
194
- default: {
195
- name: config.name + " Explorer",
196
- url: config.explorerUrl,
197
- apiUrl: config.explorerUrl
198
- },
199
-
200
- },
201
- testnet: config.testnet || false
202
-
203
- },
174
+ chain: fromChainToViemChain(config),
204
175
  transport: http(config.rpcUrl)
205
176
  },
206
-
207
177
  )
178
+ const account = privateKeyToAccount(privateKey as Hex)
179
+ this.wallet = createWalletClient({
180
+ account,
181
+ chain: fromChainToViemChain(config),
182
+ transport: http(config.rpcUrl)
183
+ })
184
+
185
+ this.address = account.address
186
+ this.privateKey = privateKey;
208
187
  }
209
188
 
210
189
  // ============================================
@@ -227,6 +206,10 @@ export class EVMChainWallet extends ChainWallet<string, string, JsonRpcProvider>
227
206
  return !!this.smartWallet;
228
207
  }
229
208
 
209
+ createSavingsManager(mnemonic: string, index: number = 0, chain: ChainConfig) {
210
+ return new SavingsManager(mnemonic, index, chain)
211
+ }
212
+
230
213
  /**
231
214
  * Validate that AA is available for sponsored transactions
232
215
  * @throws Error with helpful message if AA is not available
@@ -364,8 +347,8 @@ export class EVMChainWallet extends ChainWallet<string, string, JsonRpcProvider>
364
347
  // Existing Wallet Methods
365
348
  // ============================================
366
349
 
367
- getWallet(): Wallet {
368
- return new Wallet(this.privateKey, this.connection);
350
+ getWallet(): WalletClient {
351
+ return this.wallet
369
352
  }
370
353
 
371
354
  generateAddress(): string {
@@ -383,7 +366,7 @@ export class EVMChainWallet extends ChainWallet<string, string, JsonRpcProvider>
383
366
  }
384
367
 
385
368
  async getTokenInfo(tokenAddress: string) {
386
- return await EVMVM.getTokenInfo(tokenAddress, this.connection!)
369
+ return await EVMVM.getTokenInfo(tokenAddress as Hex, this.connection!)
387
370
  }
388
371
 
389
372
  async discoverToken(): Promise<UserTokenBalance<string>[]> {
@@ -398,24 +381,38 @@ export class EVMChainWallet extends ChainWallet<string, string, JsonRpcProvider>
398
381
 
399
382
  async transferNative(to: string, amount: number): Promise<TransactionResult> {
400
383
  const wallet = this.getWallet();
401
- return await sendNativeToken(wallet, to, amount.toString(), undefined, this.config.confirmationNo || 5);
384
+ return await sendNativeToken(wallet, this.connection!, to as Hex, amount.toString(), this.config.confirmationNo || 5);
402
385
  }
403
386
 
404
387
  async transferToken(tokenAddress: TokenInfo, to: string, amount: number): Promise<TransactionResult> {
405
388
  const wallet = this.getWallet();
406
- return await sendERC20Token(wallet, tokenAddress.address, to, amount.toString(), undefined, this.config.confirmationNo || 5);
389
+ return await sendERC20Token(wallet, this.connection!, tokenAddress.address as Hex, to as Hex, BigInt(amount.toString()), this.config.confirmationNo || 5);
407
390
  }
408
391
 
409
392
  async getTransactionHistory(): Promise<EVMTransactionHistoryItem[]> {
410
393
  const wallet = this.getWallet();
411
394
  let res: EVMTransactionHistoryItem
412
395
  try {
413
- return await getEVMTransactionHistory(this.client, wallet.address as Hex);
396
+ return await getEVMTransactionHistory(this.connection!, this.address as Hex);
414
397
  } catch (error) {
415
398
  return []
416
399
  }
417
400
  }
418
401
 
402
+ async getPrices(tokenAddresses: string[]): Promise<PriceResponse> {
403
+ const result = await fetchPrices({
404
+ vm: 'EVM',
405
+ chainId: this.config.chainId,
406
+ tokenAddresses,
407
+ });
408
+
409
+ if (result.error) {
410
+ throw new Error(result.error.message);
411
+ }
412
+
413
+ return result.data as PriceResponse;
414
+ }
415
+
419
416
  // Updated swap method signature to match base class so created another method to use it inside swap
420
417
  async swap(
421
418
  tokenAddress: TokenInfo,
@@ -423,187 +420,12 @@ export class EVMChainWallet extends ChainWallet<string, string, JsonRpcProvider>
423
420
  amount: number,
424
421
  slippage: number = 50
425
422
  ): Promise<TransactionResult> {
426
- if (amount <= 0) {
427
- return {
428
- success: false,
429
- hash: "",
430
- error: "Amount must be greater than 0"
431
- };
432
- }
433
- const tokenOut: TokenInfo = {
434
- address: to,
435
- name: '',
436
- symbol: '',
437
- decimals: 18
438
- };
439
-
440
- return await this.performCompleteSwap(tokenAddress, tokenOut, amount, slippage);
423
+ throw new Error("Not Implemented")
441
424
  }
442
425
 
443
- async performCompleteSwap(
444
- tokenIn: TokenInfo,
445
- tokenOut: TokenInfo,
446
- amount: number,
447
- slippage: number = 50,
448
- recipient?: string,
449
- deadline?: number
450
- ): Promise<TransactionResult> {
451
- try {
452
- const wallet = this.getWallet();
453
- const chainId = (await this.connection!.getNetwork()).chainId.toString();
454
-
455
- console.log(` Starting swap on chain ${chainId}:`, {
456
- from: tokenIn.symbol || tokenIn.address,
457
- to: tokenOut.symbol || tokenOut.address,
458
- amount: amount,
459
- slippage: `${slippage / 100}%`
460
- });
461
-
462
- // Check if this is a 0G chain that should use Debonk
463
- if (isChainSupportedByDebonk(chainId)) {
464
- console.log('Using Debonk API for 0G chain swap');
465
- return await this.performDebonkSwap(tokenIn, tokenOut, amount, slippage, recipient, deadline);
466
- }
467
-
468
- // Otherwise use Kyber (existing flow)
469
- console.log('Using KyberSwap for non-0G chain swap');
470
- return await this.performKyberSwap(tokenIn, tokenOut, amount, slippage, recipient, deadline);
471
-
472
- } catch (error) {
473
- console.error('Swap failed:', error);
474
- throw new Error(`Swap failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
475
- }
476
- }
477
-
478
- private async performDebonkSwap(
479
- tokenIn: TokenInfo,
480
- tokenOut: TokenInfo,
481
- amount: number,
482
- slippage: number = 50, // bps (e.g., 50 = 0.5%)
483
- recipient?: string,
484
- deadline?: number
485
- ): Promise<DebonkSwapResult> {
486
- try {
487
- const BASE_URL = 'https://evm-api.deserialize.xyz';
488
- const tokenInAddress = tokenIn.address;
489
- const tokenOutAddress = tokenOut.address;
490
-
491
- // Convert amount to wei (multiply by 10^18 for 18 decimal tokens)
492
- const amountInWei = (amount * Math.pow(10, tokenIn.decimals || 18)).toString();
493
-
494
- // Convert slippage from bps to percentage (e.g., 50 bps -> 0.5%)
495
- const slippagePercentage = slippage / 100;
496
-
497
-
498
- // Step 1: Get quote from API
499
-
500
- const quotePayload = {
501
- tokenA: tokenInAddress,
502
- tokenB: tokenOutAddress,
503
- amountIn: amountInWei, // Use wei amount
504
- dexId: "ZERO_G"
505
- };
506
-
507
-
508
- const quoteResponse = await fetch(`${BASE_URL}/quote`, {
509
- method: 'POST',
510
- headers: {
511
- 'Content-Type': 'application/json',
512
- },
513
- body: JSON.stringify(quotePayload)
514
- });
515
-
516
- if (!quoteResponse.ok) {
517
- const errorText = await quoteResponse.text();
518
- console.error("Quote API error response:", errorText);
519
- return this.fail(`Quote API failed: ${quoteResponse.status} ${errorText}`);
520
- }
521
-
522
- const quote: DebonkQuoteResponse = await quoteResponse.json();
523
-
524
- // Step 2: Fix the quote dexId for swap API (it expects "ALL")
525
- const modifiedQuote = {
526
- ...quote,
527
- dexId: "ALL" // Change from "ZERO_G" to "ALL" as required by swap API
528
- };
529
-
530
- // Step 3: Get wallet address
531
- const walletAddress = await this.getWallet().getAddress();
532
-
533
- const swapPayload = {
534
- publicKey: walletAddress,
535
- slippage: slippagePercentage,
536
- quote: modifiedQuote
537
-
538
- };
539
-
540
-
541
- const swapResponse = await fetch(`${BASE_URL}/swap`, {
542
- method: 'POST',
543
- headers: {
544
- 'Content-Type': 'application/json',
545
- },
546
- body: JSON.stringify(swapPayload)
547
- });
548
-
549
- if (!swapResponse.ok) {
550
- const errorText = await swapResponse.text();
551
- console.error("Swap API error response:", errorText);
552
- return this.fail(`Swap API failed: ${swapResponse.status} ${errorText}`);
553
- }
554
-
555
- const swapData: DebonkSwapResponse = await swapResponse.json();
556
-
557
- const wallet = this.getWallet();
558
- let lastTxHash = '';
559
426
 
560
- // Step 5: Execute each transaction sequentially
561
427
 
562
- for (let i = 0; i < swapData.transactions.length; i++) {
563
- const transaction = swapData.transactions[i];
564
428
 
565
- // Prepare transaction object
566
- const txRequest = {
567
- to: transaction.to,
568
- data: transaction.data,
569
- value: transaction.value,
570
- // gasPrice: 70000000000,
571
- // gasLimit: 70000, // Increase significantly
572
- };
573
-
574
- try {
575
- const txResponse = await wallet.sendTransaction(txRequest);
576
- console.log(`Transaction ${i + 1} sent:`, txResponse.hash);
577
-
578
- // Wait for confirmation
579
- const receipt = await txResponse.wait();
580
-
581
- if (!receipt) {
582
- return this.fail(`Transaction ${i + 1} failed - no receipt received`);
583
- }
584
-
585
- const txHash = receipt.hash || txResponse.hash;
586
- lastTxHash = txHash;
587
-
588
-
589
- } catch (txError: any) {
590
- console.error(`Transaction ${i + 1} failed:`, txError);
591
- return this.fail(`Transaction ${i + 1} failed: ${txError?.message ?? String(txError)}`);
592
- }
593
- }
594
-
595
-
596
-
597
- return {
598
- success: true,
599
- hash: lastTxHash // Return the hash of the last transaction
600
- };
601
-
602
- } catch (error: any) {
603
- console.error("Debonk API swap error:", error);
604
- return this.fail(`Debonk API swap failed: ${error?.message ?? String(error)}`);
605
- }
606
- }
607
429
 
608
430
  // Helper method for EVMChainWallet class
609
431
  private fail(message: string): DebonkSwapResult {
@@ -615,170 +437,9 @@ export class EVMChainWallet extends ChainWallet<string, string, JsonRpcProvider>
615
437
  }
616
438
 
617
439
 
618
- private async performKyberSwap(
619
- tokenIn: TokenInfo,
620
- tokenOut: TokenInfo,
621
- amount: number,
622
- slippage: number = 50,
623
- recipient?: string,
624
- deadline?: number
625
- ): Promise<TransactionResult> {
626
- try {
627
- const wallet = this.getWallet();
628
- const chainId = (await this.connection!.getNetwork()).chainId.toString();
629
-
630
- if (!isChainSupportedByKyber(chainId)) {
631
- throw new Error(`Chain ${chainId} is not supported by KyberSwap`);
632
- }
633
-
634
- const isNativeIn = tokenIn.address === 'native' ||
635
- tokenIn.address.toLowerCase() === '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
636
- const isNativeOut = tokenOut.address === 'native' ||
637
- tokenOut.address.toLowerCase() === '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
638
-
639
- let tokenInDecimals = 18;
640
- if (!isNativeIn && tokenIn.decimals) {
641
- tokenInDecimals = tokenIn.decimals;
642
- } else if (!isNativeIn) {
643
- const tokenBalance = await this.getTokenBalance(tokenIn.address);
644
- tokenInDecimals = tokenBalance.decimal;
645
- }
646
-
647
- const { tokenInAddress, tokenOutAddress, formattedAmountIn } = prepareSwapParams(
648
- tokenIn.address,
649
- tokenOut.address,
650
- amount.toString(),
651
- tokenInDecimals,
652
- isNativeIn,
653
- isNativeOut
654
- );
655
440
 
656
441
 
657
442
 
658
- if (isNativeIn) {
659
- const nativeBalance = await this.getNativeBalance();
660
- const requiredAmount = new BN(formattedAmountIn);
661
- if (nativeBalance.balance.lt(requiredAmount)) {
662
- throw new Error(`Insufficient native balance. Required: ${amount}, Available: ${nativeBalance.formatted}`);
663
- }
664
- } else {
665
- const tokenBalance = await this.getTokenBalance(tokenIn.address);
666
- const requiredAmount = new BN(formattedAmountIn);
667
- if (tokenBalance.balance.lt(requiredAmount)) {
668
- throw new Error(`Insufficient token balance. Required: ${amount}, Available: ${tokenBalance.formatted}`);
669
- }
670
- }
671
-
672
- const swapTx = await performSwap({
673
- chainId,
674
- tokenIn: tokenInAddress,
675
- tokenOut: tokenOutAddress,
676
- amountIn: formattedAmountIn,
677
- sender: this.address,
678
- recipient: recipient || this.address,
679
- slippageTolerance: slippage,
680
- deadline: deadline ? Math.floor(Date.now() / 1000) + deadline : Math.floor(Date.now() / 1000) + 1200, // 20 minutes default
681
- clientId: 'EVMChainWallet'
682
- });
683
-
684
- console.log('Kyber swap transaction prepared:', {
685
- to: swapTx.to,
686
- dataLength: swapTx.data?.length || 0,
687
- gasLimit: swapTx.gasLimit?.toString(),
688
- value: swapTx.value?.toString()
689
- });
690
-
691
- if (!isNativeIn) {
692
-
693
- const approvalResult = await checkAndApprove(
694
- wallet,
695
- tokenIn.address,
696
- swapTx.to,
697
- formattedAmountIn,
698
- undefined,
699
- undefined,
700
- this.config.confirmationNo || 1
701
- );
702
-
703
- if (approvalResult.approvalNeeded && approvalResult.approvalResult) {
704
- if (!approvalResult.approvalResult.success) {
705
- throw new Error('Token approval failed');
706
- }
707
- console.log('Token approval successful');
708
- } else if (approvalResult.approvalNeeded) {
709
- throw new Error('Token approval was needed but failed');
710
- } else {
711
- console.log('Token approval not needed - sufficient allowance');
712
- }
713
- }
714
-
715
- const result = await signSendAndConfirm(
716
- wallet,
717
- {
718
- to: swapTx.to,
719
- data: swapTx.data,
720
- value: swapTx.value || '0',
721
- gasLimit: swapTx.gasLimit
722
- },
723
- this.config.confirmationNo || 1,
724
- );
725
-
726
-
727
- return result;
728
-
729
- } catch (error) {
730
-
731
- throw new Error(`Kyber swap failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
732
- }
733
- }
734
-
735
- async getSwapQuote(
736
- tokenIn: TokenInfo,
737
- tokenOut: TokenInfo,
738
- amount: number
739
- ): Promise<{
740
- amountOut: string;
741
- priceImpact: string;
742
- gasEstimate: string;
743
- route: string[];
744
- }> {
745
-
746
- try {
747
- const chainId = (await this.connection!.getNetwork()).chainId.toString();
748
-
749
- if (!isChainSupportedByKyber(chainId)) {
750
- throw new Error(`Chain ${chainId} is not supported by KyberSwap`);
751
- }
752
-
753
- const isNativeIn = tokenIn.address === 'native' ||
754
- tokenIn.address.toLowerCase() === '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
755
- const isNativeOut = tokenOut.address === 'native' ||
756
- tokenOut.address.toLowerCase() === '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
757
-
758
- let tokenInDecimals = 18;
759
- if (!isNativeIn && tokenIn.decimals) {
760
- tokenInDecimals = tokenIn.decimals;
761
- } else if (!isNativeIn) {
762
- const tokenBalance = await this.getTokenBalance(tokenIn.address);
763
- tokenInDecimals = tokenBalance.decimal;
764
- }
765
-
766
- const { tokenInAddress, tokenOutAddress, formattedAmountIn } = prepareSwapParams(
767
- tokenIn.address,
768
- tokenOut.address,
769
- amount.toString(),
770
- tokenInDecimals,
771
- isNativeIn,
772
- isNativeOut
773
- );
774
-
775
- throw new Error("Quote functionality requires direct API integration - use the swap method for full execution");
776
-
777
- } catch (error) {
778
- console.error('Error getting swap quote:', error);
779
- throw error;
780
- }
781
- }
782
443
  async approveToken(params: {
783
444
  tokenAddress: string
784
445
  spender: string
@@ -787,303 +448,287 @@ export class EVMChainWallet extends ChainWallet<string, string, JsonRpcProvider>
787
448
  gasLimit?: string | bigint
788
449
  }): Promise<TransactionResult> {
789
450
  const signer = this.getWallet()
451
+
790
452
  const r = await approveToken(
791
453
  signer,
792
- params.tokenAddress,
793
- params.spender,
794
- params.amountRaw,
795
- params.gasLimit,
454
+ this.connection!,
455
+ params.tokenAddress as Hex,
456
+ params.spender as Hex,
457
+ BigInt(params.amountRaw),
796
458
  params.confirmations ?? this.config.confirmationNo ?? 1,
797
459
  )
798
- return {
799
- hash: r.hash,
800
- success: r.success,
801
- error: (r as any).error,
802
- }
803
- }
804
460
 
805
- async executeContractMethod(params: {
806
- contractAddress: string
807
- abi: any[]
808
- methodName: string
809
- methodParams?: any[]
810
- value?: string | bigint
811
- gasLimit?: string | bigint
812
- confirmations?: number
813
- }): Promise<TransactionResult> {
814
- const signer = this.getWallet()
815
- const r = await executeContractMethod(
816
- signer,
817
- params.contractAddress,
818
- params.abi,
819
- params.methodName,
820
- params.methodParams ?? [],
821
- params.value,
822
- )
823
461
  return {
824
462
  hash: r.hash,
825
463
  success: r.success,
826
- error: (r as any).error,
827
464
  }
828
465
  }
829
- async signMessage(message: string): Promise<string> {
830
- const signer = this.getWallet()
831
- return signer.signMessage(message)
832
- }
833
-
834
- // ============================================
835
- // Savings Pocket Methods
836
- // ============================================
837
-
838
- /**
839
- * Get or create the SavingsManager instance (lazy initialization)
840
- * @private
841
- */
842
- private getSavingsManager(): SavingsManager {
843
- if (!this.savingsManager) {
844
- // Create a VM instance from the current wallet's seed
845
- // We need to get the seed from the privateKey
846
- // For now, we'll create an EVMVM from the wallet
847
- // Note: This requires access to the seed, which we'll need to handle
848
- throw new Error(
849
- "SavingsManager requires access to the seed phrase. " +
850
- "Please initialize the wallet with a seed phrase or mnemonic to use savings features."
851
- );
852
- }
853
- return this.savingsManager;
854
- }
855
-
856
- /**
857
- * Initialize savings functionality with a seed phrase
858
- *
859
- * This must be called before using any savings methods.
860
- * The seed is used to derive savings accounts using BIP-44.
861
- *
862
- * @param seed - The wallet seed (hex string)
863
- *
864
- * @example
865
- * const seed = VM.mnemonicToSeed(mnemonic);
866
- * wallet.initializeSavings(seed);
867
- */
868
- initializeSavings(seed: string): void {
869
- const vm = new EVMVM(seed);
870
- this.savingsManager = new SavingsManager(vm);
871
- }
872
-
873
- /**
874
- * Derive a new savings account from BIP-44 account index
875
- *
876
- * @param accountIndex - The BIP-44 account index (1+ for savings, 0 is main wallet)
877
- * @returns SavingsAccount with derived address and private key
878
- *
879
- * @example
880
- * const savingsAccount1 = wallet.deriveSavingsAccount(1); // m/44'/60'/1'/0/0
881
- * const savingsAccount2 = wallet.deriveSavingsAccount(2); // m/44'/60'/2'/0/0
882
- */
883
- deriveSavingsAccount(accountIndex: number): SavingsAccount {
884
- return this.getSavingsManager().createSavingsAccount(accountIndex);
885
- }
886
-
887
- /**
888
- * Transfer native tokens to a savings account
889
- *
890
- * Security: Always derives the destination address from accountIndex.
891
- *
892
- * @param accountIndex - Savings account index to deposit to
893
- * @param amount - Amount in ether units (e.g., "1.5" for 1.5 ETH)
894
- * @param options - Optional security and priority settings
895
- * @returns Transaction result
896
- *
897
- * @example
898
- * const result = await wallet.transferToSavings(1, "1.5"); // Send 1.5 ETH to savings account 1
899
- */
900
- async transferToSavings(
901
- accountIndex: number,
902
- amount: string,
903
- options?: TransferToSavingsOptions
904
- ): Promise<TransactionResult> {
905
- const manager = this.getSavingsManager();
906
-
907
- // Build transaction using derived address
908
- const amountWei = parseEther(amount);
909
- const txParams = manager.buildDepositTransaction(accountIndex, amountWei, options);
910
-
911
- // Execute using existing transferNative method
912
- return await this.transferNative(txParams.to, Number(amount));
913
- }
914
-
915
- /**
916
- * Withdraw native tokens from a savings account
917
- *
918
- * Security: Uses the derived private key for signing.
919
- *
920
- * @param accountIndex - Savings account index to withdraw from
921
- * @param to - Destination address
922
- * @param amount - Amount in ether units (e.g., "0.5" for 0.5 ETH)
923
- * @param options - Optional security and priority settings
924
- * @returns Transaction result
925
- *
926
- * @example
927
- * const result = await wallet.withdrawFromSavings(1, destinationAddress, "0.5");
928
- */
929
- async withdrawFromSavings(
930
- accountIndex: number,
931
- to: string,
932
- amount: string,
933
- options?: WithdrawFromSavingsOptions
934
- ): Promise<TransactionResult> {
935
- const manager = this.getSavingsManager();
936
-
937
- // Build withdrawal transaction
938
- const amountWei = parseEther(amount);
939
- const withdrawalParams = manager.buildWithdrawalTransaction(
940
- accountIndex,
941
- to as Hex,
942
- amountWei,
943
- options
944
- );
945
-
946
- // Create a temporary wallet with the savings account private key
947
- const savingsWallet = new Wallet(withdrawalParams.privateKey, this.connection);
948
-
949
- // Send transaction using the savings wallet
950
- const tx = await savingsWallet.sendTransaction({
951
- to: withdrawalParams.to,
952
- value: withdrawalParams.value
953
- });
954
-
955
- const receipt = await tx.wait(this.config.confirmationNo || 1);
956
-
957
- return {
958
- success: receipt?.status === 1,
959
- hash: receipt?.hash || tx.hash
960
- };
961
- }
962
466
 
963
- /**
964
- * Verify a stored savings address matches the derived address
965
- *
966
- * Security: Prevents database tampering attacks.
967
- *
968
- * @param accountIndex - The account index to verify
969
- * @param storedAddress - The address from storage/database
970
- * @returns Verification result
971
- *
972
- * @example
973
- * const result = wallet.verifySavingsAddress(1, storedAddress);
974
- * if (!result.isValid) {
975
- * console.error("Security alert: Address tampering detected!");
976
- * }
977
- */
978
- verifySavingsAddress(accountIndex: number, storedAddress: Hex): AddressVerificationResult {
979
- return this.getSavingsManager().verifySavingsAddress(accountIndex, storedAddress);
980
- }
981
467
 
982
- /**
983
- * Audit multiple savings addresses at once
984
- *
985
- * @param addresses - Map of accountIndex to stored address
986
- * @returns Audit result with summary and details
987
- *
988
- * @example
989
- * const addresses = new Map([[1, "0xabc..."], [2, "0xdef..."]]);
990
- * const audit = wallet.auditSavingsAddresses(addresses);
991
- * console.log(`Valid: ${audit.valid}/${audit.total}`);
992
- */
993
- auditSavingsAddresses(addresses: Map<number, Hex>): SavingsAuditResult {
994
- return this.getSavingsManager().auditSavingsAddresses(addresses);
995
- }
996
468
 
997
- /**
998
- * Get savings account information without exposing private key
999
- *
1000
- * @param accountIndex - The account index
1001
- * @returns Public account information (address and derivation path)
1002
- *
1003
- * @example
1004
- * const info = wallet.getSavingsAccountInfo(1);
1005
- * console.log(`Address: ${info.address}`);
1006
- * console.log(`Path: ${info.derivationPath}`); // m/44'/60'/1'/0/0
1007
- */
1008
- getSavingsAccountInfo(accountIndex: number): Omit<SavingsAccount, 'privateKey'> {
1009
- return this.getSavingsManager().getSavingsAccountInfo(accountIndex);
1010
- }
1011
-
1012
- // ============================================
1013
- // Smart Savings Methods (EIP-7702)
1014
- // ============================================
1015
-
1016
- /**
1017
- * Get or create the SmartSavingsManager instance (lazy initialization)
1018
- * @private
1019
- */
1020
- private getSmartSavingsManager(): SmartSavingsManager {
1021
- if (!this.smartSavingsManager) {
1022
- this.smartSavingsManager = new SmartSavingsManager(this.config);
1023
- }
1024
- return this.smartSavingsManager;
1025
- }
469
+ async signMessage(message: string): Promise<string> {
1026
470
 
1027
- /**
1028
- * Upgrade a savings account to a smart account with EIP-7702 delegation
1029
- *
1030
- * This enables advanced features:
1031
- * - Lock modules for time-locked savings
1032
- * - Hooks for spend & save
1033
- * - Session keys for periodic savings
1034
- * - Sponsored transactions via paymaster
1035
- *
1036
- * @param accountIndex - The savings account index to upgrade
1037
- * @param options - Optional smart wallet configuration
1038
- * @param autoInitialize - Whether to initialize the smart wallet (default: false)
1039
- * @returns SmartSavingsAccount with EVMSmartWallet instance
1040
- *
1041
- * @example
1042
- * // Upgrade without initialization
1043
- * const smartSavings = await wallet.upgradeSavingsToSmartAccount(1);
1044
- * await smartSavings.smartWallet.initialize(); // Initialize separately
1045
- *
1046
- * @example
1047
- * // Upgrade and initialize in one step
1048
- * const smartSavings = await wallet.upgradeSavingsToSmartAccount(1, {}, true);
1049
- * // Ready to use immediately
1050
- */
1051
- async upgradeSavingsToSmartAccount(
1052
- accountIndex: number,
1053
- options?: SmartWalletOptions,
1054
- autoInitialize: boolean = false
1055
- ): Promise<SmartSavingsAccount> {
1056
- const manager = this.getSmartSavingsManager();
1057
-
1058
- // First derive the basic savings account
1059
- const basicSavings = this.deriveSavingsAccount(accountIndex);
1060
-
1061
- // Then upgrade to smart account
1062
- if (autoInitialize) {
1063
- return await manager.upgradeSavingsAndInitialize(basicSavings, options);
1064
- } else {
1065
- return await manager.upgradeSavingsToSmartAccount(basicSavings, options);
1066
- }
1067
- }
471
+ const signer = this.wallet
1068
472
 
1069
- /**
1070
- * Check if smart savings is supported on this chain
1071
- *
1072
- * @returns true if chain supports Account Abstraction
1073
- *
1074
- * @example
1075
- * if (wallet.isSmartSavingsSupported()) {
1076
- * const smartSavings = await wallet.upgradeSavingsToSmartAccount(1);
1077
- * } else {
1078
- * console.log("This chain doesn't support smart savings");
1079
- * }
1080
- */
1081
- isSmartSavingsSupported(): boolean {
1082
- try {
1083
- const manager = this.getSmartSavingsManager();
1084
- return manager.canUpgradeToSmartAccount();
1085
- } catch (error) {
1086
- return false;
473
+ if (!signer.account) {
474
+ throw new Error("Account is required for signing, signer.account from this.wallet is undefined")
1087
475
  }
1088
- }
476
+ return signer.signMessage({ message, account: signer.account?.address })
477
+ }
478
+
479
+ // // ============================================
480
+ // // Savings Pocket Methods
481
+ // // ============================================
482
+
483
+ // /**
484
+ // * Get or create the SavingsManager instance (lazy initialization)
485
+ // * @private
486
+ // */
487
+ // private getSavingsManager(): SavingsManager {
488
+ // if (!this.savingsManager) {
489
+ // // Create a VM instance from the current wallet's seed
490
+ // // We need to get the seed from the privateKey
491
+ // // For now, we'll create an EVMVM from the wallet
492
+ // // Note: This requires access to the seed, which we'll need to handle
493
+ // throw new Error(
494
+ // "SavingsManager requires access to the seed phrase. " +
495
+ // "Please initialize the wallet with a seed phrase or mnemonic to use savings features."
496
+ // );
497
+ // }
498
+ // return this.savingsManager;
499
+ // }
500
+
501
+ // /**
502
+ // * Initialize savings functionality with a seed phrase
503
+ // *
504
+ // * This must be called before using any savings methods.
505
+ // * The seed is used to derive savings accounts using BIP-44.
506
+ // *
507
+ // * @param seed - The wallet seed (hex string)
508
+ // *
509
+ // * @example
510
+ // * const seed = VM.mnemonicToSeed(mnemonic);
511
+ // * wallet.initializeSavings(seed);
512
+ // */
513
+ // initializeSavings(seed: string): void {
514
+ // const vm = new EVMVM(seed);
515
+ // this.savingsManager = new SavingsManager(vm);
516
+ // }
517
+
518
+ // /**
519
+ // * Derive a new savings account from BIP-44 account index
520
+ // *
521
+ // * @param accountIndex - The BIP-44 account index (1+ for savings, 0 is main wallet)
522
+ // * @returns SavingsAccount with derived address and private key
523
+ // *
524
+ // * @example
525
+ // * const savingsAccount1 = wallet.deriveSavingsAccount(1); // m/44'/60'/1'/0/0
526
+ // * const savingsAccount2 = wallet.deriveSavingsAccount(2); // m/44'/60'/2'/0/0
527
+ // */
528
+ // deriveSavingsAccount(accountIndex: number): SavingsAccount {
529
+ // return this.getSavingsManager().createSavingsAccount(accountIndex);
530
+ // }
531
+
532
+ // /**
533
+ // * Transfer native tokens to a savings account
534
+ // *
535
+ // * Security: Always derives the destination address from accountIndex.
536
+ // *
537
+ // * @param accountIndex - Savings account index to deposit to
538
+ // * @param amount - Amount in ether units (e.g., "1.5" for 1.5 ETH)
539
+ // * @param options - Optional security and priority settings
540
+ // * @returns Transaction result
541
+ // *
542
+ // * @example
543
+ // * const result = await wallet.transferToSavings(1, "1.5"); // Send 1.5 ETH to savings account 1
544
+ // */
545
+ // async transferToSavings(
546
+ // accountIndex: number,
547
+ // amount: string,
548
+ // options?: TransferToSavingsOptions
549
+ // ): Promise<TransactionResult> {
550
+ // const manager = this.getSavingsManager();
551
+
552
+ // // Build transaction using derived address
553
+ // const amountWei = parseEther(amount);
554
+ // const txParams = manager.buildDepositTransaction(accountIndex, amountWei, options);
555
+
556
+ // // Execute using existing transferNative method
557
+ // return await this.transferNative(txParams.to, Number(amount));
558
+ // }
559
+
560
+ // // /**
561
+ // // * Withdraw native tokens from a savings account
562
+ // // *
563
+ // // * Security: Uses the derived private key for signing.
564
+ // // *
565
+ // // * @param accountIndex - Savings account index to withdraw from
566
+ // // * @param to - Destination address
567
+ // // * @param amount - Amount in ether units (e.g., "0.5" for 0.5 ETH)
568
+ // // * @param options - Optional security and priority settings
569
+ // // * @returns Transaction result
570
+ // // *
571
+ // // * @example
572
+ // // * const result = await wallet.withdrawFromSavings(1, destinationAddress, "0.5");
573
+ // // */
574
+ // // async withdrawFromSavings(
575
+ // // accountIndex: number,
576
+ // // to: string,
577
+ // // amount: string,
578
+ // // options?: WithdrawFromSavingsOptions
579
+ // // ): Promise<TransactionResult> {
580
+ // // const manager = this.getSavingsManager();
581
+
582
+ // // // Build withdrawal transaction
583
+ // // const amountWei = parseEther(amount);
584
+ // // const withdrawalParams = manager.buildWithdrawalTransaction(
585
+ // // accountIndex,
586
+ // // to as Hex,
587
+ // // amountWei,
588
+ // // options
589
+ // // );
590
+
591
+ // // // Create a temporary wallet with the savings account private key
592
+ // // const savingsWallet = new Wallet(withdrawalParams.privateKey, this.connection);
593
+
594
+ // // // Send transaction using the savings wallet
595
+ // // const tx = await savingsWallet.sendTransaction({
596
+ // // to: withdrawalParams.to,
597
+ // // value: withdrawalParams.value
598
+ // // });
599
+
600
+ // // const receipt = await tx.wait(this.config.confirmationNo || 1);
601
+
602
+ // // return {
603
+ // // success: receipt?.status === 1,
604
+ // // hash: receipt?.hash || tx.hash
605
+ // // };
606
+ // // }
607
+
608
+ // /**
609
+ // * Verify a stored savings address matches the derived address
610
+ // *
611
+ // * Security: Prevents database tampering attacks.
612
+ // *
613
+ // * @param accountIndex - The account index to verify
614
+ // * @param storedAddress - The address from storage/database
615
+ // * @returns Verification result
616
+ // *
617
+ // * @example
618
+ // * const result = wallet.verifySavingsAddress(1, storedAddress);
619
+ // * if (!result.isValid) {
620
+ // * console.error("Security alert: Address tampering detected!");
621
+ // * }
622
+ // */
623
+ // verifySavingsAddress(accountIndex: number, storedAddress: Hex): AddressVerificationResult {
624
+ // return this.getSavingsManager().verifySavingsAddress(accountIndex, storedAddress);
625
+ // }
626
+
627
+ // /**
628
+ // * Audit multiple savings addresses at once
629
+ // *
630
+ // * @param addresses - Map of accountIndex to stored address
631
+ // * @returns Audit result with summary and details
632
+ // *
633
+ // * @example
634
+ // * const addresses = new Map([[1, "0xabc..."], [2, "0xdef..."]]);
635
+ // * const audit = wallet.auditSavingsAddresses(addresses);
636
+ // * console.log(`Valid: ${audit.valid}/${audit.total}`);
637
+ // */
638
+ // auditSavingsAddresses(addresses: Map<number, Hex>): SavingsAuditResult {
639
+ // return this.getSavingsManager().auditSavingsAddresses(addresses);
640
+ // }
641
+
642
+ // /**
643
+ // * Get savings account information without exposing private key
644
+ // *
645
+ // * @param accountIndex - The account index
646
+ // * @returns Public account information (address and derivation path)
647
+ // *
648
+ // * @example
649
+ // * const info = wallet.getSavingsAccountInfo(1);
650
+ // * console.log(`Address: ${info.address}`);
651
+ // * console.log(`Path: ${info.derivationPath}`); // m/44'/60'/1'/0/0
652
+ // */
653
+ // getSavingsAccountInfo(accountIndex: number): Omit<SavingsAccount, 'privateKey'> {
654
+ // return this.getSavingsManager().getSavingsAccountInfo(accountIndex);
655
+ // }
656
+
657
+ // // ============================================
658
+ // // Smart Savings Methods (EIP-7702)
659
+ // // ============================================
660
+
661
+ // /**
662
+ // * Get or create the SmartSavingsManager instance (lazy initialization)
663
+ // * @private
664
+ // */
665
+ // private getSmartSavingsManager(): SmartSavingsManager {
666
+ // if (!this.smartSavingsManager) {
667
+ // this.smartSavingsManager = new SmartSavingsManager(this.config);
668
+ // }
669
+ // return this.smartSavingsManager;
670
+ // }
671
+
672
+ // /**
673
+ // * Upgrade a savings account to a smart account with EIP-7702 delegation
674
+ // *
675
+ // * This enables advanced features:
676
+ // * - Lock modules for time-locked savings
677
+ // * - Hooks for spend & save
678
+ // * - Session keys for periodic savings
679
+ // * - Sponsored transactions via paymaster
680
+ // *
681
+ // * @param accountIndex - The savings account index to upgrade
682
+ // * @param options - Optional smart wallet configuration
683
+ // * @param autoInitialize - Whether to initialize the smart wallet (default: false)
684
+ // * @returns SmartSavingsAccount with EVMSmartWallet instance
685
+ // *
686
+ // * @example
687
+ // * // Upgrade without initialization
688
+ // * const smartSavings = await wallet.upgradeSavingsToSmartAccount(1);
689
+ // * await smartSavings.smartWallet.initialize(); // Initialize separately
690
+ // *
691
+ // * @example
692
+ // * // Upgrade and initialize in one step
693
+ // * const smartSavings = await wallet.upgradeSavingsToSmartAccount(1, {}, true);
694
+ // * // Ready to use immediately
695
+ // */
696
+ // async upgradeSavingsToSmartAccount(
697
+ // accountIndex: number,
698
+ // options?: SmartWalletOptions,
699
+ // autoInitialize: boolean = false
700
+ // ): Promise<SmartSavingsAccount> {
701
+ // const manager = this.getSmartSavingsManager();
702
+
703
+ // // First derive the basic savings account
704
+ // const basicSavings = this.deriveSavingsAccount(accountIndex);
705
+
706
+ // // Then upgrade to smart account
707
+ // if (autoInitialize) {
708
+ // return await manager.upgradeSavingsAndInitialize(basicSavings, options);
709
+ // } else {
710
+ // return await manager.upgradeSavingsToSmartAccount(basicSavings, options);
711
+ // }
712
+ // }
713
+
714
+ // /**
715
+ // * Check if smart savings is supported on this chain
716
+ // *
717
+ // * @returns true if chain supports Account Abstraction
718
+ // *
719
+ // * @example
720
+ // * if (wallet.isSmartSavingsSupported()) {
721
+ // * const smartSavings = await wallet.upgradeSavingsToSmartAccount(1);
722
+ // * } else {
723
+ // * console.log("This chain doesn't support smart savings");
724
+ // * }
725
+ // */
726
+ // isSmartSavingsSupported(): boolean {
727
+ // try {
728
+ // const manager = this.getSmartSavingsManager();
729
+ // return manager.canUpgradeToSmartAccount();
730
+ // } catch (error) {
731
+ // return false;
732
+ // }
733
+ // }
1089
734
  }