@circle-fin/adapter-ethers-v6 1.2.0 → 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/CHANGELOG.md +15 -0
- package/index.cjs +251 -72
- package/index.d.ts +85 -7
- package/index.mjs +251 -72
- package/package.json +16 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @circle-fin/adapter-ethers-v6
|
|
2
2
|
|
|
3
|
+
## 1.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Add `native.balanceOf` action to query native token balances (ETH, SOL, etc.)
|
|
8
|
+
- Added `NativeActionMap` with `balanceOf` action to check native token balance for any wallet address
|
|
9
|
+
- Added abstract `readNativeBalance(address, chain)` method to base adapters
|
|
10
|
+
- Implemented `readNativeBalance` in all concrete adapters (Viem, Ethers, Solana, SolanaKit)
|
|
11
|
+
- Registered `native.balanceOf` action handlers for EVM and Solana chains
|
|
12
|
+
- Balance reads are gas-free operations returning balance as string (wei for EVM, lamports for Solana)
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- **Faster balance and allowance checks**: Read-only operations like checking token balances or allowances no longer require wallet network switching. This means no wallet popups asking for permission to switch networks, resulting in quicker responses and a smoother user experience.
|
|
17
|
+
|
|
3
18
|
## 1.2.0
|
|
4
19
|
|
|
5
20
|
### Minor Changes
|
package/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright (c)
|
|
2
|
+
* Copyright (c) 2026, Circle Internet Group, Inc. All rights reserved.
|
|
3
3
|
*
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*
|
|
@@ -623,7 +623,7 @@ function createValidationErrorFromZod(zodError, context) {
|
|
|
623
623
|
*
|
|
624
624
|
* @param chain - The blockchain network where the balance check failed
|
|
625
625
|
* @param token - The token symbol (e.g., 'USDC', 'ETH')
|
|
626
|
-
* @param
|
|
626
|
+
* @param trace - Optional trace context to include in error (can include rawError and additional debugging data)
|
|
627
627
|
* @returns KitError with insufficient token balance details
|
|
628
628
|
*
|
|
629
629
|
* @example
|
|
@@ -636,24 +636,28 @@ function createValidationErrorFromZod(zodError, context) {
|
|
|
636
636
|
*
|
|
637
637
|
* @example
|
|
638
638
|
* ```typescript
|
|
639
|
-
* // With
|
|
639
|
+
* // With trace context for debugging
|
|
640
640
|
* try {
|
|
641
641
|
* await transfer(...)
|
|
642
642
|
* } catch (error) {
|
|
643
|
-
* throw createInsufficientTokenBalanceError('Base', 'USDC',
|
|
643
|
+
* throw createInsufficientTokenBalanceError('Base', 'USDC', {
|
|
644
|
+
* rawError: error,
|
|
645
|
+
* balance: '1000000',
|
|
646
|
+
* amount: '5000000',
|
|
647
|
+
* })
|
|
644
648
|
* }
|
|
645
649
|
* ```
|
|
646
650
|
*/
|
|
647
|
-
function createInsufficientTokenBalanceError(chain, token,
|
|
651
|
+
function createInsufficientTokenBalanceError(chain, token, trace) {
|
|
648
652
|
return new KitError({
|
|
649
653
|
...BalanceError.INSUFFICIENT_TOKEN,
|
|
650
654
|
recoverability: 'FATAL',
|
|
651
655
|
message: `Insufficient ${token} balance on ${chain}`,
|
|
652
656
|
cause: {
|
|
653
657
|
trace: {
|
|
658
|
+
...trace,
|
|
654
659
|
chain,
|
|
655
660
|
token,
|
|
656
|
-
rawError,
|
|
657
661
|
},
|
|
658
662
|
},
|
|
659
663
|
});
|
|
@@ -666,7 +670,7 @@ function createInsufficientTokenBalanceError(chain, token, rawError) {
|
|
|
666
670
|
* as it requires user intervention to add gas funds.
|
|
667
671
|
*
|
|
668
672
|
* @param chain - The blockchain network where the gas check failed
|
|
669
|
-
* @param
|
|
673
|
+
* @param trace - Optional trace context to include in error (can include rawError and additional debugging data)
|
|
670
674
|
* @returns KitError with insufficient gas details
|
|
671
675
|
*
|
|
672
676
|
* @example
|
|
@@ -676,16 +680,27 @@ function createInsufficientTokenBalanceError(chain, token, rawError) {
|
|
|
676
680
|
* throw createInsufficientGasError('Ethereum')
|
|
677
681
|
* // Message: "Insufficient gas funds on Ethereum"
|
|
678
682
|
* ```
|
|
683
|
+
*
|
|
684
|
+
* @example
|
|
685
|
+
* ```typescript
|
|
686
|
+
* // With trace context for debugging
|
|
687
|
+
* throw createInsufficientGasError('Ethereum', {
|
|
688
|
+
* rawError: error,
|
|
689
|
+
* gasRequired: '21000',
|
|
690
|
+
* gasAvailable: '10000',
|
|
691
|
+
* walletAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
|
|
692
|
+
* })
|
|
693
|
+
* ```
|
|
679
694
|
*/
|
|
680
|
-
function createInsufficientGasError(chain,
|
|
695
|
+
function createInsufficientGasError(chain, trace) {
|
|
681
696
|
return new KitError({
|
|
682
697
|
...BalanceError.INSUFFICIENT_GAS,
|
|
683
698
|
recoverability: 'FATAL',
|
|
684
699
|
message: `Insufficient gas funds on ${chain}`,
|
|
685
700
|
cause: {
|
|
686
701
|
trace: {
|
|
702
|
+
...trace,
|
|
687
703
|
chain,
|
|
688
|
-
rawError,
|
|
689
704
|
},
|
|
690
705
|
},
|
|
691
706
|
});
|
|
@@ -700,7 +715,7 @@ function createInsufficientGasError(chain, rawError) {
|
|
|
700
715
|
*
|
|
701
716
|
* @param chain - The blockchain network where the simulation failed
|
|
702
717
|
* @param reason - The reason for simulation failure (e.g., revert message)
|
|
703
|
-
* @param
|
|
718
|
+
* @param trace - Optional trace context to include in error (can include rawError and additional debugging data)
|
|
704
719
|
* @returns KitError with simulation failure details
|
|
705
720
|
*
|
|
706
721
|
* @example
|
|
@@ -710,17 +725,27 @@ function createInsufficientGasError(chain, rawError) {
|
|
|
710
725
|
* throw createSimulationFailedError('Ethereum', 'ERC20: insufficient allowance')
|
|
711
726
|
* // Message: "Simulation failed on Ethereum: ERC20: insufficient allowance"
|
|
712
727
|
* ```
|
|
728
|
+
*
|
|
729
|
+
* @example
|
|
730
|
+
* ```typescript
|
|
731
|
+
* // With trace context for debugging
|
|
732
|
+
* throw createSimulationFailedError('Ethereum', 'ERC20: insufficient allowance', {
|
|
733
|
+
* rawError: error,
|
|
734
|
+
* txHash: '0x1234...',
|
|
735
|
+
* gasLimit: '21000',
|
|
736
|
+
* })
|
|
737
|
+
* ```
|
|
713
738
|
*/
|
|
714
|
-
function createSimulationFailedError(chain, reason,
|
|
739
|
+
function createSimulationFailedError(chain, reason, trace) {
|
|
715
740
|
return new KitError({
|
|
716
741
|
...OnchainError.SIMULATION_FAILED,
|
|
717
742
|
recoverability: 'FATAL',
|
|
718
743
|
message: `Simulation failed on ${chain}: ${reason}`,
|
|
719
744
|
cause: {
|
|
720
745
|
trace: {
|
|
746
|
+
...trace,
|
|
721
747
|
chain,
|
|
722
748
|
reason,
|
|
723
|
-
rawError,
|
|
724
749
|
},
|
|
725
750
|
},
|
|
726
751
|
});
|
|
@@ -734,7 +759,7 @@ function createSimulationFailedError(chain, reason, rawError) {
|
|
|
734
759
|
*
|
|
735
760
|
* @param chain - The blockchain network where the transaction reverted
|
|
736
761
|
* @param reason - The reason for the revert (e.g., revert message)
|
|
737
|
-
* @param
|
|
762
|
+
* @param trace - Optional trace context to include in error (can include rawError and additional debugging data)
|
|
738
763
|
* @returns KitError with transaction revert details
|
|
739
764
|
*
|
|
740
765
|
* @example
|
|
@@ -744,17 +769,27 @@ function createSimulationFailedError(chain, reason, rawError) {
|
|
|
744
769
|
* throw createTransactionRevertedError('Base', 'Slippage exceeded')
|
|
745
770
|
* // Message: "Transaction reverted on Base: Slippage exceeded"
|
|
746
771
|
* ```
|
|
772
|
+
*
|
|
773
|
+
* @example
|
|
774
|
+
* ```typescript
|
|
775
|
+
* // With trace context for debugging
|
|
776
|
+
* throw createTransactionRevertedError('Base', 'Slippage exceeded', {
|
|
777
|
+
* rawError: error,
|
|
778
|
+
* txHash: '0xabc...',
|
|
779
|
+
* blockNumber: '12345',
|
|
780
|
+
* })
|
|
781
|
+
* ```
|
|
747
782
|
*/
|
|
748
|
-
function createTransactionRevertedError(chain, reason,
|
|
783
|
+
function createTransactionRevertedError(chain, reason, trace) {
|
|
749
784
|
return new KitError({
|
|
750
785
|
...OnchainError.TRANSACTION_REVERTED,
|
|
751
786
|
recoverability: 'FATAL',
|
|
752
787
|
message: `Transaction reverted on ${chain}: ${reason}`,
|
|
753
788
|
cause: {
|
|
754
789
|
trace: {
|
|
790
|
+
...trace,
|
|
755
791
|
chain,
|
|
756
792
|
reason,
|
|
757
|
-
rawError,
|
|
758
793
|
},
|
|
759
794
|
},
|
|
760
795
|
});
|
|
@@ -766,7 +801,7 @@ function createTransactionRevertedError(chain, reason, rawError) {
|
|
|
766
801
|
* The error is FATAL as it requires adjusting gas limits or transaction logic.
|
|
767
802
|
*
|
|
768
803
|
* @param chain - The blockchain network where the transaction ran out of gas
|
|
769
|
-
* @param
|
|
804
|
+
* @param trace - Optional trace context to include in error (can include rawError and additional debugging data)
|
|
770
805
|
* @returns KitError with out of gas details
|
|
771
806
|
*
|
|
772
807
|
* @example
|
|
@@ -776,16 +811,26 @@ function createTransactionRevertedError(chain, reason, rawError) {
|
|
|
776
811
|
* throw createOutOfGasError('Polygon')
|
|
777
812
|
* // Message: "Transaction ran out of gas on Polygon"
|
|
778
813
|
* ```
|
|
814
|
+
*
|
|
815
|
+
* @example
|
|
816
|
+
* ```typescript
|
|
817
|
+
* // With trace context for debugging
|
|
818
|
+
* throw createOutOfGasError('Polygon', {
|
|
819
|
+
* rawError: error,
|
|
820
|
+
* gasUsed: '50000',
|
|
821
|
+
* gasLimit: '45000',
|
|
822
|
+
* })
|
|
823
|
+
* ```
|
|
779
824
|
*/
|
|
780
|
-
function createOutOfGasError(chain,
|
|
825
|
+
function createOutOfGasError(chain, trace) {
|
|
781
826
|
return new KitError({
|
|
782
827
|
...OnchainError.OUT_OF_GAS,
|
|
783
828
|
recoverability: 'FATAL',
|
|
784
829
|
message: `Transaction ran out of gas on ${chain}`,
|
|
785
830
|
cause: {
|
|
786
831
|
trace: {
|
|
832
|
+
...trace,
|
|
787
833
|
chain,
|
|
788
|
-
rawError,
|
|
789
834
|
},
|
|
790
835
|
},
|
|
791
836
|
});
|
|
@@ -798,7 +843,7 @@ function createOutOfGasError(chain, rawError) {
|
|
|
798
843
|
* or is unavailable. The error is RETRYABLE as RPC issues are often temporary.
|
|
799
844
|
*
|
|
800
845
|
* @param chain - The blockchain network where the RPC error occurred
|
|
801
|
-
* @param
|
|
846
|
+
* @param trace - Optional trace context to include in error (can include rawError and additional debugging data)
|
|
802
847
|
* @returns KitError with RPC endpoint error details
|
|
803
848
|
*
|
|
804
849
|
* @example
|
|
@@ -808,16 +853,26 @@ function createOutOfGasError(chain, rawError) {
|
|
|
808
853
|
* throw createRpcEndpointError('Ethereum')
|
|
809
854
|
* // Message: "RPC endpoint error on Ethereum"
|
|
810
855
|
* ```
|
|
856
|
+
*
|
|
857
|
+
* @example
|
|
858
|
+
* ```typescript
|
|
859
|
+
* // With trace context for debugging
|
|
860
|
+
* throw createRpcEndpointError('Ethereum', {
|
|
861
|
+
* rawError: error,
|
|
862
|
+
* endpoint: 'https://mainnet.infura.io/v3/...',
|
|
863
|
+
* statusCode: 429,
|
|
864
|
+
* })
|
|
865
|
+
* ```
|
|
811
866
|
*/
|
|
812
|
-
function createRpcEndpointError(chain,
|
|
867
|
+
function createRpcEndpointError(chain, trace) {
|
|
813
868
|
return new KitError({
|
|
814
869
|
...RpcError.ENDPOINT_ERROR,
|
|
815
870
|
recoverability: 'RETRYABLE',
|
|
816
871
|
message: `RPC endpoint error on ${chain}`,
|
|
817
872
|
cause: {
|
|
818
873
|
trace: {
|
|
874
|
+
...trace,
|
|
819
875
|
chain,
|
|
820
|
-
rawError,
|
|
821
876
|
},
|
|
822
877
|
},
|
|
823
878
|
});
|
|
@@ -831,7 +886,7 @@ function createRpcEndpointError(chain, rawError) {
|
|
|
831
886
|
* often temporary.
|
|
832
887
|
*
|
|
833
888
|
* @param chain - The blockchain network where the connection failed
|
|
834
|
-
* @param
|
|
889
|
+
* @param trace - Optional trace context to include in error (can include rawError and additional debugging data)
|
|
835
890
|
* @returns KitError with network connection error details
|
|
836
891
|
*
|
|
837
892
|
* @example
|
|
@@ -841,16 +896,26 @@ function createRpcEndpointError(chain, rawError) {
|
|
|
841
896
|
* throw createNetworkConnectionError('Ethereum')
|
|
842
897
|
* // Message: "Network connection failed for Ethereum"
|
|
843
898
|
* ```
|
|
899
|
+
*
|
|
900
|
+
* @example
|
|
901
|
+
* ```typescript
|
|
902
|
+
* // With trace context for debugging
|
|
903
|
+
* throw createNetworkConnectionError('Ethereum', {
|
|
904
|
+
* rawError: error,
|
|
905
|
+
* endpoint: 'https://eth-mainnet.g.alchemy.com/v2/...',
|
|
906
|
+
* retryCount: 3,
|
|
907
|
+
* })
|
|
908
|
+
* ```
|
|
844
909
|
*/
|
|
845
|
-
function createNetworkConnectionError(chain,
|
|
910
|
+
function createNetworkConnectionError(chain, trace) {
|
|
846
911
|
return new KitError({
|
|
847
912
|
...NetworkError.CONNECTION_FAILED,
|
|
848
913
|
recoverability: 'RETRYABLE',
|
|
849
914
|
message: `Network connection failed for ${chain}`,
|
|
850
915
|
cause: {
|
|
851
916
|
trace: {
|
|
917
|
+
...trace,
|
|
852
918
|
chain,
|
|
853
|
-
rawError,
|
|
854
919
|
},
|
|
855
920
|
},
|
|
856
921
|
});
|
|
@@ -905,7 +970,9 @@ function parseBlockchainError(error, context) {
|
|
|
905
970
|
// Pattern 1: Insufficient balance errors
|
|
906
971
|
// Matches balance-related errors from ERC20 contracts, native transfers, and Solana programs
|
|
907
972
|
if (/transfer amount exceeds balance|insufficient (balance|funds)|burn amount exceeded/i.test(msg)) {
|
|
908
|
-
return createInsufficientTokenBalanceError(context.chain, token,
|
|
973
|
+
return createInsufficientTokenBalanceError(context.chain, token, {
|
|
974
|
+
rawError: error,
|
|
975
|
+
});
|
|
909
976
|
}
|
|
910
977
|
// Pattern 2: Simulation and execution reverts
|
|
911
978
|
// Matches contract revert errors and simulation failures
|
|
@@ -915,46 +982,50 @@ function parseBlockchainError(error, context) {
|
|
|
915
982
|
// "simulation failed" or "eth_call" indicates pre-flight simulation
|
|
916
983
|
// "transaction failed" or context.operation === 'transaction' indicates post-execution
|
|
917
984
|
if (/simulation failed/i.test(msg) || context.operation === 'simulation') {
|
|
918
|
-
return createSimulationFailedError(context.chain, reason,
|
|
985
|
+
return createSimulationFailedError(context.chain, reason, {
|
|
986
|
+
rawError: error,
|
|
987
|
+
});
|
|
919
988
|
}
|
|
920
989
|
// Transaction execution failures or reverts
|
|
921
|
-
return createTransactionRevertedError(context.chain, reason,
|
|
990
|
+
return createTransactionRevertedError(context.chain, reason, {
|
|
991
|
+
rawError: error,
|
|
992
|
+
});
|
|
922
993
|
}
|
|
923
994
|
// Pattern 3: Gas-related errors
|
|
924
995
|
// Matches gas estimation failures and gas exhaustion
|
|
925
996
|
// Check specific patterns first, then generic "gas" patterns
|
|
926
997
|
// Gas estimation failures are RPC issues
|
|
927
998
|
if (/gas estimation failed|cannot estimate gas/i.test(msg)) {
|
|
928
|
-
return createRpcEndpointError(context.chain, error);
|
|
999
|
+
return createRpcEndpointError(context.chain, { rawError: error });
|
|
929
1000
|
}
|
|
930
1001
|
// Gas exhaustion errors
|
|
931
1002
|
// Use specific patterns without wildcards to avoid ReDoS
|
|
932
1003
|
if (/out of gas|gas limit exceeded|exceeds block gas limit/i.test(msg)) {
|
|
933
|
-
return createOutOfGasError(context.chain, error);
|
|
1004
|
+
return createOutOfGasError(context.chain, { rawError: error });
|
|
934
1005
|
}
|
|
935
1006
|
// Insufficient funds for gas
|
|
936
1007
|
if (/insufficient funds for gas/i.test(msg)) {
|
|
937
|
-
return createInsufficientGasError(context.chain, error);
|
|
1008
|
+
return createInsufficientGasError(context.chain, { rawError: error });
|
|
938
1009
|
}
|
|
939
1010
|
// Pattern 4: Network connectivity errors
|
|
940
1011
|
// Matches connection failures, DNS errors, and timeouts
|
|
941
1012
|
if (/connection (refused|failed)|network|timeout|ENOTFOUND|ECONNREFUSED/i.test(msg)) {
|
|
942
|
-
return createNetworkConnectionError(context.chain, error);
|
|
1013
|
+
return createNetworkConnectionError(context.chain, { rawError: error });
|
|
943
1014
|
}
|
|
944
1015
|
// Pattern 5: RPC provider errors
|
|
945
1016
|
// Matches RPC endpoint errors, invalid responses, and rate limits
|
|
946
1017
|
if (/rpc|invalid response|rate limit|too many requests/i.test(msg)) {
|
|
947
|
-
return createRpcEndpointError(context.chain, error);
|
|
1018
|
+
return createRpcEndpointError(context.chain, { rawError: error });
|
|
948
1019
|
}
|
|
949
1020
|
// Fallback based on operation context
|
|
950
1021
|
// Gas-related operations are RPC calls
|
|
951
1022
|
if (context.operation === 'estimateGas' ||
|
|
952
1023
|
context.operation === 'getGasPrice') {
|
|
953
|
-
return createRpcEndpointError(context.chain, error);
|
|
1024
|
+
return createRpcEndpointError(context.chain, { rawError: error });
|
|
954
1025
|
}
|
|
955
1026
|
// Fallback for unrecognized errors
|
|
956
1027
|
// Defaults to simulation failed as transaction execution is the most common failure point
|
|
957
|
-
return createSimulationFailedError(context.chain, msg.length > 0 ? msg : 'Unknown error', error);
|
|
1028
|
+
return createSimulationFailedError(context.chain, msg.length > 0 ? msg : 'Unknown error', { rawError: error });
|
|
958
1029
|
}
|
|
959
1030
|
/**
|
|
960
1031
|
* Type guard to check if error has Solana-Kit structure with logs.
|
|
@@ -1401,8 +1472,11 @@ const ArcTestnet = defineChain({
|
|
|
1401
1472
|
name: 'Arc Testnet',
|
|
1402
1473
|
title: 'ArcTestnet',
|
|
1403
1474
|
nativeCurrency: {
|
|
1404
|
-
name: '
|
|
1405
|
-
symbol: '
|
|
1475
|
+
name: 'USDC',
|
|
1476
|
+
symbol: 'USDC',
|
|
1477
|
+
// Arc uses native USDC with 18 decimals for gas payments (EVM standard).
|
|
1478
|
+
// Note: The ERC-20 USDC contract at usdcAddress uses 6 decimals.
|
|
1479
|
+
// See: https://docs.arc.network/arc/references/contract-addresses
|
|
1406
1480
|
decimals: 18,
|
|
1407
1481
|
},
|
|
1408
1482
|
chainId: 5042002,
|
|
@@ -8506,6 +8580,60 @@ const customBurn = async (params, adapter, context) => {
|
|
|
8506
8580
|
}, context);
|
|
8507
8581
|
};
|
|
8508
8582
|
|
|
8583
|
+
/**
|
|
8584
|
+
* Prepares an EVM-compatible native token balance read (ETH, MATIC, etc.).
|
|
8585
|
+
*
|
|
8586
|
+
* This function creates a prepared chain request for reading the native token balance
|
|
8587
|
+
* of a given wallet address on an EVM-based chain. If no wallet address is provided,
|
|
8588
|
+
* the adapter's default address from context is used.
|
|
8589
|
+
*
|
|
8590
|
+
* @param params - The action payload for the native balance lookup:
|
|
8591
|
+
* - `walletAddress` (optional): The wallet address to check. Defaults to context address.
|
|
8592
|
+
* @param adapter - The EVM adapter providing chain context.
|
|
8593
|
+
* @param context - The resolved operation context providing chain and address information.
|
|
8594
|
+
* @returns A promise resolving to a prepared chain request.
|
|
8595
|
+
* The `execute` method returns the balance as a string (in wei).
|
|
8596
|
+
* @throws {KitError} If the current chain is not EVM-compatible (INPUT_INVALID_CHAIN).
|
|
8597
|
+
* @throws Error If address validation fails.
|
|
8598
|
+
*
|
|
8599
|
+
* @example
|
|
8600
|
+
* ```typescript
|
|
8601
|
+
* import { Ethereum } from '@core/chains'
|
|
8602
|
+
*
|
|
8603
|
+
* const prepared = await balanceOf(
|
|
8604
|
+
* { walletAddress: '0x1234...' },
|
|
8605
|
+
* adapter,
|
|
8606
|
+
* context
|
|
8607
|
+
* )
|
|
8608
|
+
* const balance = await prepared.execute()
|
|
8609
|
+
* console.log(balance) // e.g., "1000000000000000000" (1 ETH in wei)
|
|
8610
|
+
*
|
|
8611
|
+
* // Check balance for the adapter's address (no walletAddress provided)
|
|
8612
|
+
* const prepared2 = await balanceOf({}, adapter, context)
|
|
8613
|
+
* const balance2 = await prepared2.execute()
|
|
8614
|
+
* ```
|
|
8615
|
+
*/
|
|
8616
|
+
const balanceOf$2 = async (params, adapter, context) => {
|
|
8617
|
+
const chain = context.chain;
|
|
8618
|
+
if (chain.type !== 'evm') {
|
|
8619
|
+
throw createInvalidChainError(chain.name, `Expected EVM chain definition, but received chain type: ${chain.type}`);
|
|
8620
|
+
}
|
|
8621
|
+
// Use provided wallet address or fall back to context address
|
|
8622
|
+
const walletAddress = params.walletAddress ?? context.address;
|
|
8623
|
+
// Validate the address
|
|
8624
|
+
assertEvmAddress(walletAddress);
|
|
8625
|
+
// Create noop request for gas estimation (reading balance is free)
|
|
8626
|
+
const noopRequest = await createNoopChainRequest();
|
|
8627
|
+
return {
|
|
8628
|
+
type: 'evm',
|
|
8629
|
+
estimate: noopRequest.estimate,
|
|
8630
|
+
execute: async () => {
|
|
8631
|
+
const balance = await adapter.readNativeBalance(walletAddress, chain);
|
|
8632
|
+
return balance.toString();
|
|
8633
|
+
},
|
|
8634
|
+
};
|
|
8635
|
+
};
|
|
8636
|
+
|
|
8509
8637
|
/**
|
|
8510
8638
|
* Prepares an EVM-compatible `balanceOf` read for any ERC-20 token.
|
|
8511
8639
|
*
|
|
@@ -8832,6 +8960,14 @@ const getHandlers = (adapter) => {
|
|
|
8832
8960
|
'cctp.v2.customBurn': async (params, context) => {
|
|
8833
8961
|
return customBurn(params, adapter, context);
|
|
8834
8962
|
},
|
|
8963
|
+
/**
|
|
8964
|
+
* Handler for native token balance operations on EVM chains.
|
|
8965
|
+
*
|
|
8966
|
+
* Gets the native balance (ETH, MATIC, etc.) for the given address.
|
|
8967
|
+
*/
|
|
8968
|
+
'native.balanceOf': async (params, context) => {
|
|
8969
|
+
return balanceOf$2(params, adapter, context);
|
|
8970
|
+
},
|
|
8835
8971
|
};
|
|
8836
8972
|
};
|
|
8837
8973
|
|
|
@@ -10845,18 +10981,13 @@ class EthersAdapter extends EvmAdapter {
|
|
|
10845
10981
|
return this._signer;
|
|
10846
10982
|
}
|
|
10847
10983
|
/**
|
|
10848
|
-
*
|
|
10984
|
+
* Parses and validates an ABI, ensuring the specified function exists.
|
|
10985
|
+
*
|
|
10986
|
+
* @param abi - The ABI to parse (can be a string array or object array).
|
|
10987
|
+
* @param functionName - The function name to validate exists in the ABI.
|
|
10988
|
+
* @throws Error when ABI parsing fails or function is not found.
|
|
10849
10989
|
*/
|
|
10850
|
-
|
|
10851
|
-
const { address, abi: abiInput, functionName } = params;
|
|
10852
|
-
// Wallet address is passed as parameter to avoid race conditions in concurrent requests
|
|
10853
|
-
if (!walletAddress) {
|
|
10854
|
-
throw new Error('Unable to retrieve account from signer. Please ensure a valid wallet is connected.');
|
|
10855
|
-
}
|
|
10856
|
-
assertEvmPreparedChainRequestParams(params);
|
|
10857
|
-
const abi = Array.isArray(abiInput) && typeof abiInput[0] === 'string'
|
|
10858
|
-
? abiInput
|
|
10859
|
-
: abiInput;
|
|
10990
|
+
_parseAndValidateAbi(abi, functionName) {
|
|
10860
10991
|
let contractInterface = undefined;
|
|
10861
10992
|
try {
|
|
10862
10993
|
contractInterface = new ethers.Interface(abi);
|
|
@@ -10871,6 +11002,21 @@ class EthersAdapter extends EvmAdapter {
|
|
|
10871
11002
|
if (!functionNames.includes(functionName)) {
|
|
10872
11003
|
throw new Error(`Function '${functionName}' not found in ABI.`);
|
|
10873
11004
|
}
|
|
11005
|
+
}
|
|
11006
|
+
/**
|
|
11007
|
+
* Creates a contract instance for the given address, ABI, and wallet.
|
|
11008
|
+
*/
|
|
11009
|
+
createContractInstance(params, signer, walletAddress, provider) {
|
|
11010
|
+
const { address, abi: abiInput, functionName } = params;
|
|
11011
|
+
// Wallet address is passed as parameter to avoid race conditions in concurrent requests
|
|
11012
|
+
if (!walletAddress) {
|
|
11013
|
+
throw new Error('Unable to retrieve account from signer. Please ensure a valid wallet is connected.');
|
|
11014
|
+
}
|
|
11015
|
+
assertEvmPreparedChainRequestParams(params);
|
|
11016
|
+
const abi = Array.isArray(abiInput) && typeof abiInput[0] === 'string'
|
|
11017
|
+
? abiInput
|
|
11018
|
+
: abiInput;
|
|
11019
|
+
this._parseAndValidateAbi(abi, functionName);
|
|
10874
11020
|
const contract = provider
|
|
10875
11021
|
? new ethers.Contract(address, abi, provider).connect(signer)
|
|
10876
11022
|
: new ethers.Contract(address, abi, signer);
|
|
@@ -11131,27 +11277,24 @@ class EthersAdapter extends EvmAdapter {
|
|
|
11131
11277
|
if (targetChain.type !== 'evm') {
|
|
11132
11278
|
throw new Error(`Invalid chain type '${String(targetChain.type)}' for EthersAdapter. Expected 'evm' chain type.`);
|
|
11133
11279
|
}
|
|
11134
|
-
// Ensure we're on the correct chain BEFORE resolving context
|
|
11135
|
-
// This is critical because resolveOperationContext may call getAddress(),
|
|
11136
|
-
// which queries the wallet. For browser wallets, we must be on the correct
|
|
11137
|
-
// chain before querying wallet state (especially for smart contract wallets).
|
|
11138
|
-
await this.ensureChain(targetChain);
|
|
11139
|
-
// Now resolve the full operation context (address resolution happens on correct chain)
|
|
11140
|
-
const resolvedContext = await resolveOperationContext(this, ctx);
|
|
11141
|
-
if (!resolvedContext) {
|
|
11142
|
-
throw new Error('OperationContext resolution failed. Ensure the adapter has capabilities configured.');
|
|
11143
|
-
}
|
|
11144
|
-
const signer = this.getSigner();
|
|
11145
|
-
const provider = await this.getProvider(targetChain);
|
|
11146
11280
|
// Check if the function is read-only (view or pure)
|
|
11281
|
+
// Read-only functions don't need chain switching
|
|
11147
11282
|
// If function is not found in ABI or ABI is invalid, default to false
|
|
11148
11283
|
const isReadOnly = Array.isArray(abi)
|
|
11149
11284
|
? isReadOnlyFunction(abi, functionName, false)
|
|
11150
11285
|
: false;
|
|
11286
|
+
const provider = await this.getProvider(targetChain);
|
|
11151
11287
|
if (isReadOnly) {
|
|
11152
|
-
return this.handleReadOnlyFunction(params,
|
|
11288
|
+
return this.handleReadOnlyFunction(params, provider);
|
|
11289
|
+
}
|
|
11290
|
+
// For state-changing functions, ensure we're on the correct chain
|
|
11291
|
+
await this.ensureChain(targetChain);
|
|
11292
|
+
// Resolve the full operation context (address resolution happens here)
|
|
11293
|
+
const resolvedContext = await resolveOperationContext(this, ctx);
|
|
11294
|
+
if (!resolvedContext) {
|
|
11295
|
+
throw new Error('OperationContext resolution failed. Ensure the adapter has capabilities configured.');
|
|
11153
11296
|
}
|
|
11154
|
-
|
|
11297
|
+
const signer = this.getSigner();
|
|
11155
11298
|
const contract = this.createContractInstance(params, signer, resolvedContext.address, provider);
|
|
11156
11299
|
return {
|
|
11157
11300
|
type: 'evm',
|
|
@@ -11216,8 +11359,6 @@ class EthersAdapter extends EvmAdapter {
|
|
|
11216
11359
|
if (!chain) {
|
|
11217
11360
|
throw new Error('Chain parameter is required for address resolution. This should be provided by the OperationContext pattern.');
|
|
11218
11361
|
}
|
|
11219
|
-
// Ensure we're on the correct chain before getting address
|
|
11220
|
-
await this.ensureChain(chain);
|
|
11221
11362
|
const signer = this.getSigner();
|
|
11222
11363
|
const address = await signer.getAddress();
|
|
11223
11364
|
return address;
|
|
@@ -11269,6 +11410,44 @@ class EthersAdapter extends EvmAdapter {
|
|
|
11269
11410
|
}
|
|
11270
11411
|
return feeData.gasPrice;
|
|
11271
11412
|
}
|
|
11413
|
+
/**
|
|
11414
|
+
* Reads the native token balance (ETH, MATIC, etc.) for a given address.
|
|
11415
|
+
*
|
|
11416
|
+
* @param address - The wallet address to check the balance for.
|
|
11417
|
+
* @param chain - The chain definition to fetch the balance from.
|
|
11418
|
+
* @returns Promise resolving to the balance in wei as a bigint.
|
|
11419
|
+
* @throws Error when balance retrieval fails.
|
|
11420
|
+
*
|
|
11421
|
+
* @example
|
|
11422
|
+
* ```typescript
|
|
11423
|
+
* const balance = await adapter.readNativeBalance(
|
|
11424
|
+
* '0x1234...',
|
|
11425
|
+
* Ethereum
|
|
11426
|
+
* )
|
|
11427
|
+
* console.log('Balance:', balance.toString(), 'wei')
|
|
11428
|
+
* ```
|
|
11429
|
+
*/
|
|
11430
|
+
async readNativeBalance(address, chain) {
|
|
11431
|
+
const provider = await this.getProvider(chain);
|
|
11432
|
+
try {
|
|
11433
|
+
return await provider.getBalance(address);
|
|
11434
|
+
}
|
|
11435
|
+
catch (error) {
|
|
11436
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
11437
|
+
throw new KitError({
|
|
11438
|
+
...RpcError.ENDPOINT_ERROR,
|
|
11439
|
+
recoverability: 'RETRYABLE',
|
|
11440
|
+
message: `Failed to get native balance for ${address}: ${errorMessage}`,
|
|
11441
|
+
cause: {
|
|
11442
|
+
trace: {
|
|
11443
|
+
operation: 'readNativeBalance',
|
|
11444
|
+
address,
|
|
11445
|
+
chain: chain.name,
|
|
11446
|
+
},
|
|
11447
|
+
},
|
|
11448
|
+
});
|
|
11449
|
+
}
|
|
11450
|
+
}
|
|
11272
11451
|
/**
|
|
11273
11452
|
* Signs EIP-712 typed data using the configured signer with OperationContext.
|
|
11274
11453
|
*
|
|
@@ -11383,8 +11562,6 @@ class EthersAdapter extends EvmAdapter {
|
|
|
11383
11562
|
if (targetChain.type !== 'evm') {
|
|
11384
11563
|
throw new Error(`Invalid chain type '${String(targetChain.type)}' for EthersAdapter. Expected 'evm' chain type.`);
|
|
11385
11564
|
}
|
|
11386
|
-
// Ensure adapter is connected to correct chain
|
|
11387
|
-
await this.ensureChain(targetChain);
|
|
11388
11565
|
const signer = this.getSigner();
|
|
11389
11566
|
if (!signer) {
|
|
11390
11567
|
throw new Error('No signer is configured. Please provide a signer to sign typed data.');
|
|
@@ -11405,13 +11582,11 @@ class EthersAdapter extends EvmAdapter {
|
|
|
11405
11582
|
* Handle read-only function execution with noop estimation.
|
|
11406
11583
|
*
|
|
11407
11584
|
* @param params - The EVM prepared chain request parameters.
|
|
11408
|
-
* @param
|
|
11409
|
-
* @param provider - The Ethers provider instance.
|
|
11410
|
-
* @param walletAddress - The wallet address (prevents race conditions in concurrent requests).
|
|
11585
|
+
* @param provider - The Ethers provider instance configured for the target chain.
|
|
11411
11586
|
* @returns A prepared chain request for read-only function execution.
|
|
11412
11587
|
*/
|
|
11413
|
-
async handleReadOnlyFunction(params,
|
|
11414
|
-
const { functionName, args } = params;
|
|
11588
|
+
async handleReadOnlyFunction(params, provider) {
|
|
11589
|
+
const { address, abi: abiInput, functionName, args } = params;
|
|
11415
11590
|
// For read-only functions, use contract call and noop estimation
|
|
11416
11591
|
const noopRequest = await createNoopChainRequest();
|
|
11417
11592
|
return {
|
|
@@ -11419,8 +11594,12 @@ class EthersAdapter extends EvmAdapter {
|
|
|
11419
11594
|
estimate: noopRequest.estimate,
|
|
11420
11595
|
execute: async () => {
|
|
11421
11596
|
try {
|
|
11422
|
-
//
|
|
11423
|
-
|
|
11597
|
+
// For read-only functions, create contract with provider only (no signer needed)
|
|
11598
|
+
// This ensures we query the correct target chain, not the wallet's current chain
|
|
11599
|
+
const abi = Array.isArray(abiInput) && typeof abiInput[0] === 'string'
|
|
11600
|
+
? abiInput
|
|
11601
|
+
: abiInput;
|
|
11602
|
+
const contract = new ethers.Contract(address, abi, provider);
|
|
11424
11603
|
const contractFunction = contract.getFunction(functionName);
|
|
11425
11604
|
const result = await contractFunction.staticCall(...args);
|
|
11426
11605
|
// For read-only functions, return the result as a string
|