@circle-fin/adapter-ethers-v6 1.3.0 → 1.5.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 (5) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/index.cjs +845 -172
  3. package/index.d.ts +113 -2
  4. package/index.mjs +845 -172
  5. package/package.json +1 -1
package/index.mjs CHANGED
@@ -80,6 +80,8 @@ const ERROR_TYPES = {
80
80
  RPC: 'RPC',
81
81
  /** Internet connectivity, DNS resolution, connection issues */
82
82
  NETWORK: 'NETWORK',
83
+ /** Catch-all for unrecognized errors (code 0) */
84
+ UNKNOWN: 'UNKNOWN',
83
85
  };
84
86
  /**
85
87
  * Array of valid error type values for validation.
@@ -93,6 +95,8 @@ const ERROR_TYPE_ARRAY = [...ERROR_TYPE_VALUES];
93
95
  /**
94
96
  * Error code ranges for validation.
95
97
  * Single source of truth for valid error code ranges.
98
+ *
99
+ * Note: Code 0 is special - it's the UNKNOWN catch-all error.
96
100
  */
97
101
  const ERROR_CODE_RANGES = [
98
102
  { min: 1000, max: 1999, type: 'INPUT' },
@@ -101,6 +105,8 @@ const ERROR_CODE_RANGES = [
101
105
  { min: 5000, max: 5999, type: 'ONCHAIN' },
102
106
  { min: 9000, max: 9999, type: 'BALANCE' },
103
107
  ];
108
+ /** Special code for UNKNOWN errors */
109
+ const UNKNOWN_ERROR_CODE = 0;
104
110
  /**
105
111
  * Zod schema for validating ErrorDetails objects.
106
112
  *
@@ -139,6 +145,7 @@ const ERROR_CODE_RANGES = [
139
145
  const errorDetailsSchema = z.object({
140
146
  /**
141
147
  * Numeric identifier following standardized ranges:
148
+ * - 0: UNKNOWN - Catch-all for unrecognized errors
142
149
  * - 1000-1999: INPUT errors - Parameter validation
143
150
  * - 3000-3999: NETWORK errors - Connectivity issues
144
151
  * - 4000-4999: RPC errors - Provider issues, gas estimation
@@ -148,8 +155,9 @@ const errorDetailsSchema = z.object({
148
155
  code: z
149
156
  .number()
150
157
  .int('Error code must be an integer')
151
- .refine((code) => ERROR_CODE_RANGES.some((range) => code >= range.min && code <= range.max), {
152
- message: 'Error code must be in valid ranges: 1000-1999 (INPUT), 3000-3999 (NETWORK), 4000-4999 (RPC), 5000-5999 (ONCHAIN), 9000-9999 (BALANCE)',
158
+ .refine((code) => code === UNKNOWN_ERROR_CODE ||
159
+ ERROR_CODE_RANGES.some((range) => code >= range.min && code <= range.max), {
160
+ 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)',
153
161
  }),
154
162
  /** Human-readable ID (e.g., "INPUT_NETWORK_MISMATCH", "BALANCE_INSUFFICIENT_TOKEN") */
155
163
  name: z
@@ -159,7 +167,7 @@ const errorDetailsSchema = z.object({
159
167
  /** Error category indicating where the error originated */
160
168
  type: z.enum(ERROR_TYPE_ARRAY, {
161
169
  errorMap: () => ({
162
- message: 'Error type must be one of: INPUT, BALANCE, ONCHAIN, RPC, NETWORK',
170
+ message: 'Error type must be one of: INPUT, BALANCE, ONCHAIN, RPC, NETWORK, UNKNOWN',
163
171
  }),
164
172
  }),
165
173
  /** Error handling strategy */
@@ -342,6 +350,7 @@ class KitError extends Error {
342
350
  /**
343
351
  * Standardized error code ranges for consistent categorization:
344
352
  *
353
+ * - 0: UNKNOWN - Catch-all for unrecognized errors
345
354
  * - 1000-1999: INPUT errors - Parameter validation, input format errors
346
355
  * - 3000-3999: NETWORK errors - Internet connectivity, DNS, connection issues
347
356
  * - 4000-4999: RPC errors - Blockchain provider issues, gas estimation, nonce errors
@@ -537,6 +546,53 @@ function createInvalidChainError(chain, reason) {
537
546
  };
538
547
  return new KitError(errorDetails);
539
548
  }
549
+ /**
550
+ * Creates error for general validation failure.
551
+ *
552
+ * This error is thrown when input validation fails for reasons not covered
553
+ * by more specific error types.
554
+ *
555
+ * @param field - The field that failed validation
556
+ * @param value - The invalid value (can be any type)
557
+ * @param reason - Specific reason why validation failed
558
+ * @returns KitError with validation details
559
+ *
560
+ * @example
561
+ * ```typescript
562
+ * import { createValidationFailedError } from '@core/errors'
563
+ *
564
+ * throw createValidationFailedError('recipient', 'invalid@email', 'Must be a valid wallet address')
565
+ * // Message: "Validation failed for 'recipient': 'invalid@email' - Must be a valid wallet address"
566
+ *
567
+ * throw createValidationFailedError('chainId', 999, 'Unsupported chain ID')
568
+ * // Message: "Validation failed for 'chainId': 999 - Unsupported chain ID"
569
+ *
570
+ * throw createValidationFailedError('config', { invalid: true }, 'Missing required properties')
571
+ * // Message: "Validation failed for 'config': [object Object] - Missing required properties"
572
+ * ```
573
+ */
574
+ function createValidationFailedError(field, value, reason) {
575
+ // Convert value to string for display, handling different types appropriately
576
+ let valueString;
577
+ if (typeof value === 'string') {
578
+ valueString = `'${value}'`;
579
+ }
580
+ else if (typeof value === 'object' && value !== null) {
581
+ valueString = JSON.stringify(value);
582
+ }
583
+ else {
584
+ valueString = String(value);
585
+ }
586
+ const errorDetails = {
587
+ ...InputError.VALIDATION_FAILED,
588
+ recoverability: 'FATAL',
589
+ message: `Validation failed for '${field}': ${valueString} - ${reason}.`,
590
+ cause: {
591
+ trace: { field, value, reason },
592
+ },
593
+ };
594
+ return new KitError(errorDetails);
595
+ }
540
596
  /**
541
597
  * Creates a KitError from a Zod validation error with detailed error information.
542
598
  *
@@ -1163,6 +1219,8 @@ var Blockchain;
1163
1219
  Blockchain["Ink_Testnet"] = "Ink_Testnet";
1164
1220
  Blockchain["Linea"] = "Linea";
1165
1221
  Blockchain["Linea_Sepolia"] = "Linea_Sepolia";
1222
+ Blockchain["Monad"] = "Monad";
1223
+ Blockchain["Monad_Testnet"] = "Monad_Testnet";
1166
1224
  Blockchain["NEAR"] = "NEAR";
1167
1225
  Blockchain["NEAR_Testnet"] = "NEAR_Testnet";
1168
1226
  Blockchain["Noble"] = "Noble";
@@ -1254,6 +1312,7 @@ var BridgeChain;
1254
1312
  BridgeChain["HyperEVM"] = "HyperEVM";
1255
1313
  BridgeChain["Ink"] = "Ink";
1256
1314
  BridgeChain["Linea"] = "Linea";
1315
+ BridgeChain["Monad"] = "Monad";
1257
1316
  BridgeChain["Optimism"] = "Optimism";
1258
1317
  BridgeChain["Plume"] = "Plume";
1259
1318
  BridgeChain["Polygon"] = "Polygon";
@@ -1273,6 +1332,7 @@ var BridgeChain;
1273
1332
  BridgeChain["HyperEVM_Testnet"] = "HyperEVM_Testnet";
1274
1333
  BridgeChain["Ink_Testnet"] = "Ink_Testnet";
1275
1334
  BridgeChain["Linea_Sepolia"] = "Linea_Sepolia";
1335
+ BridgeChain["Monad_Testnet"] = "Monad_Testnet";
1276
1336
  BridgeChain["Optimism_Sepolia"] = "Optimism_Sepolia";
1277
1337
  BridgeChain["Plume_Testnet"] = "Plume_Testnet";
1278
1338
  BridgeChain["Polygon_Amoy_Testnet"] = "Polygon_Amoy_Testnet";
@@ -1399,6 +1459,10 @@ const Aptos = defineChain({
1399
1459
  confirmations: 1,
1400
1460
  },
1401
1461
  },
1462
+ forwarderSupported: {
1463
+ source: false,
1464
+ destination: false,
1465
+ },
1402
1466
  },
1403
1467
  });
1404
1468
 
@@ -1432,6 +1496,10 @@ const AptosTestnet = defineChain({
1432
1496
  confirmations: 1,
1433
1497
  },
1434
1498
  },
1499
+ forwarderSupported: {
1500
+ source: false,
1501
+ destination: false,
1502
+ },
1435
1503
  },
1436
1504
  });
1437
1505
 
@@ -1491,6 +1559,10 @@ const ArcTestnet = defineChain({
1491
1559
  fastConfirmations: 1,
1492
1560
  },
1493
1561
  },
1562
+ forwarderSupported: {
1563
+ source: true,
1564
+ destination: true,
1565
+ },
1494
1566
  },
1495
1567
  kitContracts: {
1496
1568
  bridge: BRIDGE_CONTRACT_EVM_TESTNET,
@@ -1535,6 +1607,10 @@ const Arbitrum = defineChain({
1535
1607
  fastConfirmations: 1,
1536
1608
  },
1537
1609
  },
1610
+ forwarderSupported: {
1611
+ source: true,
1612
+ destination: true,
1613
+ },
1538
1614
  },
1539
1615
  kitContracts: {
1540
1616
  bridge: BRIDGE_CONTRACT_EVM_MAINNET,
@@ -1579,6 +1655,10 @@ const ArbitrumSepolia = defineChain({
1579
1655
  fastConfirmations: 1,
1580
1656
  },
1581
1657
  },
1658
+ forwarderSupported: {
1659
+ source: true,
1660
+ destination: true,
1661
+ },
1582
1662
  },
1583
1663
  kitContracts: {
1584
1664
  bridge: BRIDGE_CONTRACT_EVM_TESTNET,
@@ -1623,6 +1703,10 @@ const Avalanche = defineChain({
1623
1703
  fastConfirmations: 1,
1624
1704
  },
1625
1705
  },
1706
+ forwarderSupported: {
1707
+ source: true,
1708
+ destination: true,
1709
+ },
1626
1710
  },
1627
1711
  kitContracts: {
1628
1712
  bridge: BRIDGE_CONTRACT_EVM_MAINNET,
@@ -1666,6 +1750,10 @@ const AvalancheFuji = defineChain({
1666
1750
  fastConfirmations: 1,
1667
1751
  },
1668
1752
  },
1753
+ forwarderSupported: {
1754
+ source: true,
1755
+ destination: true,
1756
+ },
1669
1757
  },
1670
1758
  rpcEndpoints: ['https://api.avax-test.network/ext/bc/C/rpc'],
1671
1759
  kitContracts: {
@@ -1711,6 +1799,10 @@ const Base = defineChain({
1711
1799
  fastConfirmations: 1,
1712
1800
  },
1713
1801
  },
1802
+ forwarderSupported: {
1803
+ source: true,
1804
+ destination: true,
1805
+ },
1714
1806
  },
1715
1807
  kitContracts: {
1716
1808
  bridge: BRIDGE_CONTRACT_EVM_MAINNET,
@@ -1755,6 +1847,10 @@ const BaseSepolia = defineChain({
1755
1847
  fastConfirmations: 1,
1756
1848
  },
1757
1849
  },
1850
+ forwarderSupported: {
1851
+ source: true,
1852
+ destination: true,
1853
+ },
1758
1854
  },
1759
1855
  kitContracts: {
1760
1856
  bridge: BRIDGE_CONTRACT_EVM_TESTNET,
@@ -1841,6 +1937,10 @@ const Codex = defineChain({
1841
1937
  fastConfirmations: 1,
1842
1938
  },
1843
1939
  },
1940
+ forwarderSupported: {
1941
+ source: true,
1942
+ destination: false,
1943
+ },
1844
1944
  },
1845
1945
  kitContracts: {
1846
1946
  bridge: BRIDGE_CONTRACT_EVM_MAINNET,
@@ -1879,6 +1979,10 @@ const CodexTestnet = defineChain({
1879
1979
  fastConfirmations: 1,
1880
1980
  },
1881
1981
  },
1982
+ forwarderSupported: {
1983
+ source: true,
1984
+ destination: false,
1985
+ },
1882
1986
  },
1883
1987
  kitContracts: {
1884
1988
  bridge: BRIDGE_CONTRACT_EVM_TESTNET,
@@ -1923,6 +2027,10 @@ const Ethereum = defineChain({
1923
2027
  fastConfirmations: 2,
1924
2028
  },
1925
2029
  },
2030
+ forwarderSupported: {
2031
+ source: true,
2032
+ destination: true,
2033
+ },
1926
2034
  },
1927
2035
  kitContracts: {
1928
2036
  bridge: BRIDGE_CONTRACT_EVM_MAINNET,
@@ -1967,6 +2075,10 @@ const EthereumSepolia = defineChain({
1967
2075
  fastConfirmations: 2,
1968
2076
  },
1969
2077
  },
2078
+ forwarderSupported: {
2079
+ source: true,
2080
+ destination: true,
2081
+ },
1970
2082
  },
1971
2083
  kitContracts: {
1972
2084
  bridge: BRIDGE_CONTRACT_EVM_TESTNET,
@@ -2053,6 +2165,10 @@ const HyperEVM = defineChain({
2053
2165
  fastConfirmations: 1,
2054
2166
  },
2055
2167
  },
2168
+ forwarderSupported: {
2169
+ source: true,
2170
+ destination: true,
2171
+ },
2056
2172
  },
2057
2173
  kitContracts: {
2058
2174
  bridge: BRIDGE_CONTRACT_EVM_MAINNET,
@@ -2092,6 +2208,10 @@ const HyperEVMTestnet = defineChain({
2092
2208
  fastConfirmations: 1,
2093
2209
  },
2094
2210
  },
2211
+ forwarderSupported: {
2212
+ source: true,
2213
+ destination: true,
2214
+ },
2095
2215
  },
2096
2216
  kitContracts: {
2097
2217
  bridge: BRIDGE_CONTRACT_EVM_TESTNET,
@@ -2135,6 +2255,10 @@ const Ink = defineChain({
2135
2255
  fastConfirmations: 1,
2136
2256
  },
2137
2257
  },
2258
+ forwarderSupported: {
2259
+ source: true,
2260
+ destination: true,
2261
+ },
2138
2262
  },
2139
2263
  kitContracts: {
2140
2264
  bridge: BRIDGE_CONTRACT_EVM_MAINNET,
@@ -2177,6 +2301,10 @@ const InkTestnet = defineChain({
2177
2301
  fastConfirmations: 1,
2178
2302
  },
2179
2303
  },
2304
+ forwarderSupported: {
2305
+ source: true,
2306
+ destination: true,
2307
+ },
2180
2308
  },
2181
2309
  kitContracts: {
2182
2310
  bridge: BRIDGE_CONTRACT_EVM_TESTNET,
@@ -2215,6 +2343,10 @@ const Linea = defineChain({
2215
2343
  fastConfirmations: 1,
2216
2344
  },
2217
2345
  },
2346
+ forwarderSupported: {
2347
+ source: true,
2348
+ destination: true,
2349
+ },
2218
2350
  },
2219
2351
  kitContracts: {
2220
2352
  bridge: BRIDGE_CONTRACT_EVM_MAINNET,
@@ -2253,6 +2385,98 @@ const LineaSepolia = defineChain({
2253
2385
  fastConfirmations: 1,
2254
2386
  },
2255
2387
  },
2388
+ forwarderSupported: {
2389
+ source: true,
2390
+ destination: true,
2391
+ },
2392
+ },
2393
+ kitContracts: {
2394
+ bridge: BRIDGE_CONTRACT_EVM_TESTNET,
2395
+ },
2396
+ });
2397
+
2398
+ /**
2399
+ * Monad Mainnet chain definition
2400
+ * @remarks
2401
+ * This represents the official production network for the Monad blockchain.
2402
+ * Monad is a high-performance EVM-compatible Layer-1 blockchain featuring
2403
+ * over 10,000 TPS, sub-second finality, and near-zero gas fees.
2404
+ */
2405
+ const Monad = defineChain({
2406
+ type: 'evm',
2407
+ chain: Blockchain.Monad,
2408
+ name: 'Monad',
2409
+ title: 'Monad Mainnet',
2410
+ nativeCurrency: {
2411
+ name: 'Monad',
2412
+ symbol: 'MON',
2413
+ decimals: 18,
2414
+ },
2415
+ chainId: 143,
2416
+ isTestnet: false,
2417
+ explorerUrl: 'https://monadscan.com/tx/{hash}',
2418
+ rpcEndpoints: ['https://rpc.monad.xyz'],
2419
+ eurcAddress: null,
2420
+ usdcAddress: '0x754704Bc059F8C67012fEd69BC8A327a5aafb603',
2421
+ cctp: {
2422
+ domain: 15,
2423
+ contracts: {
2424
+ v2: {
2425
+ type: 'split',
2426
+ tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
2427
+ messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
2428
+ confirmations: 1,
2429
+ fastConfirmations: 1,
2430
+ },
2431
+ },
2432
+ forwarderSupported: {
2433
+ source: true,
2434
+ destination: true,
2435
+ },
2436
+ },
2437
+ kitContracts: {
2438
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
2439
+ },
2440
+ });
2441
+
2442
+ /**
2443
+ * Monad Testnet chain definition
2444
+ * @remarks
2445
+ * This represents the official test network for the Monad blockchain.
2446
+ * Monad is a high-performance EVM-compatible Layer-1 blockchain featuring
2447
+ * over 10,000 TPS, sub-second finality, and near-zero gas fees.
2448
+ */
2449
+ const MonadTestnet = defineChain({
2450
+ type: 'evm',
2451
+ chain: Blockchain.Monad_Testnet,
2452
+ name: 'Monad Testnet',
2453
+ title: 'Monad Testnet',
2454
+ nativeCurrency: {
2455
+ name: 'Monad',
2456
+ symbol: 'MON',
2457
+ decimals: 18,
2458
+ },
2459
+ chainId: 10143,
2460
+ isTestnet: true,
2461
+ explorerUrl: 'https://testnet.monadscan.com/tx/{hash}',
2462
+ rpcEndpoints: ['https://testnet-rpc.monad.xyz'],
2463
+ eurcAddress: null,
2464
+ usdcAddress: '0x534b2f3A21130d7a60830c2Df862319e593943A3',
2465
+ cctp: {
2466
+ domain: 15,
2467
+ contracts: {
2468
+ v2: {
2469
+ type: 'split',
2470
+ tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
2471
+ messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
2472
+ confirmations: 1,
2473
+ fastConfirmations: 1,
2474
+ },
2475
+ },
2476
+ forwarderSupported: {
2477
+ source: true,
2478
+ destination: true,
2479
+ },
2256
2480
  },
2257
2481
  kitContracts: {
2258
2482
  bridge: BRIDGE_CONTRACT_EVM_TESTNET,
@@ -2334,6 +2558,10 @@ const Noble = defineChain({
2334
2558
  confirmations: 1,
2335
2559
  },
2336
2560
  },
2561
+ forwarderSupported: {
2562
+ source: false,
2563
+ destination: false,
2564
+ },
2337
2565
  },
2338
2566
  });
2339
2567
 
@@ -2366,6 +2594,10 @@ const NobleTestnet = defineChain({
2366
2594
  confirmations: 1,
2367
2595
  },
2368
2596
  },
2597
+ forwarderSupported: {
2598
+ source: false,
2599
+ destination: false,
2600
+ },
2369
2601
  },
2370
2602
  });
2371
2603
 
@@ -2407,6 +2639,10 @@ const Optimism = defineChain({
2407
2639
  fastConfirmations: 1,
2408
2640
  },
2409
2641
  },
2642
+ forwarderSupported: {
2643
+ source: true,
2644
+ destination: true,
2645
+ },
2410
2646
  },
2411
2647
  kitContracts: {
2412
2648
  bridge: BRIDGE_CONTRACT_EVM_MAINNET,
@@ -2451,6 +2687,10 @@ const OptimismSepolia = defineChain({
2451
2687
  fastConfirmations: 1,
2452
2688
  },
2453
2689
  },
2690
+ forwarderSupported: {
2691
+ source: true,
2692
+ destination: true,
2693
+ },
2454
2694
  },
2455
2695
  kitContracts: {
2456
2696
  bridge: BRIDGE_CONTRACT_EVM_TESTNET,
@@ -2491,6 +2731,10 @@ const Plume = defineChain({
2491
2731
  fastConfirmations: 1,
2492
2732
  },
2493
2733
  },
2734
+ forwarderSupported: {
2735
+ source: true,
2736
+ destination: false,
2737
+ },
2494
2738
  },
2495
2739
  kitContracts: {
2496
2740
  bridge: BRIDGE_CONTRACT_EVM_MAINNET,
@@ -2530,6 +2774,10 @@ const PlumeTestnet = defineChain({
2530
2774
  fastConfirmations: 1,
2531
2775
  },
2532
2776
  },
2777
+ forwarderSupported: {
2778
+ source: true,
2779
+ destination: false,
2780
+ },
2533
2781
  },
2534
2782
  kitContracts: {
2535
2783
  bridge: BRIDGE_CONTRACT_EVM_TESTNET,
@@ -2620,6 +2868,10 @@ const Polygon = defineChain({
2620
2868
  fastConfirmations: 13,
2621
2869
  },
2622
2870
  },
2871
+ forwarderSupported: {
2872
+ source: true,
2873
+ destination: true,
2874
+ },
2623
2875
  },
2624
2876
  kitContracts: {
2625
2877
  bridge: BRIDGE_CONTRACT_EVM_MAINNET,
@@ -2664,6 +2916,10 @@ const PolygonAmoy = defineChain({
2664
2916
  fastConfirmations: 13,
2665
2917
  },
2666
2918
  },
2919
+ forwarderSupported: {
2920
+ source: true,
2921
+ destination: true,
2922
+ },
2667
2923
  },
2668
2924
  kitContracts: {
2669
2925
  bridge: BRIDGE_CONTRACT_EVM_TESTNET,
@@ -2704,6 +2960,10 @@ const Sei = defineChain({
2704
2960
  fastConfirmations: 1,
2705
2961
  },
2706
2962
  },
2963
+ forwarderSupported: {
2964
+ source: true,
2965
+ destination: true,
2966
+ },
2707
2967
  },
2708
2968
  kitContracts: {
2709
2969
  bridge: BRIDGE_CONTRACT_EVM_MAINNET,
@@ -2743,6 +3003,10 @@ const SeiTestnet = defineChain({
2743
3003
  fastConfirmations: 1,
2744
3004
  },
2745
3005
  },
3006
+ forwarderSupported: {
3007
+ source: true,
3008
+ destination: true,
3009
+ },
2746
3010
  },
2747
3011
  kitContracts: {
2748
3012
  bridge: BRIDGE_CONTRACT_EVM_TESTNET,
@@ -2781,6 +3045,10 @@ const Sonic = defineChain({
2781
3045
  fastConfirmations: 1,
2782
3046
  },
2783
3047
  },
3048
+ forwarderSupported: {
3049
+ source: true,
3050
+ destination: true,
3051
+ },
2784
3052
  },
2785
3053
  kitContracts: {
2786
3054
  bridge: BRIDGE_CONTRACT_EVM_MAINNET,
@@ -2819,6 +3087,10 @@ const SonicTestnet = defineChain({
2819
3087
  fastConfirmations: 1,
2820
3088
  },
2821
3089
  },
3090
+ forwarderSupported: {
3091
+ source: true,
3092
+ destination: true,
3093
+ },
2822
3094
  },
2823
3095
  kitContracts: {
2824
3096
  bridge: BRIDGE_CONTRACT_EVM_TESTNET,
@@ -2862,6 +3134,10 @@ const Solana = defineChain({
2862
3134
  fastConfirmations: 3,
2863
3135
  },
2864
3136
  },
3137
+ forwarderSupported: {
3138
+ source: true,
3139
+ destination: false,
3140
+ },
2865
3141
  },
2866
3142
  kitContracts: {
2867
3143
  bridge: 'DFaauJEjmiHkPs1JG89A4p95hDWi9m9SAEERY1LQJiC3',
@@ -2904,6 +3180,10 @@ const SolanaDevnet = defineChain({
2904
3180
  fastConfirmations: 3,
2905
3181
  },
2906
3182
  },
3183
+ forwarderSupported: {
3184
+ source: true,
3185
+ destination: false,
3186
+ },
2907
3187
  },
2908
3188
  kitContracts: {
2909
3189
  bridge: 'DFaauJEjmiHkPs1JG89A4p95hDWi9m9SAEERY1LQJiC3',
@@ -2987,6 +3267,10 @@ const Sui = defineChain({
2987
3267
  confirmations: 1,
2988
3268
  },
2989
3269
  },
3270
+ forwarderSupported: {
3271
+ source: false,
3272
+ destination: false,
3273
+ },
2990
3274
  },
2991
3275
  });
2992
3276
 
@@ -3020,6 +3304,10 @@ const SuiTestnet = defineChain({
3020
3304
  confirmations: 1,
3021
3305
  },
3022
3306
  },
3307
+ forwarderSupported: {
3308
+ source: false,
3309
+ destination: false,
3310
+ },
3023
3311
  },
3024
3312
  });
3025
3313
 
@@ -3041,7 +3329,7 @@ const Unichain = defineChain({
3041
3329
  chainId: 130,
3042
3330
  isTestnet: false,
3043
3331
  explorerUrl: 'https://unichain.blockscout.com/tx/{hash}',
3044
- rpcEndpoints: ['https://rpc.unichain.org', 'https://mainnet.unichain.org'],
3332
+ rpcEndpoints: ['https://mainnet.unichain.org'],
3045
3333
  eurcAddress: null,
3046
3334
  usdcAddress: '0x078D782b760474a361dDA0AF3839290b0EF57AD6',
3047
3335
  cctp: {
@@ -3061,6 +3349,10 @@ const Unichain = defineChain({
3061
3349
  fastConfirmations: 1,
3062
3350
  },
3063
3351
  },
3352
+ forwarderSupported: {
3353
+ source: true,
3354
+ destination: true,
3355
+ },
3064
3356
  },
3065
3357
  kitContracts: {
3066
3358
  bridge: BRIDGE_CONTRACT_EVM_MAINNET,
@@ -3105,6 +3397,10 @@ const UnichainSepolia = defineChain({
3105
3397
  fastConfirmations: 1,
3106
3398
  },
3107
3399
  },
3400
+ forwarderSupported: {
3401
+ source: true,
3402
+ destination: true,
3403
+ },
3108
3404
  },
3109
3405
  kitContracts: {
3110
3406
  bridge: BRIDGE_CONTRACT_EVM_TESTNET,
@@ -3131,7 +3427,7 @@ const WorldChain = defineChain({
3131
3427
  explorerUrl: 'https://worldscan.org/tx/{hash}',
3132
3428
  rpcEndpoints: ['https://worldchain-mainnet.g.alchemy.com/public'],
3133
3429
  eurcAddress: null,
3134
- usdcAddress: '0x79A02482A880bCe3F13E09da970dC34dB4cD24D1',
3430
+ usdcAddress: '0x79A02482A880bCE3F13e09Da970dC34db4CD24d1',
3135
3431
  cctp: {
3136
3432
  domain: 14,
3137
3433
  contracts: {
@@ -3143,6 +3439,10 @@ const WorldChain = defineChain({
3143
3439
  fastConfirmations: 1,
3144
3440
  },
3145
3441
  },
3442
+ forwarderSupported: {
3443
+ source: true,
3444
+ destination: true,
3445
+ },
3146
3446
  },
3147
3447
  kitContracts: {
3148
3448
  bridge: BRIDGE_CONTRACT_EVM_MAINNET,
@@ -3184,6 +3484,10 @@ const WorldChainSepolia = defineChain({
3184
3484
  fastConfirmations: 1,
3185
3485
  },
3186
3486
  },
3487
+ forwarderSupported: {
3488
+ source: true,
3489
+ destination: true,
3490
+ },
3187
3491
  },
3188
3492
  kitContracts: {
3189
3493
  bridge: BRIDGE_CONTRACT_EVM_TESTNET,
@@ -3210,7 +3514,7 @@ const XDC = defineChain({
3210
3514
  chainId: 50,
3211
3515
  isTestnet: false,
3212
3516
  explorerUrl: 'https://xdcscan.io/tx/{hash}',
3213
- rpcEndpoints: ['https://erpc.xinfin.network'],
3517
+ rpcEndpoints: ['https://erpc.xdcrpc.com', 'https://erpc.xinfin.network'],
3214
3518
  eurcAddress: null,
3215
3519
  usdcAddress: '0xfA2958CB79b0491CC627c1557F441eF849Ca8eb1',
3216
3520
  cctp: {
@@ -3224,6 +3528,10 @@ const XDC = defineChain({
3224
3528
  fastConfirmations: 3,
3225
3529
  },
3226
3530
  },
3531
+ forwarderSupported: {
3532
+ source: true,
3533
+ destination: false,
3534
+ },
3227
3535
  },
3228
3536
  kitContracts: {
3229
3537
  bridge: BRIDGE_CONTRACT_EVM_MAINNET,
@@ -3262,6 +3570,10 @@ const XDCApothem = defineChain({
3262
3570
  fastConfirmations: 1,
3263
3571
  },
3264
3572
  },
3573
+ forwarderSupported: {
3574
+ source: true,
3575
+ destination: false,
3576
+ },
3265
3577
  },
3266
3578
  kitContracts: {
3267
3579
  bridge: BRIDGE_CONTRACT_EVM_TESTNET,
@@ -3343,6 +3655,8 @@ var Blockchains = /*#__PURE__*/Object.freeze({
3343
3655
  InkTestnet: InkTestnet,
3344
3656
  Linea: Linea,
3345
3657
  LineaSepolia: LineaSepolia,
3658
+ Monad: Monad,
3659
+ MonadTestnet: MonadTestnet,
3346
3660
  NEAR: NEAR,
3347
3661
  NEARTestnet: NEARTestnet,
3348
3662
  Noble: Noble,
@@ -4371,9 +4685,8 @@ const transactionHashSchema = z
4371
4685
  required_error: 'Transaction hash is required',
4372
4686
  invalid_type_error: 'Transaction hash must be a string',
4373
4687
  })
4374
- .min(1, 'Transaction hash cannot be empty')
4375
- .transform((hash) => hash.trim()) // Automatically trim whitespace
4376
- .refine((hash) => hash.length > 0, 'Transaction hash must not be empty or whitespace-only');
4688
+ .transform((hash) => hash.trim())
4689
+ .refine((hash) => hash.length > 0, 'Transaction hash cannot be empty');
4377
4690
  /**
4378
4691
  * Zod schema for validating buildExplorerUrl function parameters.
4379
4692
  * This schema validates both the chain definition and transaction hash together.
@@ -8289,28 +8602,79 @@ const bridgeContractAbi = [
8289
8602
  const ZERO_HASH = '0x0000000000000000000000000000000000000000000000000000000000000000';
8290
8603
 
8291
8604
  /**
8292
- * Prepares an EVM-compatible `depositForBurn` transaction for CCTP v2.
8605
+ * Prepare an EVM-compatible deposit for burn transaction for CCTP v2.
8293
8606
  *
8294
- * This function creates a prepared chain request for burning USDC tokens on the source EVM chain
8295
- * to initiate a cross-chain transfer using Circle's CCTP v2 protocol. It leverages the adapter's
8296
- * chain context and transaction preparation utilities to construct a transaction targeting the
8297
- * TokenMessengerV2 contract.
8607
+ * This is the shared implementation for both `depositForBurn` and `depositForBurnWithHook` actions.
8608
+ * It creates a prepared chain request for burning USDC tokens on the source EVM chain to initiate
8609
+ * a cross-chain transfer using Circle's CCTP v2 protocol.
8298
8610
  *
8299
- * The function validates that the `fromChain` is EVM-compatible, then constructs a transaction
8300
- * using the canonical TokenMessengerV2 ABI and the resolved contract address for the source chain.
8301
- * The resulting prepared request can be executed or simulated by the caller.
8611
+ * The function automatically selects the appropriate contract method based on whether hookData
8612
+ * is present:
8613
+ * - Without hookData `depositForBurn`
8614
+ * - With hookData → `depositForBurnWithHook`
8615
+ *
8616
+ * @param params - The action payload (either depositForBurn or depositForBurnWithHook params).
8617
+ * @param adapter - The EVM adapter responsible for chain context and transaction preparation.
8618
+ * @param context - The resolved operation context providing chain and address information.
8619
+ * @returns A promise that resolves to a prepared chain request.
8620
+ * @throws KitError if the `fromChain` is not EVM-compatible.
8621
+ * @throws KitError if `hookData` is provided but is not a valid hex string.
8622
+ *
8623
+ * @internal This function is used internally by depositForBurn and depositForBurnWithHook wrappers.
8624
+ */
8625
+ const prepareDepositForBurn = async (params, adapter, context) => {
8626
+ if (params.fromChain.type !== 'evm') {
8627
+ throw createInvalidChainError(params.fromChain.name, `Expected EVM chain but received chain type: ${params.fromChain.type}`);
8628
+ }
8629
+ // Detect if hookData is present (depositForBurnWithHook action)
8630
+ const hookData = 'hookData' in params ? params.hookData : undefined;
8631
+ // Build args array - base args are the same for both variants
8632
+ const args = [
8633
+ params.amount,
8634
+ params.toChain.cctp.domain,
8635
+ convertAddress(params.mintRecipient, 'bytes32'),
8636
+ params.fromChain.usdcAddress,
8637
+ params.destinationCaller ?? ZERO_HASH,
8638
+ params.maxFee,
8639
+ params.minFinalityThreshold,
8640
+ ];
8641
+ const hasHookData = hookData !== undefined && hookData !== '';
8642
+ // Add hookData as the last argument if present, after validating format
8643
+ if (hasHookData) {
8644
+ const result = hexStringSchema.safeParse(hookData);
8645
+ if (!result.success) {
8646
+ throw createValidationFailedError('hookData', hookData, 'Must be a valid hex string starting with 0x');
8647
+ }
8648
+ args.push(hookData);
8649
+ }
8650
+ // Select function name based on hookData presence
8651
+ const functionName = hasHookData ? 'depositForBurnWithHook' : 'depositForBurn';
8652
+ return adapter.prepare({
8653
+ type: 'evm',
8654
+ abi: tokenMessengerV2Abi,
8655
+ address: resolveCCTPV2ContractAddress(params.fromChain, 'tokenMessenger'),
8656
+ functionName,
8657
+ args,
8658
+ }, context);
8659
+ };
8660
+
8661
+ /**
8662
+ * Prepare an EVM-compatible `depositForBurn` transaction for CCTP v2.
8663
+ *
8664
+ * This function creates a prepared chain request for burning USDC tokens on the source EVM chain
8665
+ * to initiate a cross-chain transfer using Circle's CCTP v2 protocol.
8302
8666
  *
8303
8667
  * @param params - The action payload containing:
8304
8668
  * - `fromChain`: The EVM chain definition where the burn will occur.
8305
8669
  * - `toChain`: The destination chain definition (must include CCTP domain).
8306
- * - `amount`: The amount of USDC to burn (as a string, in the smallest unit).
8307
- * - `mintRecipient`: The address (as string) to receive minted USDC on the destination chain.
8670
+ * - `amount`: The amount of USDC to burn (as bigint, in the smallest unit).
8671
+ * - `mintRecipient`: The address to receive minted USDC on the destination chain.
8308
8672
  * - `maxFee`: The maximum fee to pay for the cross-chain message relay.
8309
8673
  * - `minFinalityThreshold`: The minimum finality threshold for the burn event.
8310
8674
  * @param adapter - The EVM adapter responsible for chain context and transaction preparation.
8311
8675
  * @param context - The resolved operation context providing chain and address information.
8312
8676
  * @returns A promise that resolves to a prepared chain request for the `depositForBurn` call.
8313
- * @throws Error if the `fromChain` is not EVM-compatible or if preparation fails.
8677
+ * @throws KitError if the `fromChain` is not EVM-compatible or if preparation fails.
8314
8678
  *
8315
8679
  * @example
8316
8680
  * ```typescript
@@ -8318,37 +8682,63 @@ const ZERO_HASH = '0x00000000000000000000000000000000000000000000000000000000000
8318
8682
  * {
8319
8683
  * fromChain,
8320
8684
  * toChain,
8321
- * amount: '1000000',
8685
+ * amount: 1000000n,
8322
8686
  * mintRecipient: '0xabc...',
8323
8687
  * maxFee: 0n,
8324
- * minFinalityThreshold: 1n,
8688
+ * minFinalityThreshold: 1000,
8325
8689
  * },
8326
8690
  * adapter,
8327
8691
  * context
8328
- * );
8329
- * await prepared.execute();
8692
+ * )
8693
+ * await prepared.execute()
8330
8694
  * ```
8331
8695
  */
8332
8696
  const depositForBurn = async (params, adapter, context) => {
8333
- if (params.fromChain.type !== 'evm') {
8334
- throw new Error(`Expected fromChain to be EVM chain definition, but received chain type: ${params.fromChain.type}`);
8335
- }
8336
- // Prepare the deposit for burn transaction
8337
- return adapter.prepare({
8338
- type: 'evm',
8339
- abi: tokenMessengerV2Abi,
8340
- address: resolveCCTPV2ContractAddress(params.fromChain, 'tokenMessenger'),
8341
- functionName: 'depositForBurn',
8342
- args: [
8343
- BigInt(params.amount),
8344
- params.toChain.cctp.domain,
8345
- convertAddress(params.mintRecipient, 'bytes32'),
8346
- params.fromChain.usdcAddress,
8347
- params.destinationCaller ?? ZERO_HASH, // destinationCaller as ZeroHash (allows any caller) if not provided
8348
- params.maxFee,
8349
- params.minFinalityThreshold,
8350
- ],
8351
- }, context);
8697
+ return prepareDepositForBurn(params, adapter, context);
8698
+ };
8699
+
8700
+ /**
8701
+ * Prepare an EVM-compatible `depositForBurnWithHook` transaction for CCTP v2 forwarding.
8702
+ *
8703
+ * This function creates a prepared chain request for burning USDC tokens on the source EVM chain
8704
+ * with hook data that signals to Circle's Orbit relayer that forwarding is requested. The relayer
8705
+ * will automatically fetch the attestation and submit the destination mint transaction.
8706
+ *
8707
+ * @param params - The action payload containing:
8708
+ * - `fromChain`: The EVM chain definition where the burn will occur.
8709
+ * - `toChain`: The destination chain definition (must include CCTP domain).
8710
+ * - `amount`: The amount of USDC to burn (as bigint, in the smallest unit).
8711
+ * - `mintRecipient`: The address to receive minted USDC on the destination chain.
8712
+ * - `maxFee`: The maximum fee to pay (must cover burn fee + forwarding fee).
8713
+ * - `minFinalityThreshold`: The minimum finality threshold for the burn event.
8714
+ * - `hookData`: Hex-encoded hook data for CCTP forwarding.
8715
+ * @param adapter - The EVM adapter responsible for chain context and transaction preparation.
8716
+ * @param context - The resolved operation context providing chain and address information.
8717
+ * @returns A promise that resolves to a prepared chain request for the `depositForBurnWithHook` call.
8718
+ * @throws KitError if the `fromChain` is not EVM-compatible or if preparation fails.
8719
+ *
8720
+ * @example
8721
+ * ```typescript
8722
+ * import { buildForwardingHookData } from '@core/utils'
8723
+ *
8724
+ * const prepared = await depositForBurnWithHook(
8725
+ * {
8726
+ * fromChain,
8727
+ * toChain,
8728
+ * amount: 1000000n,
8729
+ * mintRecipient: '0xabc...',
8730
+ * maxFee: 50000n,
8731
+ * minFinalityThreshold: 1000,
8732
+ * hookData: buildForwardingHookData(),
8733
+ * },
8734
+ * adapter,
8735
+ * context
8736
+ * )
8737
+ * await prepared.execute()
8738
+ * ```
8739
+ */
8740
+ const depositForBurnWithHook = async (params, adapter, context) => {
8741
+ return prepareDepositForBurn(params, adapter, context);
8352
8742
  };
8353
8743
 
8354
8744
  /**
@@ -8399,8 +8789,317 @@ const receiveMessage = async (params, adapter, context) => {
8399
8789
  }, context);
8400
8790
  };
8401
8791
 
8792
+ // ============================================================================
8793
+ // Validation Helper Functions
8794
+ // ============================================================================
8795
+ /**
8796
+ * Validate that the source chain is EVM-compatible and supports custom bridge contracts.
8797
+ *
8798
+ * Custom bridge contracts are Circle's kit-specific contracts that provide
8799
+ * additional functionality beyond the standard CCTP TokenMessenger, such as:
8800
+ * - Permit-based gasless approvals (EIP-2612)
8801
+ * - Protocol fee collection
8802
+ * - Hook data for automatic forwarding
8803
+ *
8804
+ * Not all chains have custom bridge contracts deployed. For chains without
8805
+ * custom bridge support, users should use `cctp.v2.depositForBurn` instead,
8806
+ * which interacts directly with Circle's TokenMessenger contract.
8807
+ *
8808
+ * @param params - The custom burn parameters containing chain definitions.
8809
+ * @throws KitError if the source chain is not EVM (e.g., Solana).
8810
+ * @throws KitError if the chain doesn't have a custom bridge contract deployed.
8811
+ */
8812
+ function validateChainSupport(params) {
8813
+ // Ensure source chain is EVM - Solana uses a different code path
8814
+ if (params.fromChain.type !== 'evm') {
8815
+ throw createInvalidChainError(params.fromChain.name, `Expected EVM chain but received chain type: ${params.fromChain.type}`);
8816
+ }
8817
+ // Check if the chain has a custom bridge contract in its configuration
8818
+ if (!hasCustomContractSupport(params.fromChain, 'bridge')) {
8819
+ throw createInvalidChainError(params.fromChain.name, "Chain does not support custom bridge contracts. Use 'cctp.v2.depositForBurn' instead.");
8820
+ }
8821
+ }
8822
+ /**
8823
+ * Validate the logical consistency of protocol fee parameters.
8824
+ *
8825
+ * The protocol fee mechanism allows integrators to collect a fee on each
8826
+ * cross-chain transfer. When enabled, the fee is deducted from the transfer
8827
+ * amount and sent to the specified fee recipient.
8828
+ *
8829
+ * Business rules:
8830
+ * - protocolFee MUST be non-negative
8831
+ * - If protocolFee is non-zero, feeRecipient MUST be specified (where does the fee go?)
8832
+ * - If feeRecipient is specified, protocolFee MUST be non-zero (why specify recipient for zero fee?)
8833
+ * - When feeRecipient is provided, it must be a valid EVM address
8834
+ *
8835
+ * @param protocolFee - The fee amount (defaults to 0n if not provided).
8836
+ * @param feeRecipient - The address to receive the fee (optional).
8837
+ * @throws KitError if protocolFee is negative.
8838
+ * @throws KitError if protocolFee is set but feeRecipient is missing.
8839
+ * @throws KitError if feeRecipient is set but protocolFee is zero.
8840
+ * @throws KitError if feeRecipient is not a valid EVM address.
8841
+ */
8842
+ function validateProtocolFeeParams(protocolFee, feeRecipient) {
8843
+ // Validate: fee must be non-negative
8844
+ if (protocolFee < 0n) {
8845
+ throw createValidationFailedError('protocolFee', protocolFee, 'Must be non-negative');
8846
+ }
8847
+ // Validate: fee without recipient makes no sense
8848
+ if (protocolFee > 0n && feeRecipient === undefined) {
8849
+ throw createValidationFailedError('protocolFee', protocolFee, 'Requires feeRecipient. Specify where the fee should be sent');
8850
+ }
8851
+ // Validate: recipient without fee makes no sense
8852
+ if (feeRecipient !== undefined && protocolFee === 0n) {
8853
+ throw createValidationFailedError('feeRecipient', feeRecipient, 'Requires non-zero protocolFee. Specify the fee amount to send');
8854
+ }
8855
+ // Validate the fee recipient address format if provided
8856
+ if (feeRecipient !== undefined) {
8857
+ assertEvmAddress(feeRecipient);
8858
+ }
8859
+ }
8860
+ /**
8861
+ * Validate EIP-2612 permit signature components if provided.
8862
+ *
8863
+ * EIP-2612 permits allow gasless token approvals via off-chain signatures.
8864
+ * Instead of the user calling `approve()` on the USDC contract (which costs gas),
8865
+ * they sign a message off-chain, and the bridge contract calls `permit()` on
8866
+ * their behalf as part of the bridge transaction.
8867
+ *
8868
+ * The permit signature consists of:
8869
+ * - `deadline`: Unix timestamp after which the permit expires
8870
+ * - `v`: ECDSA signature recovery parameter (27 or 28)
8871
+ * - `r`: ECDSA signature component (32 bytes)
8872
+ * - `s`: ECDSA signature component (32 bytes)
8873
+ *
8874
+ * @param permitParams - The permit parameters including deadline and signature.
8875
+ * @throws KitError if the permit deadline has already passed.
8876
+ * @throws KitError if the signature recovery parameter (v) is invalid.
8877
+ */
8878
+ function validatePermitParams(permitParams) {
8879
+ // Early return if no permit params - caller will use preapproval flow instead
8880
+ if (!permitParams) {
8881
+ return;
8882
+ }
8883
+ // Check that the permit hasn't expired
8884
+ // Deadline is compared against current Unix timestamp (seconds since epoch)
8885
+ const currentTimestamp = BigInt(Math.floor(Date.now() / 1000));
8886
+ if (permitParams.deadline <= currentTimestamp) {
8887
+ throw createValidationFailedError('permitParams.deadline', permitParams.deadline, 'Permit deadline has expired');
8888
+ }
8889
+ // Validate ECDSA signature recovery parameter (v).
8890
+ // In the EVM's ECDSA implementation, 'v' is the recovery identifier that
8891
+ // allows the public key to be recovered from the signature. Per EIP-155,
8892
+ // valid values are:
8893
+ // - 27: corresponds to recovery id 0 (even y-coordinate)
8894
+ // - 28: corresponds to recovery id 1 (odd y-coordinate)
8895
+ // These are the only valid values for standard Ethereum signatures.
8896
+ const VALID_RECOVERY_PARAMS = [27, 28];
8897
+ if (!VALID_RECOVERY_PARAMS.includes(permitParams.v)) {
8898
+ throw createValidationFailedError('permitParams.v', permitParams.v, 'Invalid ECDSA recovery parameter. Must be 27 or 28');
8899
+ }
8900
+ }
8901
+ /**
8902
+ * Get and validate the custom bridge contract address from chain definition.
8903
+ *
8904
+ * The bridge contract address is stored in the chain definition under
8905
+ * `kitContracts.bridge`. This function retrieves it and validates that
8906
+ * it's a properly formatted EVM address (0x-prefixed, 40 hex characters).
8907
+ *
8908
+ * @param fromChain - The source EVM chain definition.
8909
+ * @returns The validated bridge contract address.
8910
+ * @throws KitError if no bridge contract address is configured for the chain.
8911
+ * @throws KitError if the address format is invalid.
8912
+ */
8913
+ function getBridgeAddress(fromChain) {
8914
+ const bridgeAddress = fromChain.kitContracts?.bridge;
8915
+ if (bridgeAddress === undefined) {
8916
+ throw createInvalidChainError(fromChain.name, 'No bridge contract address found in chain configuration');
8917
+ }
8918
+ // Validate address format (throws if invalid)
8919
+ assertEvmAddress(bridgeAddress);
8920
+ return bridgeAddress;
8921
+ }
8922
+ // ============================================================================
8923
+ // Contract Call Builder Functions
8924
+ // ============================================================================
8925
+ /**
8926
+ * Build the BridgeParams struct for the smart contract call.
8927
+ *
8928
+ * Construct the struct from user-provided parameters, applying defaults and
8929
+ * converting addresses to bytes32 format as required by CCTP's cross-chain messaging.
8930
+ *
8931
+ * @param params - The custom burn parameters from the user.
8932
+ * @param protocolFee - The validated protocol fee amount.
8933
+ * @param bridgeAddress - The bridge contract address (used as default fee recipient).
8934
+ * @returns The BridgeParams struct ready for the contract call.
8935
+ */
8936
+ function buildBridgeParams(params, protocolFee, bridgeAddress) {
8937
+ const fromChain = params.fromChain;
8938
+ return {
8939
+ amount: params.amount,
8940
+ maxFee: params.maxFee,
8941
+ fee: protocolFee,
8942
+ // Convert recipient to bytes32 for cross-chain compatibility
8943
+ mintRecipient: convertAddress(params.mintRecipient, 'bytes32'),
8944
+ // Zero address means any caller can claim on destination
8945
+ destinationCaller: convertAddress(params.destinationCaller ?? ZERO_HASH, 'bytes32'),
8946
+ // USDC address on the source chain
8947
+ burnToken: fromChain.usdcAddress,
8948
+ // Fee recipient defaults to bridge contract if not specified
8949
+ feeRecipient: params.feeRecipient ?? bridgeAddress,
8950
+ // CCTP domain ID (e.g., 0 = Ethereum, 1 = Avalanche, 6 = Base)
8951
+ destinationDomain: params.toChain.cctp.domain,
8952
+ // Convert to bigint for contract compatibility
8953
+ minFinalityThreshold: BigInt(params.minFinalityThreshold),
8954
+ };
8955
+ }
8956
+ /**
8957
+ * Determine which bridge contract function to call.
8958
+ *
8959
+ * The function selection follows a 2x2 matrix based on:
8960
+ * 1. Authorization method: Has the user provided a permit signature?
8961
+ * 2. Forwarding mode: Should the relayer auto-complete the transfer?
8962
+ *
8963
+ * Decision tree:
8964
+ * - Permit + Hook → bridgeWithPermitAndHook
8965
+ * - Permit + No Hook → bridgeWithPermit
8966
+ * - No Permit + Hook → bridgeWithPreapprovalAndHook
8967
+ * - No Permit + No Hook → bridgeWithPreapproval
8968
+ *
8969
+ * @param hasPermit - True if permit params were provided.
8970
+ * @param hasHookData - True if hook data was provided for auto-forwarding.
8971
+ * @returns The name of the bridge contract function to call.
8972
+ */
8973
+ function selectBridgeFunction(hasPermit, hasHookData) {
8974
+ if (hasPermit) {
8975
+ return hasHookData ? 'bridgeWithPermitAndHook' : 'bridgeWithPermit';
8976
+ }
8977
+ return hasHookData ? 'bridgeWithPreapprovalAndHook' : 'bridgeWithPreapproval';
8978
+ }
8979
+ /**
8980
+ * Build the arguments array for the bridge contract call.
8981
+ *
8982
+ * Different bridge functions expect different argument patterns:
8983
+ * - bridgeWithPreapproval(bridgeParams)
8984
+ * - bridgeWithPermit(bridgeParams, permitParams)
8985
+ * - bridgeWithPreapprovalAndHook(bridgeParams, hookData)
8986
+ * - bridgeWithPermitAndHook(bridgeParams, permitParams, hookData)
8987
+ *
8988
+ * This function constructs the correct args array based on which optional
8989
+ * parameters are present.
8990
+ *
8991
+ * @param bridgeParams - The BridgeParams struct (always first argument).
8992
+ * @param permitParams - Optional EIP-2612 permit signature.
8993
+ * @param hookData - Optional hook data for CCTP forwarding.
8994
+ * @returns The arguments array ready for the contract call.
8995
+ * @throws KitError if hookData is not a valid hex string starting with 0x.
8996
+ */
8997
+ function buildContractArgs(bridgeParams, permitParams, hookData) {
8998
+ // BridgeParams is always the first argument
8999
+ const args = [bridgeParams];
9000
+ // Add permit params if using permit-based authorization
9001
+ if (permitParams) {
9002
+ args.push({
9003
+ deadline: permitParams.deadline,
9004
+ v: permitParams.v,
9005
+ r: permitParams.r,
9006
+ s: permitParams.s,
9007
+ });
9008
+ }
9009
+ // Add hook data if using auto-forwarding, after validating format
9010
+ // Empty string is treated as "no hook data"
9011
+ if (hookData !== undefined && hookData !== '') {
9012
+ const result = hexStringSchema.safeParse(hookData);
9013
+ if (!result.success) {
9014
+ throw createValidationFailedError('hookData', hookData, 'Must be a valid hex string starting with 0x');
9015
+ }
9016
+ args.push(hookData);
9017
+ }
9018
+ return args;
9019
+ }
9020
+ // ============================================================================
9021
+ // Main Export Function
9022
+ // ============================================================================
9023
+ /**
9024
+ * Prepare an EVM-compatible custom burn transaction for CCTP v2 using a custom bridge contract.
9025
+ *
9026
+ * This is the shared implementation for both `customBurn` and `customBurnWithHook` actions.
9027
+ * It creates a prepared chain request for burning USDC tokens using a custom bridge
9028
+ * contract that supports preapproval, permit-based authorization, and optional hook data
9029
+ * for CCTP forwarding.
9030
+ *
9031
+ * **Authorization Methods**
9032
+ *
9033
+ * **Preapproval**: User has already called `approve()` on the USDC contract to allow
9034
+ * the bridge to spend their tokens. This requires a separate transaction but gives
9035
+ * the user full control over when and how much they approve.
9036
+ *
9037
+ * **Permit**: User signs an EIP-2612 permit message off-chain, and the bridge contract
9038
+ * calls `permit()` on their behalf. This enables gasless approvals in a single transaction.
9039
+ *
9040
+ * **Forwarding Modes**
9041
+ *
9042
+ * **Standard**: User must manually claim the USDC on the destination chain by submitting
9043
+ * the attestation to the destination MessageTransmitter.
9044
+ *
9045
+ * **Hook (Auto-forwarding)**: Circle's Orbit relayer automatically fetches the attestation
9046
+ * and submits the mint transaction on the destination chain. The user pays a higher fee
9047
+ * but doesn't need to interact with the destination chain.
9048
+ *
9049
+ * **Function Selection**
9050
+ *
9051
+ * The function automatically selects the appropriate bridge method based on parameters:
9052
+ * - Without hookData + without permit → `bridgeWithPreapproval`
9053
+ * - Without hookData + with permit → `bridgeWithPermit`
9054
+ * - With hookData + without permit → `bridgeWithPreapprovalAndHook`
9055
+ * - With hookData + with permit → `bridgeWithPermitAndHook`
9056
+ *
9057
+ * @param params - The action payload (either customBurn or customBurnWithHook params).
9058
+ * @param adapter - The EVM adapter responsible for chain context and transaction preparation.
9059
+ * @param context - The resolved operation context providing chain and address information.
9060
+ * @returns A promise that resolves to a prepared chain request.
9061
+ * @throws KitError if the source chain doesn't support custom bridge contracts.
9062
+ * @throws KitError if the source chain is not EVM-compatible.
9063
+ * @throws KitError if permit signature validation fails.
9064
+ * @throws KitError if transaction preparation fails.
9065
+ *
9066
+ * @internal This function is used internally by customBurn and customBurnWithHook wrappers.
9067
+ */
9068
+ const prepareCustomBurn = async (params, adapter, context) => {
9069
+ // Step 1: Validate that the source chain supports custom bridge contracts
9070
+ validateChainSupport(params);
9071
+ // Step 2: Validate protocol fee configuration
9072
+ // Default protocolFee to 0 if not specified (no fee collection)
9073
+ const protocolFee = params.protocolFee ?? 0n;
9074
+ validateProtocolFeeParams(protocolFee, params.feeRecipient);
9075
+ // Step 3: Validate permit signature if provided
9076
+ // This checks deadline and ECDSA parameters
9077
+ validatePermitParams(params.permitParams);
9078
+ // Step 4: Get the custom bridge contract address from chain config
9079
+ const fromChain = params.fromChain;
9080
+ const bridgeAddress = getBridgeAddress(fromChain);
9081
+ // Step 5: Build the BridgeParams struct for the contract
9082
+ const bridgeParams = buildBridgeParams(params, protocolFee, bridgeAddress);
9083
+ // Step 6: Detect if hookData is present and determine which function to call
9084
+ // Hook data is only present in customBurnWithHook action payload
9085
+ const hookData = 'hookData' in params ? params.hookData : undefined;
9086
+ const hasHookData = hookData !== undefined && hookData !== '';
9087
+ const functionName = selectBridgeFunction(!!params.permitParams, hasHookData);
9088
+ // Step 7: Build the arguments array for the contract call
9089
+ const args = buildContractArgs(bridgeParams, params.permitParams, hookData);
9090
+ // Step 8: Prepare the transaction using the adapter
9091
+ // This creates a PreparedChainRequest that can be executed or simulated
9092
+ return adapter.prepare({
9093
+ type: 'evm',
9094
+ abi: bridgeContractAbi,
9095
+ address: bridgeAddress,
9096
+ functionName,
9097
+ args,
9098
+ }, context);
9099
+ };
9100
+
8402
9101
  /**
8403
- * Prepares an EVM-compatible `customBurn` transaction for CCTP v2 using a custom bridge contract.
9102
+ * Prepare an EVM-compatible `customBurn` transaction for CCTP v2 using a custom bridge contract.
8404
9103
  *
8405
9104
  * This function creates a prepared chain request for burning USDC tokens using a custom bridge
8406
9105
  * contract that supports both preapproval and permit-based token authorization. The custom
@@ -8410,9 +9109,6 @@ const receiveMessage = async (params, adapter, context) => {
8410
9109
  * - When `permitParams` is provided → uses `bridgeWithPermit` for EIP-2612 signature-based approval
8411
9110
  * - When `permitParams` is not provided → uses `bridgeWithPreapproval` for traditional preapproval
8412
9111
  *
8413
- * The action provides a unified interface for both approval methods, with extra bridge
8414
- * contract parameters (fee, burnToken, feeRecipient) automatically resolved for ease of use.
8415
- *
8416
9112
  * @param params - The action payload containing:
8417
9113
  * - `fromChain`: The EVM chain definition where the burn will occur (must support custom bridge).
8418
9114
  * - `toChain`: The destination chain definition (must include CCTP domain).
@@ -8424,17 +9120,13 @@ const receiveMessage = async (params, adapter, context) => {
8424
9120
  * - `protocolFee`: Optional protocol fee amount (defaults to 0n for basic usage).
8425
9121
  * - `feeRecipient`: Optional fee recipient address (defaults to bridge address).
8426
9122
  * - `permitParams`: Optional EIP-2612 permit signature for gasless approval.
8427
- *
8428
- * For basic usage, only the core CCTP parameters are needed (same as depositForBurn).
8429
- * For advanced usage, protocol fee parameters enable custom fee collection.
8430
- * For gasless transactions, provide permitParams to avoid separate approval transactions.
8431
9123
  * @param adapter - The EVM adapter responsible for chain context and transaction preparation.
8432
9124
  * @param context - The resolved operation context providing chain and address information.
8433
9125
  * @returns A promise that resolves to a prepared chain request for the `customBurn` operation.
8434
- * @throws Error if the source chain doesn't support custom bridge contracts.
8435
- * @throws Error if the source chain is not EVM-compatible.
8436
- * @throws Error if permit signature validation fails.
8437
- * @throws Error if transaction preparation fails.
9126
+ * @throws KitError if the source chain doesn't support custom bridge contracts.
9127
+ * @throws KitError if the source chain is not EVM-compatible.
9128
+ * @throws KitError if permit signature validation fails.
9129
+ * @throws KitError if transaction preparation fails.
8438
9130
  *
8439
9131
  * @example
8440
9132
  * ```typescript
@@ -8442,9 +9134,7 @@ const receiveMessage = async (params, adapter, context) => {
8442
9134
  *
8443
9135
  * // Check if chain supports custom bridge before using
8444
9136
  * if (hasCustomContractSupport(sourceChain, 'bridge')) {
8445
- *
8446
- * // Basic usage with preapproval (same interface as depositForBurn)
8447
- * const basicTransfer = await customBurn(
9137
+ * const prepared = await customBurn(
8448
9138
  * {
8449
9139
  * fromChain: sourceChain,
8450
9140
  * toChain: destinationChain,
@@ -8452,127 +9142,74 @@ const receiveMessage = async (params, adapter, context) => {
8452
9142
  * mintRecipient: recipientAddress,
8453
9143
  * maxFee: BigInt('1000'),
8454
9144
  * minFinalityThreshold: 65
8455
- * // protocolFee and feeRecipient auto-default
8456
9145
  * },
8457
9146
  * adapter,
8458
9147
  * context
8459
9148
  * )
9149
+ * await prepared.execute()
9150
+ * }
9151
+ * ```
9152
+ */
9153
+ const customBurn = async (params, adapter, context) => {
9154
+ return prepareCustomBurn(params, adapter, context);
9155
+ };
9156
+
9157
+ /**
9158
+ * Prepare an EVM-compatible `customBurnWithHook` transaction for CCTP v2 with forwarding.
8460
9159
  *
8461
- * // Usage with permit signature (gasless approval)
8462
- * const permitTransfer = await customBurn(
8463
- * {
8464
- * fromChain: sourceChain,
8465
- * toChain: destinationChain,
8466
- * amount: BigInt('1000000'),
8467
- * mintRecipient: recipientAddress,
8468
- * maxFee: BigInt('1000'),
8469
- * minFinalityThreshold: 65,
8470
- * permitParams: {
8471
- * deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour
8472
- * v: 27,
8473
- * r: '0x1234567890abcdef...',
8474
- * s: '0xfedcba0987654321...'
8475
- * }
8476
- * },
8477
- * adapter,
8478
- * context
8479
- * )
9160
+ * This function creates a prepared chain request for burning USDC tokens using a custom bridge
9161
+ * contract with hook data that signals to Circle's Orbit relayer that forwarding is requested.
9162
+ * The relayer will automatically fetch the attestation and submit the destination mint transaction.
9163
+ *
9164
+ * The function automatically selects the appropriate bridge method based on parameters:
9165
+ * - When `permitParams` is provided → uses `bridgeWithPermitAndHook`
9166
+ * - When `permitParams` is not provided → uses `bridgeWithPreapprovalAndHook`
8480
9167
  *
8481
- * // Advanced usage with protocol fees
8482
- * const advancedTransfer = await customBurn(
9168
+ * @param params - The action payload containing:
9169
+ * - `fromChain`: The EVM chain definition where the burn will occur (must support custom bridge).
9170
+ * - `toChain`: The destination chain definition (must include CCTP domain).
9171
+ * - `amount`: The amount of USDC to burn (as bigint, in the smallest unit).
9172
+ * - `mintRecipient`: The address to receive minted USDC on the destination chain.
9173
+ * - `destinationCaller`: Optional address authorized to call receiveMessage (zero hash if any).
9174
+ * - `maxFee`: The maximum fee to pay (must cover burn fee + forwarding fee).
9175
+ * - `minFinalityThreshold`: The minimum finality threshold for the burn event.
9176
+ * - `protocolFee`: Optional protocol fee amount (defaults to 0n for basic usage).
9177
+ * - `feeRecipient`: Optional fee recipient address (defaults to bridge address).
9178
+ * - `permitParams`: Optional EIP-2612 permit signature for gasless approval.
9179
+ * - `hookData`: Hex-encoded hook data for CCTP forwarding.
9180
+ * @param adapter - The EVM adapter responsible for chain context and transaction preparation.
9181
+ * @param context - The resolved operation context providing chain and address information.
9182
+ * @returns A promise that resolves to a prepared chain request for the `customBurnWithHook` operation.
9183
+ * @throws KitError if the source chain doesn't support custom bridge contracts.
9184
+ * @throws KitError if the source chain is not EVM-compatible.
9185
+ * @throws KitError if permit signature validation fails.
9186
+ * @throws KitError if transaction preparation fails.
9187
+ *
9188
+ * @example
9189
+ * ```typescript
9190
+ * import { hasCustomContractSupport } from '@core/chains'
9191
+ * import { buildForwardingHookData } from '@core/utils'
9192
+ *
9193
+ * if (hasCustomContractSupport(sourceChain, 'bridge')) {
9194
+ * const prepared = await customBurnWithHook(
8483
9195
  * {
8484
9196
  * fromChain: sourceChain,
8485
9197
  * toChain: destinationChain,
8486
9198
  * amount: BigInt('1000000'),
8487
9199
  * mintRecipient: recipientAddress,
8488
- * maxFee: BigInt('1000'),
9200
+ * maxFee: BigInt('50000'),
8489
9201
  * minFinalityThreshold: 65,
8490
- * protocolFee: BigInt('100'),
8491
- * feeRecipient: '0xFeeRecipientAddress'
9202
+ * hookData: buildForwardingHookData()
8492
9203
  * },
8493
9204
  * adapter,
8494
9205
  * context
8495
9206
  * )
8496
- *
8497
- * await basicTransfer.execute()
9207
+ * await prepared.execute()
8498
9208
  * }
8499
9209
  * ```
8500
9210
  */
8501
- const customBurn = async (params, adapter, context) => {
8502
- // Validate that this is an EVM chain
8503
- if (params.fromChain.type !== 'evm') {
8504
- throw new Error(`Expected fromChain to be EVM chain definition, but received chain type: ${params.fromChain.type}`);
8505
- }
8506
- // Validate that the chain supports custom bridge contracts
8507
- if (!hasCustomContractSupport(params.fromChain, 'bridge')) {
8508
- throw new Error(`Chain ${params.fromChain.name} does not support custom bridge contracts. Use 'cctp.v2.depositForBurn' instead.`);
8509
- }
8510
- // Validate logical consistency: protocolFee and feeRecipient must be used together
8511
- const protocolFee = params.protocolFee ?? 0n;
8512
- if (protocolFee > 0n && params.feeRecipient === undefined) {
8513
- throw new Error('protocolFee requires feeRecipient. Specify where the fee should be sent.');
8514
- }
8515
- if (params.feeRecipient !== undefined && protocolFee === 0n) {
8516
- throw new Error('feeRecipient requires non-zero protocolFee. Specify the fee amount to send.');
8517
- }
8518
- // Validate address format for user-provided feeRecipient
8519
- if (params.feeRecipient !== undefined) {
8520
- assertEvmAddress(params.feeRecipient);
8521
- }
8522
- // Validate permit signature components if provided
8523
- if (params.permitParams) {
8524
- const currentTimestamp = BigInt(Math.floor(Date.now() / 1000));
8525
- if (params.permitParams.deadline <= currentTimestamp) {
8526
- throw new Error('Permit deadline has expired');
8527
- }
8528
- if (![27, 28].includes(params.permitParams.v)) {
8529
- throw new Error('Invalid permit signature recovery parameter (v)');
8530
- }
8531
- }
8532
- // Get the custom bridge contract address from chain definition
8533
- const bridgeAddress = params.fromChain.kitContracts?.bridge;
8534
- if (bridgeAddress === undefined) {
8535
- throw new Error(`No bridge contract address found for chain ${params.fromChain.name}`);
8536
- }
8537
- assertEvmAddress(bridgeAddress);
8538
- // Prepare the bridge parameters struct for the contract call
8539
- // Use optional parameters with sensible defaults:
8540
- // - feeRecipient: defaults to bridge address (safe for zero fees)
8541
- // - burnToken: auto-resolve from chain definition (same as depositForBurn)
8542
- const destinationCallerAddress = convertAddress(params.destinationCaller ?? ZERO_HASH, 'bytes32');
8543
- const feeRecipient = params.feeRecipient ?? bridgeAddress;
8544
- const bridgeParams = {
8545
- amount: params.amount,
8546
- maxFee: params.maxFee,
8547
- fee: protocolFee,
8548
- mintRecipient: convertAddress(params.mintRecipient, 'bytes32'),
8549
- destinationCaller: destinationCallerAddress,
8550
- burnToken: params.fromChain.usdcAddress,
8551
- feeRecipient,
8552
- destinationDomain: params.toChain.cctp.domain,
8553
- minFinalityThreshold: BigInt(params.minFinalityThreshold), // Convert to bigint for contract
8554
- };
8555
- // Prepare the custom bridge transaction - select method based on whether permit is provided
8556
- const args = [bridgeParams];
8557
- let functionName = 'bridgeWithPreapproval';
8558
- if (params.permitParams) {
8559
- // Use bridgeWithPermit when permit signature is provided
8560
- const permitParams = {
8561
- deadline: params.permitParams.deadline,
8562
- v: params.permitParams.v,
8563
- r: params.permitParams.r,
8564
- s: params.permitParams.s,
8565
- };
8566
- args.push(permitParams);
8567
- functionName = 'bridgeWithPermit';
8568
- }
8569
- return adapter.prepare({
8570
- type: 'evm',
8571
- abi: bridgeContractAbi,
8572
- address: bridgeAddress,
8573
- functionName,
8574
- args,
8575
- }, context);
9211
+ const customBurnWithHook = async (params, adapter, context) => {
9212
+ return prepareCustomBurn(params, adapter, context);
8576
9213
  };
8577
9214
 
8578
9215
  /**
@@ -8935,6 +9572,16 @@ const getHandlers = (adapter) => {
8935
9572
  'cctp.v2.depositForBurn': async (params, context) => {
8936
9573
  return depositForBurn(params, adapter, context);
8937
9574
  },
9575
+ /**
9576
+ * Handler for CCTP v2 deposit for burn with hook operations on EVM chains.
9577
+ *
9578
+ * Burns USDC tokens with hook data that signals to Circle's Orbit relayer
9579
+ * that forwarding is requested. The relayer will automatically fetch the
9580
+ * attestation and submit the destination mint transaction.
9581
+ */
9582
+ 'cctp.v2.depositForBurnWithHook': async (params, context) => {
9583
+ return depositForBurnWithHook(params, adapter, context);
9584
+ },
8938
9585
  /**
8939
9586
  * Handler for CCTP v2 receive message operations on EVM chains.
8940
9587
  *
@@ -8955,6 +9602,17 @@ const getHandlers = (adapter) => {
8955
9602
  'cctp.v2.customBurn': async (params, context) => {
8956
9603
  return customBurn(params, adapter, context);
8957
9604
  },
9605
+ /**
9606
+ * Handler for CCTP v2 custom burn with hook operations on EVM chains.
9607
+ *
9608
+ * Burns USDC tokens using a custom bridge contract with hook data that
9609
+ * signals to Circle's Orbit relayer that forwarding is requested.
9610
+ * Combines the custom bridge functionality (preapproval/permit) with
9611
+ * automated attestation and destination mint execution.
9612
+ */
9613
+ 'cctp.v2.customBurnWithHook': async (params, context) => {
9614
+ return customBurnWithHook(params, adapter, context);
9615
+ },
8958
9616
  /**
8959
9617
  * Handler for native token balance operations on EVM chains.
8960
9618
  *
@@ -11021,16 +11679,31 @@ class EthersAdapter extends EvmAdapter {
11021
11679
  * Simulates a contract function call using Ethers v6 `.staticCall`.
11022
11680
  */
11023
11681
  async simulateFunctionCall(contract, functionName, args, chain) {
11024
- try {
11025
- const func = contract.getFunction(functionName);
11026
- await func.staticCall(...args);
11027
- }
11028
- catch (err) {
11029
- // Wrap simulation errors with structured error format
11030
- throw parseBlockchainError(err, {
11031
- chain: chain.name,
11032
- operation: 'simulation',
11033
- });
11682
+ // Retry on allowance errors to handle RPC state propagation delays
11683
+ // (e.g., approve tx confirmed but not yet visible in latest block for staticCall)
11684
+ const MAX_SIMULATION_ATTEMPTS = 3;
11685
+ const SIMULATION_RETRY_DELAY_MS = 1000;
11686
+ for (let attempt = 1; attempt <= MAX_SIMULATION_ATTEMPTS; attempt++) {
11687
+ try {
11688
+ const func = contract.getFunction(functionName);
11689
+ await func.staticCall(...args);
11690
+ return;
11691
+ }
11692
+ catch (err) {
11693
+ const errorMessage = err instanceof Error ? err.message : String(err);
11694
+ const lowerCaseError = errorMessage.toLowerCase();
11695
+ const isAllowanceError = lowerCaseError.includes('exceeds allowance') ||
11696
+ lowerCaseError.includes('insufficient allowance');
11697
+ if (isAllowanceError && attempt < MAX_SIMULATION_ATTEMPTS) {
11698
+ await new Promise((resolve) => setTimeout(resolve, SIMULATION_RETRY_DELAY_MS * attempt));
11699
+ continue;
11700
+ }
11701
+ // Wrap simulation errors with structured error format
11702
+ throw parseBlockchainError(err, {
11703
+ chain: chain.name,
11704
+ operation: 'simulation',
11705
+ });
11706
+ }
11034
11707
  }
11035
11708
  }
11036
11709
  /**
@@ -11938,7 +12611,7 @@ const createAdapterFromPrivateKey = createEthersAdapterFromPrivateKey;
11938
12611
  * const adapter = await createEthersAdapterFromProvider({
11939
12612
  * provider: window.ethereum,
11940
12613
  * getProvider: ({ chain }) => new JsonRpcProvider(
11941
- * `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_KEY}`,
12614
+ * `https://eth-mainnet.g.alchemy.com/v2/${process.env.ALCHEMY_KEY}`,
11942
12615
  * { name: chain.name, chainId: chain.chainId }
11943
12616
  * )
11944
12617
  * })
@@ -12108,7 +12781,7 @@ const createEthersAdapterFromProvider = async (params) => {
12108
12781
  * const adapter = await createAdapterFromProvider({
12109
12782
  * provider: window.ethereum,
12110
12783
  * getProvider: ({ chain }) => new JsonRpcProvider(
12111
- * `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_KEY}`,
12784
+ * `https://eth-mainnet.g.alchemy.com/v2/${process.env.ALCHEMY_KEY}`,
12112
12785
  * { name: chain.name, chainId: chain.chainId }
12113
12786
  * )
12114
12787
  * })