@circle-fin/adapter-ethers-v6 1.2.0 → 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.
package/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright (c) 2025, Circle Internet Group, Inc. All rights reserved.
2
+ * Copyright (c) 2026, Circle Internet Group, Inc. All rights reserved.
3
3
  *
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  *
@@ -85,6 +85,8 @@ const ERROR_TYPES = {
85
85
  RPC: 'RPC',
86
86
  /** Internet connectivity, DNS resolution, connection issues */
87
87
  NETWORK: 'NETWORK',
88
+ /** Catch-all for unrecognized errors (code 0) */
89
+ UNKNOWN: 'UNKNOWN',
88
90
  };
89
91
  /**
90
92
  * Array of valid error type values for validation.
@@ -98,6 +100,8 @@ const ERROR_TYPE_ARRAY = [...ERROR_TYPE_VALUES];
98
100
  /**
99
101
  * Error code ranges for validation.
100
102
  * Single source of truth for valid error code ranges.
103
+ *
104
+ * Note: Code 0 is special - it's the UNKNOWN catch-all error.
101
105
  */
102
106
  const ERROR_CODE_RANGES = [
103
107
  { min: 1000, max: 1999, type: 'INPUT' },
@@ -106,6 +110,8 @@ const ERROR_CODE_RANGES = [
106
110
  { min: 5000, max: 5999, type: 'ONCHAIN' },
107
111
  { min: 9000, max: 9999, type: 'BALANCE' },
108
112
  ];
113
+ /** Special code for UNKNOWN errors */
114
+ const UNKNOWN_ERROR_CODE = 0;
109
115
  /**
110
116
  * Zod schema for validating ErrorDetails objects.
111
117
  *
@@ -144,6 +150,7 @@ const ERROR_CODE_RANGES = [
144
150
  const errorDetailsSchema = zod.z.object({
145
151
  /**
146
152
  * Numeric identifier following standardized ranges:
153
+ * - 0: UNKNOWN - Catch-all for unrecognized errors
147
154
  * - 1000-1999: INPUT errors - Parameter validation
148
155
  * - 3000-3999: NETWORK errors - Connectivity issues
149
156
  * - 4000-4999: RPC errors - Provider issues, gas estimation
@@ -153,8 +160,9 @@ const errorDetailsSchema = zod.z.object({
153
160
  code: zod.z
154
161
  .number()
155
162
  .int('Error code must be an integer')
156
- .refine((code) => ERROR_CODE_RANGES.some((range) => code >= range.min && code <= range.max), {
157
- message: 'Error code must be in valid ranges: 1000-1999 (INPUT), 3000-3999 (NETWORK), 4000-4999 (RPC), 5000-5999 (ONCHAIN), 9000-9999 (BALANCE)',
163
+ .refine((code) => code === UNKNOWN_ERROR_CODE ||
164
+ ERROR_CODE_RANGES.some((range) => code >= range.min && code <= range.max), {
165
+ message: 'Error code must be 0 (UNKNOWN) or in valid ranges: 1000-1999 (INPUT), 3000-3999 (NETWORK), 4000-4999 (RPC), 5000-5999 (ONCHAIN), 9000-9999 (BALANCE)',
158
166
  }),
159
167
  /** Human-readable ID (e.g., "INPUT_NETWORK_MISMATCH", "BALANCE_INSUFFICIENT_TOKEN") */
160
168
  name: zod.z
@@ -164,7 +172,7 @@ const errorDetailsSchema = zod.z.object({
164
172
  /** Error category indicating where the error originated */
165
173
  type: zod.z.enum(ERROR_TYPE_ARRAY, {
166
174
  errorMap: () => ({
167
- message: 'Error type must be one of: INPUT, BALANCE, ONCHAIN, RPC, NETWORK',
175
+ message: 'Error type must be one of: INPUT, BALANCE, ONCHAIN, RPC, NETWORK, UNKNOWN',
168
176
  }),
169
177
  }),
170
178
  /** Error handling strategy */
@@ -347,6 +355,7 @@ class KitError extends Error {
347
355
  /**
348
356
  * Standardized error code ranges for consistent categorization:
349
357
  *
358
+ * - 0: UNKNOWN - Catch-all for unrecognized errors
350
359
  * - 1000-1999: INPUT errors - Parameter validation, input format errors
351
360
  * - 3000-3999: NETWORK errors - Internet connectivity, DNS, connection issues
352
361
  * - 4000-4999: RPC errors - Blockchain provider issues, gas estimation, nonce errors
@@ -623,7 +632,7 @@ function createValidationErrorFromZod(zodError, context) {
623
632
  *
624
633
  * @param chain - The blockchain network where the balance check failed
625
634
  * @param token - The token symbol (e.g., 'USDC', 'ETH')
626
- * @param rawError - The original error from the underlying system (optional)
635
+ * @param trace - Optional trace context to include in error (can include rawError and additional debugging data)
627
636
  * @returns KitError with insufficient token balance details
628
637
  *
629
638
  * @example
@@ -636,24 +645,28 @@ function createValidationErrorFromZod(zodError, context) {
636
645
  *
637
646
  * @example
638
647
  * ```typescript
639
- * // With raw error for debugging
648
+ * // With trace context for debugging
640
649
  * try {
641
650
  * await transfer(...)
642
651
  * } catch (error) {
643
- * throw createInsufficientTokenBalanceError('Base', 'USDC', error)
652
+ * throw createInsufficientTokenBalanceError('Base', 'USDC', {
653
+ * rawError: error,
654
+ * balance: '1000000',
655
+ * amount: '5000000',
656
+ * })
644
657
  * }
645
658
  * ```
646
659
  */
647
- function createInsufficientTokenBalanceError(chain, token, rawError) {
660
+ function createInsufficientTokenBalanceError(chain, token, trace) {
648
661
  return new KitError({
649
662
  ...BalanceError.INSUFFICIENT_TOKEN,
650
663
  recoverability: 'FATAL',
651
664
  message: `Insufficient ${token} balance on ${chain}`,
652
665
  cause: {
653
666
  trace: {
667
+ ...trace,
654
668
  chain,
655
669
  token,
656
- rawError,
657
670
  },
658
671
  },
659
672
  });
@@ -666,7 +679,7 @@ function createInsufficientTokenBalanceError(chain, token, rawError) {
666
679
  * as it requires user intervention to add gas funds.
667
680
  *
668
681
  * @param chain - The blockchain network where the gas check failed
669
- * @param rawError - The original error from the underlying system (optional)
682
+ * @param trace - Optional trace context to include in error (can include rawError and additional debugging data)
670
683
  * @returns KitError with insufficient gas details
671
684
  *
672
685
  * @example
@@ -676,16 +689,27 @@ function createInsufficientTokenBalanceError(chain, token, rawError) {
676
689
  * throw createInsufficientGasError('Ethereum')
677
690
  * // Message: "Insufficient gas funds on Ethereum"
678
691
  * ```
692
+ *
693
+ * @example
694
+ * ```typescript
695
+ * // With trace context for debugging
696
+ * throw createInsufficientGasError('Ethereum', {
697
+ * rawError: error,
698
+ * gasRequired: '21000',
699
+ * gasAvailable: '10000',
700
+ * walletAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
701
+ * })
702
+ * ```
679
703
  */
680
- function createInsufficientGasError(chain, rawError) {
704
+ function createInsufficientGasError(chain, trace) {
681
705
  return new KitError({
682
706
  ...BalanceError.INSUFFICIENT_GAS,
683
707
  recoverability: 'FATAL',
684
708
  message: `Insufficient gas funds on ${chain}`,
685
709
  cause: {
686
710
  trace: {
711
+ ...trace,
687
712
  chain,
688
- rawError,
689
713
  },
690
714
  },
691
715
  });
@@ -700,7 +724,7 @@ function createInsufficientGasError(chain, rawError) {
700
724
  *
701
725
  * @param chain - The blockchain network where the simulation failed
702
726
  * @param reason - The reason for simulation failure (e.g., revert message)
703
- * @param rawError - The original error from the underlying system (optional)
727
+ * @param trace - Optional trace context to include in error (can include rawError and additional debugging data)
704
728
  * @returns KitError with simulation failure details
705
729
  *
706
730
  * @example
@@ -710,17 +734,27 @@ function createInsufficientGasError(chain, rawError) {
710
734
  * throw createSimulationFailedError('Ethereum', 'ERC20: insufficient allowance')
711
735
  * // Message: "Simulation failed on Ethereum: ERC20: insufficient allowance"
712
736
  * ```
737
+ *
738
+ * @example
739
+ * ```typescript
740
+ * // With trace context for debugging
741
+ * throw createSimulationFailedError('Ethereum', 'ERC20: insufficient allowance', {
742
+ * rawError: error,
743
+ * txHash: '0x1234...',
744
+ * gasLimit: '21000',
745
+ * })
746
+ * ```
713
747
  */
714
- function createSimulationFailedError(chain, reason, rawError) {
748
+ function createSimulationFailedError(chain, reason, trace) {
715
749
  return new KitError({
716
750
  ...OnchainError.SIMULATION_FAILED,
717
751
  recoverability: 'FATAL',
718
752
  message: `Simulation failed on ${chain}: ${reason}`,
719
753
  cause: {
720
754
  trace: {
755
+ ...trace,
721
756
  chain,
722
757
  reason,
723
- rawError,
724
758
  },
725
759
  },
726
760
  });
@@ -734,7 +768,7 @@ function createSimulationFailedError(chain, reason, rawError) {
734
768
  *
735
769
  * @param chain - The blockchain network where the transaction reverted
736
770
  * @param reason - The reason for the revert (e.g., revert message)
737
- * @param rawError - The original error from the underlying system (optional)
771
+ * @param trace - Optional trace context to include in error (can include rawError and additional debugging data)
738
772
  * @returns KitError with transaction revert details
739
773
  *
740
774
  * @example
@@ -744,17 +778,27 @@ function createSimulationFailedError(chain, reason, rawError) {
744
778
  * throw createTransactionRevertedError('Base', 'Slippage exceeded')
745
779
  * // Message: "Transaction reverted on Base: Slippage exceeded"
746
780
  * ```
781
+ *
782
+ * @example
783
+ * ```typescript
784
+ * // With trace context for debugging
785
+ * throw createTransactionRevertedError('Base', 'Slippage exceeded', {
786
+ * rawError: error,
787
+ * txHash: '0xabc...',
788
+ * blockNumber: '12345',
789
+ * })
790
+ * ```
747
791
  */
748
- function createTransactionRevertedError(chain, reason, rawError) {
792
+ function createTransactionRevertedError(chain, reason, trace) {
749
793
  return new KitError({
750
794
  ...OnchainError.TRANSACTION_REVERTED,
751
795
  recoverability: 'FATAL',
752
796
  message: `Transaction reverted on ${chain}: ${reason}`,
753
797
  cause: {
754
798
  trace: {
799
+ ...trace,
755
800
  chain,
756
801
  reason,
757
- rawError,
758
802
  },
759
803
  },
760
804
  });
@@ -766,7 +810,7 @@ function createTransactionRevertedError(chain, reason, rawError) {
766
810
  * The error is FATAL as it requires adjusting gas limits or transaction logic.
767
811
  *
768
812
  * @param chain - The blockchain network where the transaction ran out of gas
769
- * @param rawError - The original error from the underlying system (optional)
813
+ * @param trace - Optional trace context to include in error (can include rawError and additional debugging data)
770
814
  * @returns KitError with out of gas details
771
815
  *
772
816
  * @example
@@ -776,16 +820,26 @@ function createTransactionRevertedError(chain, reason, rawError) {
776
820
  * throw createOutOfGasError('Polygon')
777
821
  * // Message: "Transaction ran out of gas on Polygon"
778
822
  * ```
823
+ *
824
+ * @example
825
+ * ```typescript
826
+ * // With trace context for debugging
827
+ * throw createOutOfGasError('Polygon', {
828
+ * rawError: error,
829
+ * gasUsed: '50000',
830
+ * gasLimit: '45000',
831
+ * })
832
+ * ```
779
833
  */
780
- function createOutOfGasError(chain, rawError) {
834
+ function createOutOfGasError(chain, trace) {
781
835
  return new KitError({
782
836
  ...OnchainError.OUT_OF_GAS,
783
837
  recoverability: 'FATAL',
784
838
  message: `Transaction ran out of gas on ${chain}`,
785
839
  cause: {
786
840
  trace: {
841
+ ...trace,
787
842
  chain,
788
- rawError,
789
843
  },
790
844
  },
791
845
  });
@@ -798,7 +852,7 @@ function createOutOfGasError(chain, rawError) {
798
852
  * or is unavailable. The error is RETRYABLE as RPC issues are often temporary.
799
853
  *
800
854
  * @param chain - The blockchain network where the RPC error occurred
801
- * @param rawError - The original error from the underlying system (optional)
855
+ * @param trace - Optional trace context to include in error (can include rawError and additional debugging data)
802
856
  * @returns KitError with RPC endpoint error details
803
857
  *
804
858
  * @example
@@ -808,16 +862,26 @@ function createOutOfGasError(chain, rawError) {
808
862
  * throw createRpcEndpointError('Ethereum')
809
863
  * // Message: "RPC endpoint error on Ethereum"
810
864
  * ```
865
+ *
866
+ * @example
867
+ * ```typescript
868
+ * // With trace context for debugging
869
+ * throw createRpcEndpointError('Ethereum', {
870
+ * rawError: error,
871
+ * endpoint: 'https://mainnet.infura.io/v3/...',
872
+ * statusCode: 429,
873
+ * })
874
+ * ```
811
875
  */
812
- function createRpcEndpointError(chain, rawError) {
876
+ function createRpcEndpointError(chain, trace) {
813
877
  return new KitError({
814
878
  ...RpcError.ENDPOINT_ERROR,
815
879
  recoverability: 'RETRYABLE',
816
880
  message: `RPC endpoint error on ${chain}`,
817
881
  cause: {
818
882
  trace: {
883
+ ...trace,
819
884
  chain,
820
- rawError,
821
885
  },
822
886
  },
823
887
  });
@@ -831,7 +895,7 @@ function createRpcEndpointError(chain, rawError) {
831
895
  * often temporary.
832
896
  *
833
897
  * @param chain - The blockchain network where the connection failed
834
- * @param rawError - The original error from the underlying system (optional)
898
+ * @param trace - Optional trace context to include in error (can include rawError and additional debugging data)
835
899
  * @returns KitError with network connection error details
836
900
  *
837
901
  * @example
@@ -841,16 +905,26 @@ function createRpcEndpointError(chain, rawError) {
841
905
  * throw createNetworkConnectionError('Ethereum')
842
906
  * // Message: "Network connection failed for Ethereum"
843
907
  * ```
908
+ *
909
+ * @example
910
+ * ```typescript
911
+ * // With trace context for debugging
912
+ * throw createNetworkConnectionError('Ethereum', {
913
+ * rawError: error,
914
+ * endpoint: 'https://eth-mainnet.g.alchemy.com/v2/...',
915
+ * retryCount: 3,
916
+ * })
917
+ * ```
844
918
  */
845
- function createNetworkConnectionError(chain, rawError) {
919
+ function createNetworkConnectionError(chain, trace) {
846
920
  return new KitError({
847
921
  ...NetworkError.CONNECTION_FAILED,
848
922
  recoverability: 'RETRYABLE',
849
923
  message: `Network connection failed for ${chain}`,
850
924
  cause: {
851
925
  trace: {
926
+ ...trace,
852
927
  chain,
853
- rawError,
854
928
  },
855
929
  },
856
930
  });
@@ -905,7 +979,9 @@ function parseBlockchainError(error, context) {
905
979
  // Pattern 1: Insufficient balance errors
906
980
  // Matches balance-related errors from ERC20 contracts, native transfers, and Solana programs
907
981
  if (/transfer amount exceeds balance|insufficient (balance|funds)|burn amount exceeded/i.test(msg)) {
908
- return createInsufficientTokenBalanceError(context.chain, token, error);
982
+ return createInsufficientTokenBalanceError(context.chain, token, {
983
+ rawError: error,
984
+ });
909
985
  }
910
986
  // Pattern 2: Simulation and execution reverts
911
987
  // Matches contract revert errors and simulation failures
@@ -915,46 +991,50 @@ function parseBlockchainError(error, context) {
915
991
  // "simulation failed" or "eth_call" indicates pre-flight simulation
916
992
  // "transaction failed" or context.operation === 'transaction' indicates post-execution
917
993
  if (/simulation failed/i.test(msg) || context.operation === 'simulation') {
918
- return createSimulationFailedError(context.chain, reason, error);
994
+ return createSimulationFailedError(context.chain, reason, {
995
+ rawError: error,
996
+ });
919
997
  }
920
998
  // Transaction execution failures or reverts
921
- return createTransactionRevertedError(context.chain, reason, error);
999
+ return createTransactionRevertedError(context.chain, reason, {
1000
+ rawError: error,
1001
+ });
922
1002
  }
923
1003
  // Pattern 3: Gas-related errors
924
1004
  // Matches gas estimation failures and gas exhaustion
925
1005
  // Check specific patterns first, then generic "gas" patterns
926
1006
  // Gas estimation failures are RPC issues
927
1007
  if (/gas estimation failed|cannot estimate gas/i.test(msg)) {
928
- return createRpcEndpointError(context.chain, error);
1008
+ return createRpcEndpointError(context.chain, { rawError: error });
929
1009
  }
930
1010
  // Gas exhaustion errors
931
1011
  // Use specific patterns without wildcards to avoid ReDoS
932
1012
  if (/out of gas|gas limit exceeded|exceeds block gas limit/i.test(msg)) {
933
- return createOutOfGasError(context.chain, error);
1013
+ return createOutOfGasError(context.chain, { rawError: error });
934
1014
  }
935
1015
  // Insufficient funds for gas
936
1016
  if (/insufficient funds for gas/i.test(msg)) {
937
- return createInsufficientGasError(context.chain, error);
1017
+ return createInsufficientGasError(context.chain, { rawError: error });
938
1018
  }
939
1019
  // Pattern 4: Network connectivity errors
940
1020
  // Matches connection failures, DNS errors, and timeouts
941
1021
  if (/connection (refused|failed)|network|timeout|ENOTFOUND|ECONNREFUSED/i.test(msg)) {
942
- return createNetworkConnectionError(context.chain, error);
1022
+ return createNetworkConnectionError(context.chain, { rawError: error });
943
1023
  }
944
1024
  // Pattern 5: RPC provider errors
945
1025
  // Matches RPC endpoint errors, invalid responses, and rate limits
946
1026
  if (/rpc|invalid response|rate limit|too many requests/i.test(msg)) {
947
- return createRpcEndpointError(context.chain, error);
1027
+ return createRpcEndpointError(context.chain, { rawError: error });
948
1028
  }
949
1029
  // Fallback based on operation context
950
1030
  // Gas-related operations are RPC calls
951
1031
  if (context.operation === 'estimateGas' ||
952
1032
  context.operation === 'getGasPrice') {
953
- return createRpcEndpointError(context.chain, error);
1033
+ return createRpcEndpointError(context.chain, { rawError: error });
954
1034
  }
955
1035
  // Fallback for unrecognized errors
956
1036
  // 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);
1037
+ return createSimulationFailedError(context.chain, msg.length > 0 ? msg : 'Unknown error', { rawError: error });
958
1038
  }
959
1039
  /**
960
1040
  * Type guard to check if error has Solana-Kit structure with logs.
@@ -1097,6 +1177,8 @@ exports.Blockchain = void 0;
1097
1177
  Blockchain["Ink_Testnet"] = "Ink_Testnet";
1098
1178
  Blockchain["Linea"] = "Linea";
1099
1179
  Blockchain["Linea_Sepolia"] = "Linea_Sepolia";
1180
+ Blockchain["Monad"] = "Monad";
1181
+ Blockchain["Monad_Testnet"] = "Monad_Testnet";
1100
1182
  Blockchain["NEAR"] = "NEAR";
1101
1183
  Blockchain["NEAR_Testnet"] = "NEAR_Testnet";
1102
1184
  Blockchain["Noble"] = "Noble";
@@ -1188,6 +1270,7 @@ var BridgeChain;
1188
1270
  BridgeChain["HyperEVM"] = "HyperEVM";
1189
1271
  BridgeChain["Ink"] = "Ink";
1190
1272
  BridgeChain["Linea"] = "Linea";
1273
+ BridgeChain["Monad"] = "Monad";
1191
1274
  BridgeChain["Optimism"] = "Optimism";
1192
1275
  BridgeChain["Plume"] = "Plume";
1193
1276
  BridgeChain["Polygon"] = "Polygon";
@@ -1207,6 +1290,7 @@ var BridgeChain;
1207
1290
  BridgeChain["HyperEVM_Testnet"] = "HyperEVM_Testnet";
1208
1291
  BridgeChain["Ink_Testnet"] = "Ink_Testnet";
1209
1292
  BridgeChain["Linea_Sepolia"] = "Linea_Sepolia";
1293
+ BridgeChain["Monad_Testnet"] = "Monad_Testnet";
1210
1294
  BridgeChain["Optimism_Sepolia"] = "Optimism_Sepolia";
1211
1295
  BridgeChain["Plume_Testnet"] = "Plume_Testnet";
1212
1296
  BridgeChain["Polygon_Amoy_Testnet"] = "Polygon_Amoy_Testnet";
@@ -1401,8 +1485,11 @@ const ArcTestnet = defineChain({
1401
1485
  name: 'Arc Testnet',
1402
1486
  title: 'ArcTestnet',
1403
1487
  nativeCurrency: {
1404
- name: 'Arc',
1405
- symbol: 'Arc',
1488
+ name: 'USDC',
1489
+ symbol: 'USDC',
1490
+ // Arc uses native USDC with 18 decimals for gas payments (EVM standard).
1491
+ // Note: The ERC-20 USDC contract at usdcAddress uses 6 decimals.
1492
+ // See: https://docs.arc.network/arc/references/contract-addresses
1406
1493
  decimals: 18,
1407
1494
  },
1408
1495
  chainId: 5042002,
@@ -2190,6 +2277,86 @@ const LineaSepolia = defineChain({
2190
2277
  },
2191
2278
  });
2192
2279
 
2280
+ /**
2281
+ * Monad Mainnet chain definition
2282
+ * @remarks
2283
+ * This represents the official production network for the Monad blockchain.
2284
+ * Monad is a high-performance EVM-compatible Layer-1 blockchain featuring
2285
+ * over 10,000 TPS, sub-second finality, and near-zero gas fees.
2286
+ */
2287
+ const Monad = defineChain({
2288
+ type: 'evm',
2289
+ chain: exports.Blockchain.Monad,
2290
+ name: 'Monad',
2291
+ title: 'Monad Mainnet',
2292
+ nativeCurrency: {
2293
+ name: 'Monad',
2294
+ symbol: 'MON',
2295
+ decimals: 18,
2296
+ },
2297
+ chainId: 143,
2298
+ isTestnet: false,
2299
+ explorerUrl: 'https://monadscan.com/tx/{hash}',
2300
+ rpcEndpoints: ['https://rpc.monad.xyz'],
2301
+ eurcAddress: null,
2302
+ usdcAddress: '0x754704Bc059F8C67012fEd69BC8A327a5aafb603',
2303
+ cctp: {
2304
+ domain: 15,
2305
+ contracts: {
2306
+ v2: {
2307
+ type: 'split',
2308
+ tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
2309
+ messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
2310
+ confirmations: 1,
2311
+ fastConfirmations: 1,
2312
+ },
2313
+ },
2314
+ },
2315
+ kitContracts: {
2316
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
2317
+ },
2318
+ });
2319
+
2320
+ /**
2321
+ * Monad Testnet chain definition
2322
+ * @remarks
2323
+ * This represents the official test network for the Monad blockchain.
2324
+ * Monad is a high-performance EVM-compatible Layer-1 blockchain featuring
2325
+ * over 10,000 TPS, sub-second finality, and near-zero gas fees.
2326
+ */
2327
+ const MonadTestnet = defineChain({
2328
+ type: 'evm',
2329
+ chain: exports.Blockchain.Monad_Testnet,
2330
+ name: 'Monad Testnet',
2331
+ title: 'Monad Testnet',
2332
+ nativeCurrency: {
2333
+ name: 'Monad',
2334
+ symbol: 'MON',
2335
+ decimals: 18,
2336
+ },
2337
+ chainId: 10143,
2338
+ isTestnet: true,
2339
+ explorerUrl: 'https://testnet.monadscan.com/tx/{hash}',
2340
+ rpcEndpoints: ['https://testnet-rpc.monad.xyz'],
2341
+ eurcAddress: null,
2342
+ usdcAddress: '0x534b2f3A21130d7a60830c2Df862319e593943A3',
2343
+ cctp: {
2344
+ domain: 15,
2345
+ contracts: {
2346
+ v2: {
2347
+ type: 'split',
2348
+ tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
2349
+ messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
2350
+ confirmations: 1,
2351
+ fastConfirmations: 1,
2352
+ },
2353
+ },
2354
+ },
2355
+ kitContracts: {
2356
+ bridge: BRIDGE_CONTRACT_EVM_TESTNET,
2357
+ },
2358
+ });
2359
+
2193
2360
  /**
2194
2361
  * NEAR Protocol Mainnet chain definition
2195
2362
  * @remarks
@@ -3274,6 +3441,8 @@ var Blockchains = {
3274
3441
  InkTestnet: InkTestnet,
3275
3442
  Linea: Linea,
3276
3443
  LineaSepolia: LineaSepolia,
3444
+ Monad: Monad,
3445
+ MonadTestnet: MonadTestnet,
3277
3446
  NEAR: NEAR,
3278
3447
  NEARTestnet: NEARTestnet,
3279
3448
  Noble: Noble,
@@ -8506,6 +8675,60 @@ const customBurn = async (params, adapter, context) => {
8506
8675
  }, context);
8507
8676
  };
8508
8677
 
8678
+ /**
8679
+ * Prepares an EVM-compatible native token balance read (ETH, MATIC, etc.).
8680
+ *
8681
+ * This function creates a prepared chain request for reading the native token balance
8682
+ * of a given wallet address on an EVM-based chain. If no wallet address is provided,
8683
+ * the adapter's default address from context is used.
8684
+ *
8685
+ * @param params - The action payload for the native balance lookup:
8686
+ * - `walletAddress` (optional): The wallet address to check. Defaults to context address.
8687
+ * @param adapter - The EVM adapter providing chain context.
8688
+ * @param context - The resolved operation context providing chain and address information.
8689
+ * @returns A promise resolving to a prepared chain request.
8690
+ * The `execute` method returns the balance as a string (in wei).
8691
+ * @throws {KitError} If the current chain is not EVM-compatible (INPUT_INVALID_CHAIN).
8692
+ * @throws Error If address validation fails.
8693
+ *
8694
+ * @example
8695
+ * ```typescript
8696
+ * import { Ethereum } from '@core/chains'
8697
+ *
8698
+ * const prepared = await balanceOf(
8699
+ * { walletAddress: '0x1234...' },
8700
+ * adapter,
8701
+ * context
8702
+ * )
8703
+ * const balance = await prepared.execute()
8704
+ * console.log(balance) // e.g., "1000000000000000000" (1 ETH in wei)
8705
+ *
8706
+ * // Check balance for the adapter's address (no walletAddress provided)
8707
+ * const prepared2 = await balanceOf({}, adapter, context)
8708
+ * const balance2 = await prepared2.execute()
8709
+ * ```
8710
+ */
8711
+ const balanceOf$2 = async (params, adapter, context) => {
8712
+ const chain = context.chain;
8713
+ if (chain.type !== 'evm') {
8714
+ throw createInvalidChainError(chain.name, `Expected EVM chain definition, but received chain type: ${chain.type}`);
8715
+ }
8716
+ // Use provided wallet address or fall back to context address
8717
+ const walletAddress = params.walletAddress ?? context.address;
8718
+ // Validate the address
8719
+ assertEvmAddress(walletAddress);
8720
+ // Create noop request for gas estimation (reading balance is free)
8721
+ const noopRequest = await createNoopChainRequest();
8722
+ return {
8723
+ type: 'evm',
8724
+ estimate: noopRequest.estimate,
8725
+ execute: async () => {
8726
+ const balance = await adapter.readNativeBalance(walletAddress, chain);
8727
+ return balance.toString();
8728
+ },
8729
+ };
8730
+ };
8731
+
8509
8732
  /**
8510
8733
  * Prepares an EVM-compatible `balanceOf` read for any ERC-20 token.
8511
8734
  *
@@ -8832,6 +9055,14 @@ const getHandlers = (adapter) => {
8832
9055
  'cctp.v2.customBurn': async (params, context) => {
8833
9056
  return customBurn(params, adapter, context);
8834
9057
  },
9058
+ /**
9059
+ * Handler for native token balance operations on EVM chains.
9060
+ *
9061
+ * Gets the native balance (ETH, MATIC, etc.) for the given address.
9062
+ */
9063
+ 'native.balanceOf': async (params, context) => {
9064
+ return balanceOf$2(params, adapter, context);
9065
+ },
8835
9066
  };
8836
9067
  };
8837
9068
 
@@ -10845,18 +11076,13 @@ class EthersAdapter extends EvmAdapter {
10845
11076
  return this._signer;
10846
11077
  }
10847
11078
  /**
10848
- * Creates a contract instance for the given address, ABI, and wallet.
11079
+ * Parses and validates an ABI, ensuring the specified function exists.
11080
+ *
11081
+ * @param abi - The ABI to parse (can be a string array or object array).
11082
+ * @param functionName - The function name to validate exists in the ABI.
11083
+ * @throws Error when ABI parsing fails or function is not found.
10849
11084
  */
10850
- createContractInstance(params, signer, walletAddress, provider) {
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;
11085
+ _parseAndValidateAbi(abi, functionName) {
10860
11086
  let contractInterface = undefined;
10861
11087
  try {
10862
11088
  contractInterface = new ethers.Interface(abi);
@@ -10871,6 +11097,21 @@ class EthersAdapter extends EvmAdapter {
10871
11097
  if (!functionNames.includes(functionName)) {
10872
11098
  throw new Error(`Function '${functionName}' not found in ABI.`);
10873
11099
  }
11100
+ }
11101
+ /**
11102
+ * Creates a contract instance for the given address, ABI, and wallet.
11103
+ */
11104
+ createContractInstance(params, signer, walletAddress, provider) {
11105
+ const { address, abi: abiInput, functionName } = params;
11106
+ // Wallet address is passed as parameter to avoid race conditions in concurrent requests
11107
+ if (!walletAddress) {
11108
+ throw new Error('Unable to retrieve account from signer. Please ensure a valid wallet is connected.');
11109
+ }
11110
+ assertEvmPreparedChainRequestParams(params);
11111
+ const abi = Array.isArray(abiInput) && typeof abiInput[0] === 'string'
11112
+ ? abiInput
11113
+ : abiInput;
11114
+ this._parseAndValidateAbi(abi, functionName);
10874
11115
  const contract = provider
10875
11116
  ? new ethers.Contract(address, abi, provider).connect(signer)
10876
11117
  : new ethers.Contract(address, abi, signer);
@@ -11131,27 +11372,24 @@ class EthersAdapter extends EvmAdapter {
11131
11372
  if (targetChain.type !== 'evm') {
11132
11373
  throw new Error(`Invalid chain type '${String(targetChain.type)}' for EthersAdapter. Expected 'evm' chain type.`);
11133
11374
  }
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
11375
  // Check if the function is read-only (view or pure)
11376
+ // Read-only functions don't need chain switching
11147
11377
  // If function is not found in ABI or ABI is invalid, default to false
11148
11378
  const isReadOnly = Array.isArray(abi)
11149
11379
  ? isReadOnlyFunction(abi, functionName, false)
11150
11380
  : false;
11381
+ const provider = await this.getProvider(targetChain);
11151
11382
  if (isReadOnly) {
11152
- return this.handleReadOnlyFunction(params, signer, provider, resolvedContext.address);
11383
+ return this.handleReadOnlyFunction(params, provider);
11384
+ }
11385
+ // For state-changing functions, ensure we're on the correct chain
11386
+ await this.ensureChain(targetChain);
11387
+ // Resolve the full operation context (address resolution happens here)
11388
+ const resolvedContext = await resolveOperationContext(this, ctx);
11389
+ if (!resolvedContext) {
11390
+ throw new Error('OperationContext resolution failed. Ensure the adapter has capabilities configured.');
11153
11391
  }
11154
- // For state-changing functions, use the original logic
11392
+ const signer = this.getSigner();
11155
11393
  const contract = this.createContractInstance(params, signer, resolvedContext.address, provider);
11156
11394
  return {
11157
11395
  type: 'evm',
@@ -11216,8 +11454,6 @@ class EthersAdapter extends EvmAdapter {
11216
11454
  if (!chain) {
11217
11455
  throw new Error('Chain parameter is required for address resolution. This should be provided by the OperationContext pattern.');
11218
11456
  }
11219
- // Ensure we're on the correct chain before getting address
11220
- await this.ensureChain(chain);
11221
11457
  const signer = this.getSigner();
11222
11458
  const address = await signer.getAddress();
11223
11459
  return address;
@@ -11269,6 +11505,44 @@ class EthersAdapter extends EvmAdapter {
11269
11505
  }
11270
11506
  return feeData.gasPrice;
11271
11507
  }
11508
+ /**
11509
+ * Reads the native token balance (ETH, MATIC, etc.) for a given address.
11510
+ *
11511
+ * @param address - The wallet address to check the balance for.
11512
+ * @param chain - The chain definition to fetch the balance from.
11513
+ * @returns Promise resolving to the balance in wei as a bigint.
11514
+ * @throws Error when balance retrieval fails.
11515
+ *
11516
+ * @example
11517
+ * ```typescript
11518
+ * const balance = await adapter.readNativeBalance(
11519
+ * '0x1234...',
11520
+ * Ethereum
11521
+ * )
11522
+ * console.log('Balance:', balance.toString(), 'wei')
11523
+ * ```
11524
+ */
11525
+ async readNativeBalance(address, chain) {
11526
+ const provider = await this.getProvider(chain);
11527
+ try {
11528
+ return await provider.getBalance(address);
11529
+ }
11530
+ catch (error) {
11531
+ const errorMessage = error instanceof Error ? error.message : String(error);
11532
+ throw new KitError({
11533
+ ...RpcError.ENDPOINT_ERROR,
11534
+ recoverability: 'RETRYABLE',
11535
+ message: `Failed to get native balance for ${address}: ${errorMessage}`,
11536
+ cause: {
11537
+ trace: {
11538
+ operation: 'readNativeBalance',
11539
+ address,
11540
+ chain: chain.name,
11541
+ },
11542
+ },
11543
+ });
11544
+ }
11545
+ }
11272
11546
  /**
11273
11547
  * Signs EIP-712 typed data using the configured signer with OperationContext.
11274
11548
  *
@@ -11383,8 +11657,6 @@ class EthersAdapter extends EvmAdapter {
11383
11657
  if (targetChain.type !== 'evm') {
11384
11658
  throw new Error(`Invalid chain type '${String(targetChain.type)}' for EthersAdapter. Expected 'evm' chain type.`);
11385
11659
  }
11386
- // Ensure adapter is connected to correct chain
11387
- await this.ensureChain(targetChain);
11388
11660
  const signer = this.getSigner();
11389
11661
  if (!signer) {
11390
11662
  throw new Error('No signer is configured. Please provide a signer to sign typed data.');
@@ -11405,13 +11677,11 @@ class EthersAdapter extends EvmAdapter {
11405
11677
  * Handle read-only function execution with noop estimation.
11406
11678
  *
11407
11679
  * @param params - The EVM prepared chain request parameters.
11408
- * @param signer - The Ethers signer instance.
11409
- * @param provider - The Ethers provider instance.
11410
- * @param walletAddress - The wallet address (prevents race conditions in concurrent requests).
11680
+ * @param provider - The Ethers provider instance configured for the target chain.
11411
11681
  * @returns A prepared chain request for read-only function execution.
11412
11682
  */
11413
- async handleReadOnlyFunction(params, signer, provider, walletAddress) {
11414
- const { functionName, args } = params;
11683
+ async handleReadOnlyFunction(params, provider) {
11684
+ const { address, abi: abiInput, functionName, args } = params;
11415
11685
  // For read-only functions, use contract call and noop estimation
11416
11686
  const noopRequest = await createNoopChainRequest();
11417
11687
  return {
@@ -11419,8 +11689,12 @@ class EthersAdapter extends EvmAdapter {
11419
11689
  estimate: noopRequest.estimate,
11420
11690
  execute: async () => {
11421
11691
  try {
11422
- // Create contract instance for read-only function
11423
- const contract = this.createContractInstance(params, signer, walletAddress, provider);
11692
+ // For read-only functions, create contract with provider only (no signer needed)
11693
+ // This ensures we query the correct target chain, not the wallet's current chain
11694
+ const abi = Array.isArray(abiInput) && typeof abiInput[0] === 'string'
11695
+ ? abiInput
11696
+ : abiInput;
11697
+ const contract = new ethers.Contract(address, abi, provider);
11424
11698
  const contractFunction = contract.getFunction(functionName);
11425
11699
  const result = await contractFunction.staticCall(...args);
11426
11700
  // For read-only functions, return the result as a string
@@ -11764,7 +12038,7 @@ const createAdapterFromPrivateKey = createEthersAdapterFromPrivateKey;
11764
12038
  * const adapter = await createEthersAdapterFromProvider({
11765
12039
  * provider: window.ethereum,
11766
12040
  * getProvider: ({ chain }) => new JsonRpcProvider(
11767
- * `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_KEY}`,
12041
+ * `https://eth-mainnet.g.alchemy.com/v2/${process.env.ALCHEMY_KEY}`,
11768
12042
  * { name: chain.name, chainId: chain.chainId }
11769
12043
  * )
11770
12044
  * })
@@ -11934,7 +12208,7 @@ const createEthersAdapterFromProvider = async (params) => {
11934
12208
  * const adapter = await createAdapterFromProvider({
11935
12209
  * provider: window.ethereum,
11936
12210
  * getProvider: ({ chain }) => new JsonRpcProvider(
11937
- * `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_KEY}`,
12211
+ * `https://eth-mainnet.g.alchemy.com/v2/${process.env.ALCHEMY_KEY}`,
11938
12212
  * { name: chain.name, chainId: chain.chainId }
11939
12213
  * )
11940
12214
  * })