@circle-fin/provider-cctp-v2 1.4.1 → 1.6.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
@@ -73,6 +73,8 @@ var Blockchain;
73
73
  Blockchain["Celo_Alfajores_Testnet"] = "Celo_Alfajores_Testnet";
74
74
  Blockchain["Codex"] = "Codex";
75
75
  Blockchain["Codex_Testnet"] = "Codex_Testnet";
76
+ Blockchain["Edge"] = "Edge";
77
+ Blockchain["Edge_Testnet"] = "Edge_Testnet";
76
78
  Blockchain["Ethereum"] = "Ethereum";
77
79
  Blockchain["Ethereum_Sepolia"] = "Ethereum_Sepolia";
78
80
  Blockchain["Hedera"] = "Hedera";
@@ -85,6 +87,8 @@ var Blockchain;
85
87
  Blockchain["Linea_Sepolia"] = "Linea_Sepolia";
86
88
  Blockchain["Monad"] = "Monad";
87
89
  Blockchain["Monad_Testnet"] = "Monad_Testnet";
90
+ Blockchain["Morph"] = "Morph";
91
+ Blockchain["Morph_Testnet"] = "Morph_Testnet";
88
92
  Blockchain["NEAR"] = "NEAR";
89
93
  Blockchain["NEAR_Testnet"] = "NEAR_Testnet";
90
94
  Blockchain["Noble"] = "Noble";
@@ -288,11 +292,13 @@ var BridgeChain;
288
292
  BridgeChain["Avalanche"] = "Avalanche";
289
293
  BridgeChain["Base"] = "Base";
290
294
  BridgeChain["Codex"] = "Codex";
295
+ BridgeChain["Edge"] = "Edge";
291
296
  BridgeChain["Ethereum"] = "Ethereum";
292
297
  BridgeChain["HyperEVM"] = "HyperEVM";
293
298
  BridgeChain["Ink"] = "Ink";
294
299
  BridgeChain["Linea"] = "Linea";
295
300
  BridgeChain["Monad"] = "Monad";
301
+ BridgeChain["Morph"] = "Morph";
296
302
  BridgeChain["Optimism"] = "Optimism";
297
303
  BridgeChain["Plume"] = "Plume";
298
304
  BridgeChain["Polygon"] = "Polygon";
@@ -308,11 +314,13 @@ var BridgeChain;
308
314
  BridgeChain["Avalanche_Fuji"] = "Avalanche_Fuji";
309
315
  BridgeChain["Base_Sepolia"] = "Base_Sepolia";
310
316
  BridgeChain["Codex_Testnet"] = "Codex_Testnet";
317
+ BridgeChain["Edge_Testnet"] = "Edge_Testnet";
311
318
  BridgeChain["Ethereum_Sepolia"] = "Ethereum_Sepolia";
312
319
  BridgeChain["HyperEVM_Testnet"] = "HyperEVM_Testnet";
313
320
  BridgeChain["Ink_Testnet"] = "Ink_Testnet";
314
321
  BridgeChain["Linea_Sepolia"] = "Linea_Sepolia";
315
322
  BridgeChain["Monad_Testnet"] = "Monad_Testnet";
323
+ BridgeChain["Morph_Testnet"] = "Morph_Testnet";
316
324
  BridgeChain["Optimism_Sepolia"] = "Optimism_Sepolia";
317
325
  BridgeChain["Plume_Testnet"] = "Plume_Testnet";
318
326
  BridgeChain["Polygon_Amoy_Testnet"] = "Polygon_Amoy_Testnet";
@@ -1058,7 +1066,7 @@ const Codex = defineChain({
1058
1066
  },
1059
1067
  forwarderSupported: {
1060
1068
  source: true,
1061
- destination: false,
1069
+ destination: true,
1062
1070
  },
1063
1071
  },
1064
1072
  kitContracts: {
@@ -1101,7 +1109,95 @@ const CodexTestnet = defineChain({
1101
1109
  },
1102
1110
  forwarderSupported: {
1103
1111
  source: true,
1104
- destination: false,
1112
+ destination: true,
1113
+ },
1114
+ },
1115
+ kitContracts: {
1116
+ bridge: BRIDGE_CONTRACT_EVM_TESTNET,
1117
+ },
1118
+ });
1119
+
1120
+ /**
1121
+ * Edge Mainnet chain definition
1122
+ * @remarks
1123
+ * This represents the official production network for the Edge blockchain.
1124
+ * Edge is an EVM-compatible blockchain.
1125
+ */
1126
+ const Edge = defineChain({
1127
+ type: 'evm',
1128
+ chain: Blockchain.Edge,
1129
+ name: 'Edge',
1130
+ title: 'Edge Mainnet',
1131
+ nativeCurrency: {
1132
+ name: 'Ether',
1133
+ symbol: 'ETH',
1134
+ decimals: 18,
1135
+ },
1136
+ chainId: 3343,
1137
+ isTestnet: false,
1138
+ explorerUrl: 'https://pro.edgex.exchange/en-US/explorer/tx/{hash}',
1139
+ rpcEndpoints: ['https://edge-mainnet.g.alchemy.com/public'],
1140
+ eurcAddress: null,
1141
+ usdcAddress: '0x98d2919b9A214E6Fa5384AC81E6864bA686Ad74c',
1142
+ usdtAddress: null,
1143
+ cctp: {
1144
+ domain: 28,
1145
+ contracts: {
1146
+ v2: {
1147
+ type: 'split',
1148
+ tokenMessenger: '0x98706A006bc632Df31CAdFCBD43F38887ce2ca5c',
1149
+ messageTransmitter: '0x5b61381Fc9e58E70EfC13a4A97516997019198ee',
1150
+ confirmations: 65,
1151
+ fastConfirmations: 1,
1152
+ },
1153
+ },
1154
+ forwarderSupported: {
1155
+ source: true,
1156
+ destination: true,
1157
+ },
1158
+ },
1159
+ kitContracts: {
1160
+ bridge: '0x6D1AaE1c34Aeb582022916a67f2A655C6f4eDFF2', //Unique bridge address as CCTP address for Edge is also unique
1161
+ },
1162
+ });
1163
+
1164
+ /**
1165
+ * Edge Testnet chain definition
1166
+ * @remarks
1167
+ * This represents the official test network for the Edge blockchain.
1168
+ * Edge is an EVM-compatible blockchain.
1169
+ */
1170
+ const EdgeTestnet = defineChain({
1171
+ type: 'evm',
1172
+ chain: Blockchain.Edge_Testnet,
1173
+ name: 'Edge Testnet',
1174
+ title: 'Edge Testnet',
1175
+ nativeCurrency: {
1176
+ name: 'Ether',
1177
+ symbol: 'ETH',
1178
+ decimals: 18,
1179
+ },
1180
+ chainId: 33431,
1181
+ isTestnet: true,
1182
+ explorerUrl: 'https://edge-testnet.explorer.alchemy.com/tx/{hash}',
1183
+ rpcEndpoints: ['https://edge-testnet.g.alchemy.com/public'],
1184
+ eurcAddress: null,
1185
+ usdcAddress: '0x2d9F7CAD728051AA35Ecdc472a14cf8cDF5CFD6B',
1186
+ usdtAddress: null,
1187
+ cctp: {
1188
+ domain: 28,
1189
+ contracts: {
1190
+ v2: {
1191
+ type: 'split',
1192
+ tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
1193
+ messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
1194
+ confirmations: 65,
1195
+ fastConfirmations: 1,
1196
+ },
1197
+ },
1198
+ forwarderSupported: {
1199
+ source: true,
1200
+ destination: true,
1105
1201
  },
1106
1202
  },
1107
1203
  kitContracts: {
@@ -1275,7 +1371,7 @@ const HyperEVM = defineChain({
1275
1371
  },
1276
1372
  chainId: 999,
1277
1373
  isTestnet: false,
1278
- explorerUrl: 'https://hyperevmscan.io/tx/{hash}',
1374
+ explorerUrl: 'https://app.hyperliquid.xyz/explorer/tx/{hash}',
1279
1375
  rpcEndpoints: ['https://rpc.hyperliquid.xyz/evm'],
1280
1376
  eurcAddress: null,
1281
1377
  usdcAddress: '0xb88339CB7199b77E23DB6E890353E22632Ba630f',
@@ -1320,7 +1416,7 @@ const HyperEVMTestnet = defineChain({
1320
1416
  },
1321
1417
  chainId: 998,
1322
1418
  isTestnet: true,
1323
- explorerUrl: 'https://testnet.hyperliquid.xyz/explorer/tx/{hash}',
1419
+ explorerUrl: 'https://app.hyperliquid-testnet.xyz/explorer/tx/{hash}',
1324
1420
  rpcEndpoints: ['https://rpc.hyperliquid-testnet.xyz/evm'],
1325
1421
  eurcAddress: null,
1326
1422
  usdcAddress: '0x2B3370eE501B4a559b57D449569354196457D8Ab',
@@ -1620,6 +1716,94 @@ const MonadTestnet = defineChain({
1620
1716
  },
1621
1717
  });
1622
1718
 
1719
+ /**
1720
+ * Morph Mainnet chain definition
1721
+ * @remarks
1722
+ * This represents the official production network for the Morph blockchain.
1723
+ * Morph is an EVM-compatible Layer-2 blockchain built on the OP Stack.
1724
+ */
1725
+ const Morph = defineChain({
1726
+ type: 'evm',
1727
+ chain: Blockchain.Morph,
1728
+ name: 'Morph',
1729
+ title: 'Morph Mainnet',
1730
+ nativeCurrency: {
1731
+ name: 'Ether',
1732
+ symbol: 'ETH',
1733
+ decimals: 18,
1734
+ },
1735
+ chainId: 2818,
1736
+ isTestnet: false,
1737
+ explorerUrl: 'https://explorer.morph.network/tx/{hash}',
1738
+ rpcEndpoints: ['https://rpc.morphl2.io'],
1739
+ eurcAddress: null,
1740
+ usdcAddress: '0xCfb1186F4e93D60E60a8bDd997427D1F33bc372B',
1741
+ usdtAddress: null,
1742
+ cctp: {
1743
+ domain: 30,
1744
+ contracts: {
1745
+ v2: {
1746
+ type: 'split',
1747
+ tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
1748
+ messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
1749
+ confirmations: 64,
1750
+ fastConfirmations: 1,
1751
+ },
1752
+ },
1753
+ forwarderSupported: {
1754
+ source: false,
1755
+ destination: false,
1756
+ },
1757
+ },
1758
+ kitContracts: {
1759
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
1760
+ },
1761
+ });
1762
+
1763
+ /**
1764
+ * Morph Hoodi Testnet chain definition
1765
+ * @remarks
1766
+ * This represents the official test network for the Morph blockchain.
1767
+ * Morph is an EVM-compatible Layer-2 blockchain built on the OP Stack.
1768
+ */
1769
+ const MorphTestnet = defineChain({
1770
+ type: 'evm',
1771
+ chain: Blockchain.Morph_Testnet,
1772
+ name: 'Morph Hoodi',
1773
+ title: 'Morph Hoodi Testnet',
1774
+ nativeCurrency: {
1775
+ name: 'Ether',
1776
+ symbol: 'ETH',
1777
+ decimals: 18,
1778
+ },
1779
+ chainId: 2910,
1780
+ isTestnet: true,
1781
+ explorerUrl: 'https://explorer-hoodi.morphl2.io/tx/{hash}',
1782
+ rpcEndpoints: ['https://rpc-hoodi.morphl2.io'],
1783
+ eurcAddress: null,
1784
+ usdcAddress: '0x7433b41C6c5e1d58D4Da99483609520255ab661B',
1785
+ usdtAddress: null,
1786
+ cctp: {
1787
+ domain: 30,
1788
+ contracts: {
1789
+ v2: {
1790
+ type: 'split',
1791
+ tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
1792
+ messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
1793
+ confirmations: 64,
1794
+ fastConfirmations: 1,
1795
+ },
1796
+ },
1797
+ forwarderSupported: {
1798
+ source: false,
1799
+ destination: false,
1800
+ },
1801
+ },
1802
+ kitContracts: {
1803
+ bridge: BRIDGE_CONTRACT_EVM_TESTNET,
1804
+ },
1805
+ });
1806
+
1623
1807
  /**
1624
1808
  * NEAR Protocol Mainnet chain definition
1625
1809
  * @remarks
@@ -1878,7 +2062,7 @@ const Plume = defineChain({
1878
2062
  },
1879
2063
  forwarderSupported: {
1880
2064
  source: true,
1881
- destination: false,
2065
+ destination: true,
1882
2066
  },
1883
2067
  },
1884
2068
  kitContracts: {
@@ -1923,7 +2107,7 @@ const PlumeTestnet = defineChain({
1923
2107
  },
1924
2108
  forwarderSupported: {
1925
2109
  source: true,
1926
- destination: false,
2110
+ destination: true,
1927
2111
  },
1928
2112
  },
1929
2113
  kitContracts: {
@@ -2701,7 +2885,7 @@ const XDC = defineChain({
2701
2885
  },
2702
2886
  forwarderSupported: {
2703
2887
  source: true,
2704
- destination: false,
2888
+ destination: true,
2705
2889
  },
2706
2890
  },
2707
2891
  kitContracts: {
@@ -2745,7 +2929,7 @@ const XDCApothem = defineChain({
2745
2929
  },
2746
2930
  forwarderSupported: {
2747
2931
  source: true,
2748
- destination: false,
2932
+ destination: true,
2749
2933
  },
2750
2934
  },
2751
2935
  kitContracts: {
@@ -2820,6 +3004,8 @@ var Chains = {
2820
3004
  CeloAlfajoresTestnet: CeloAlfajoresTestnet,
2821
3005
  Codex: Codex,
2822
3006
  CodexTestnet: CodexTestnet,
3007
+ Edge: Edge,
3008
+ EdgeTestnet: EdgeTestnet,
2823
3009
  Ethereum: Ethereum,
2824
3010
  EthereumSepolia: EthereumSepolia,
2825
3011
  Hedera: Hedera,
@@ -2832,6 +3018,8 @@ var Chains = {
2832
3018
  LineaSepolia: LineaSepolia,
2833
3019
  Monad: Monad,
2834
3020
  MonadTestnet: MonadTestnet,
3021
+ Morph: Morph,
3022
+ MorphTestnet: MorphTestnet,
2835
3023
  NEAR: NEAR,
2836
3024
  NEARTestnet: NEARTestnet,
2837
3025
  Noble: Noble,
@@ -5423,12 +5611,14 @@ const USDC = {
5423
5611
  [Blockchain.Base]: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
5424
5612
  [Blockchain.Celo]: '0xcebA9300f2b948710d2653dD7B07f33A8B32118C',
5425
5613
  [Blockchain.Codex]: '0xd996633a415985DBd7D6D12f4A4343E31f5037cf',
5614
+ [Blockchain.Edge]: '0x98d2919b9A214E6Fa5384AC81E6864bA686Ad74c',
5426
5615
  [Blockchain.Ethereum]: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
5427
5616
  [Blockchain.Hedera]: '0.0.456858',
5428
5617
  [Blockchain.HyperEVM]: '0xb88339CB7199b77E23DB6E890353E22632Ba630f',
5429
5618
  [Blockchain.Ink]: '0x2D270e6886d130D724215A266106e6832161EAEd',
5430
5619
  [Blockchain.Linea]: '0x176211869ca2b568f2a7d4ee941e073a821ee1ff',
5431
5620
  [Blockchain.Monad]: '0x754704Bc059F8C67012fEd69BC8A327a5aafb603',
5621
+ [Blockchain.Morph]: '0xCfb1186F4e93D60E60a8bDd997427D1F33bc372B',
5432
5622
  [Blockchain.NEAR]: '17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1',
5433
5623
  [Blockchain.Noble]: 'uusdc',
5434
5624
  [Blockchain.Optimism]: '0x0b2c639c533813f4aa9d7837caf62653d097ff85',
@@ -5451,12 +5641,14 @@ const USDC = {
5451
5641
  [Blockchain.Avalanche_Fuji]: '0x5425890298aed601595a70AB815c96711a31Bc65',
5452
5642
  [Blockchain.Base_Sepolia]: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',
5453
5643
  [Blockchain.Codex_Testnet]: '0x6d7f141b6819C2c9CC2f818e6ad549E7Ca090F8f',
5644
+ [Blockchain.Edge_Testnet]: '0x2d9F7CAD728051AA35Ecdc472a14cf8cDF5CFD6B',
5454
5645
  [Blockchain.Ethereum_Sepolia]: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238',
5455
5646
  [Blockchain.Hedera_Testnet]: '0.0.429274',
5456
5647
  [Blockchain.HyperEVM_Testnet]: '0x2B3370eE501B4a559b57D449569354196457D8Ab',
5457
5648
  [Blockchain.Ink_Testnet]: '0xFabab97dCE620294D2B0b0e46C68964e326300Ac',
5458
5649
  [Blockchain.Linea_Sepolia]: '0xfece4462d57bd51a6a552365a011b95f0e16d9b7',
5459
5650
  [Blockchain.Monad_Testnet]: '0x534b2f3A21130d7a60830c2Df862319e593943A3',
5651
+ [Blockchain.Morph_Testnet]: '0x7433b41C6c5e1d58D4Da99483609520255ab661B',
5460
5652
  [Blockchain.NEAR_Testnet]: '3e2210e1184b45b64c8a434c0a7e7b23cc04ea7eb7a6c3c32520d03d4afcb8af',
5461
5653
  [Blockchain.Noble_Testnet]: 'uusdc',
5462
5654
  [Blockchain.Optimism_Sepolia]: '0x5fd84259d66Cd46123540766Be93DFE6D43130D7',
@@ -9317,7 +9509,184 @@ zod.z
9317
9509
  })
9318
9510
  .passthrough();
9319
9511
 
9320
- var version = "1.4.1";
9512
+ /**
9513
+ * Check whether the source adapter supports EIP-5792 atomic batching and
9514
+ * the consumer has not explicitly opted out via `config.batchTransactions`.
9515
+ *
9516
+ * @param params - Bridge parameters (used for adapter and config access).
9517
+ * @returns `true` when batched execution should be attempted.
9518
+ *
9519
+ * @example
9520
+ * ```typescript
9521
+ * const useBatched = await shouldUseBatchedExecution(params)
9522
+ * if (useBatched) {
9523
+ * // take the batched approve + burn path
9524
+ * }
9525
+ * ```
9526
+ */
9527
+ async function shouldUseBatchedExecution(params) {
9528
+ if (params.config?.batchTransactions === false) {
9529
+ return false;
9530
+ }
9531
+ const { chain } = params.source;
9532
+ if (chain.type !== 'evm') {
9533
+ return false;
9534
+ }
9535
+ const adapter = params.source
9536
+ .adapter;
9537
+ if (typeof adapter.supportsAtomicBatch !== 'function' ||
9538
+ typeof adapter.batchExecute !== 'function') {
9539
+ return false;
9540
+ }
9541
+ try {
9542
+ return await adapter.supportsAtomicBatch(chain);
9543
+ }
9544
+ catch {
9545
+ return false;
9546
+ }
9547
+ }
9548
+ /**
9549
+ * Execute the approve and burn steps as a single EIP-5792 batched call.
9550
+ *
9551
+ * Prepare both `PreparedChainRequest` objects upfront, extract their raw
9552
+ * call data via `getCallData()`, submit both via `adapter.batchExecute()`,
9553
+ * then map the individual receipts back to standard {@link BridgeStep}
9554
+ * objects so downstream consumers (event callbacks, result tracking) are
9555
+ * unaffected.
9556
+ *
9557
+ * @param params - The CCTP v2 bridge parameters.
9558
+ * @param provider - The CCTP v2 bridging provider.
9559
+ * @returns Approve and burn steps with a shared context containing the burn tx hash.
9560
+ * @throws {@link KitError} when the source chain is not EVM.
9561
+ * @throws {@link KitError} when calldata extraction (`getCallData`) is not supported
9562
+ * by the prepared requests.
9563
+ * @remarks
9564
+ * Errors that occur after the batch has been submitted to the wallet
9565
+ * (e.g. polling timeout, insufficient receipts) are **not thrown** — they
9566
+ * are captured as `state: 'error'` on the returned steps to prevent
9567
+ * accidental double-spend on retry.
9568
+ *
9569
+ * @example
9570
+ * ```typescript
9571
+ * const { approveStep, burnStep, context } = await executeBatchedApproveAndBurn(
9572
+ * params,
9573
+ * provider,
9574
+ * )
9575
+ * result.steps.push(approveStep, burnStep)
9576
+ * ```
9577
+ */
9578
+ async function executeBatchedApproveAndBurn(params, provider) {
9579
+ // Double-unknown cast: Adapter<TFrom> has no structural overlap with
9580
+ // BatchCapableAdapter (a duck-typed interface for EIP-5792 methods).
9581
+ // A direct cast fails because TS cannot prove the intersection; the
9582
+ // runtime capability check below guards against unsupported adapters.
9583
+ const adapter = params.source.adapter;
9584
+ const sourceChain = params.source.chain;
9585
+ if (sourceChain.type !== 'evm') {
9586
+ throw new KitError({
9587
+ ...InputError.INVALID_CHAIN,
9588
+ recoverability: 'FATAL',
9589
+ message: 'Batched execution is only supported on EVM chains.',
9590
+ });
9591
+ }
9592
+ const chain = sourceChain;
9593
+ // customFee.value is in base units (integer string) at this point.
9594
+ const customFee = BigInt(params.config?.customFee?.value ?? '0');
9595
+ const amountBigInt = BigInt(params.amount);
9596
+ const approvalAmount = (amountBigInt + customFee).toString();
9597
+ const [approveRequest, burnRequest] = await Promise.all([
9598
+ provider.approve(params.source, approvalAmount),
9599
+ provider.burn(params),
9600
+ ]);
9601
+ if (approveRequest.type !== 'evm' ||
9602
+ burnRequest.type !== 'evm' ||
9603
+ !approveRequest.getCallData ||
9604
+ !burnRequest.getCallData) {
9605
+ throw new KitError({
9606
+ ...InputError.UNSUPPORTED_ACTION,
9607
+ recoverability: 'FATAL',
9608
+ message: 'Batched execution requires EVM prepared requests with getCallData() support.',
9609
+ });
9610
+ }
9611
+ const approveCallData = approveRequest.getCallData();
9612
+ const burnCallData = burnRequest.getCallData();
9613
+ // batchExecute may throw before submission (wallet declined) but never
9614
+ // after — post-submission errors are returned as empty receipts.
9615
+ const batchResult = await adapter.batchExecute([approveCallData, burnCallData], chain);
9616
+ const approveReceipt = batchResult.receipts[0];
9617
+ const burnReceipt = batchResult.receipts[1];
9618
+ const approveStep = await buildBatchedStep('approve', approveReceipt, batchResult.batchId, adapter, chain);
9619
+ const burnStep = await buildBatchedStep('burn', burnReceipt, batchResult.batchId, adapter, chain);
9620
+ if (burnStep.state !== 'error' && !burnStep.txHash) {
9621
+ burnStep.state = 'error';
9622
+ burnStep.errorMessage =
9623
+ 'Batched burn step completed but no transaction hash was returned.';
9624
+ }
9625
+ const context = { burnTxHash: burnStep.txHash ?? '' };
9626
+ return { approveStep, burnStep, context };
9627
+ }
9628
+ /**
9629
+ * Build a {@link BridgeStep} from a single receipt within a batch.
9630
+ *
9631
+ * Map the raw receipt from `batchExecute` into a standard `BridgeStep`,
9632
+ * waiting for on-chain confirmation via `adapter.waitForTransaction`.
9633
+ * All errors are captured on the step (never thrown) so the caller
9634
+ * can inspect each step independently.
9635
+ *
9636
+ * @param name - Human-readable step name (e.g. `'approve'`, `'burn'`).
9637
+ * @param receipt - Per-call receipt from `batchExecute`, or `undefined`
9638
+ * when the wallet returned fewer receipts than submitted calls.
9639
+ * @param batchId - Wallet-assigned batch identifier.
9640
+ * @param adapter - The batch-capable adapter (used for confirmation).
9641
+ * @param chain - The EVM chain the batch was executed on.
9642
+ * @returns A fully-populated bridge step with state, tx hash and explorer URL.
9643
+ *
9644
+ * @internal
9645
+ */
9646
+ async function buildBatchedStep(name, receipt, batchId, adapter, chain) {
9647
+ const step = {
9648
+ name,
9649
+ state: 'pending',
9650
+ batched: true,
9651
+ batchId,
9652
+ };
9653
+ if (!receipt) {
9654
+ step.state = 'error';
9655
+ step.errorMessage = `No receipt returned for ${name} in batch ${batchId}.`;
9656
+ return step;
9657
+ }
9658
+ step.txHash = receipt.txHash;
9659
+ if (receipt.txHash) {
9660
+ step.explorerUrl = buildExplorerUrl(chain, receipt.txHash);
9661
+ }
9662
+ if (receipt.status !== 'success') {
9663
+ step.state = 'error';
9664
+ step.errorMessage = `${name} call failed within batch ${batchId}.`;
9665
+ return step;
9666
+ }
9667
+ if (!receipt.txHash) {
9668
+ step.state = 'error';
9669
+ step.errorMessage = `${name} succeeded in batch but returned an empty transaction hash.`;
9670
+ return step;
9671
+ }
9672
+ try {
9673
+ const transaction = await adapter.waitForTransaction(receipt.txHash, { confirmations: 1 }, chain);
9674
+ step.state = transaction.blockNumber === undefined ? 'error' : 'success';
9675
+ step.data = transaction;
9676
+ if (transaction.blockNumber === undefined) {
9677
+ step.errorMessage = 'Transaction was not confirmed on-chain.';
9678
+ }
9679
+ }
9680
+ catch (err) {
9681
+ step.state = 'error';
9682
+ step.error = err;
9683
+ step.errorMessage =
9684
+ err instanceof Error ? err.message : 'Unknown error during confirmation.';
9685
+ }
9686
+ return step;
9687
+ }
9688
+
9689
+ var version = "1.6.0";
9321
9690
  var pkg = {
9322
9691
  version: version};
9323
9692
 
@@ -9346,6 +9715,67 @@ function resolveBridgeInvocation(invocationMeta) {
9346
9715
  };
9347
9716
  return extendInvocationContext(resolveInvocationContext(invocationMeta, defaults), BRIDGE_CALLER);
9348
9717
  }
9718
+ /**
9719
+ * Execute the batched approve + burn path via EIP-5792.
9720
+ *
9721
+ * Mutate `result` with step data and error state as needed. Return the
9722
+ * batch context on success, or `undefined` when the batch failed (in
9723
+ * which case `result.state` is set to `'error'`).
9724
+ *
9725
+ * @internal
9726
+ * @param params - Bridge parameters (read-only).
9727
+ * @param provider - The CCTP v2 bridging provider (read-only).
9728
+ * @param result - Bridge result object — **mutated in place** with step
9729
+ * data and, on failure, `state: 'error'` plus an `error` payload.
9730
+ * @param invocation - Invocation context for telemetry (read-only).
9731
+ * @returns The step context on success, or `undefined` when the batch failed.
9732
+ */
9733
+ async function executeBatchedPath(params, provider, result, invocation) {
9734
+ // IMPORTANT: once executeBatchedApproveAndBurn is called, we NEVER
9735
+ // fall back to sequential. The wallet may have already signed &
9736
+ // submitted the batch; retrying as individual txs would double-spend.
9737
+ try {
9738
+ const { approveStep, burnStep, context: batchContext, } = await executeBatchedApproveAndBurn(params, provider);
9739
+ for (const step of [approveStep, burnStep]) {
9740
+ const stepName = step.name;
9741
+ if (step.state === 'error') {
9742
+ ensureStepErrorMessage(step.name, step);
9743
+ result.steps.push(step);
9744
+ result.state = 'error';
9745
+ dispatchStepEvent(stepName, step, provider, invocation);
9746
+ return undefined;
9747
+ }
9748
+ dispatchStepEvent(stepName, step, provider, invocation);
9749
+ result.steps.push(step);
9750
+ }
9751
+ return batchContext;
9752
+ }
9753
+ catch (error_) {
9754
+ // Only handles pre-submission failures (prepare rejected, wallet
9755
+ // declined, etc.). batchExecute never throws after sendCalls succeeds.
9756
+ result.state = 'error';
9757
+ result.steps.push({
9758
+ name: 'batch',
9759
+ state: 'error',
9760
+ batched: true,
9761
+ error: error_,
9762
+ errorMessage: error_ instanceof Error
9763
+ ? error_.message
9764
+ : 'Batched approve + burn failed.',
9765
+ });
9766
+ return undefined;
9767
+ }
9768
+ }
9769
+ /**
9770
+ * Ensure `step.errorMessage` is populated when an error object exists.
9771
+ *
9772
+ * @internal
9773
+ */
9774
+ function ensureStepErrorMessage(name, step) {
9775
+ if (!step.errorMessage && step.error) {
9776
+ step.errorMessage = `${name} step failed: ${getErrorMessage(step.error)}`;
9777
+ }
9778
+ }
9349
9779
  /**
9350
9780
  * Execute a cross-chain USDC bridge using the CCTP v2 protocol.
9351
9781
  *
@@ -9379,9 +9809,7 @@ function resolveBridgeInvocation(invocationMeta) {
9379
9809
  * ```
9380
9810
  */
9381
9811
  async function bridge(params, provider) {
9382
- // Check if forwarder is enabled (on destination)
9383
9812
  const useForwarder = params.destination.useForwarder === true;
9384
- // Resolve invocation metadata to full context for event dispatching
9385
9813
  const invocation = resolveBridgeInvocation(params.invocationMeta);
9386
9814
  const result = {
9387
9815
  state: 'pending',
@@ -9394,11 +9822,9 @@ async function bridge(params, provider) {
9394
9822
  destination: {
9395
9823
  address: params.destination.address,
9396
9824
  chain: params.destination.chain,
9397
- // Preserve recipientAddress
9398
9825
  ...(params.destination.recipientAddress && {
9399
9826
  recipientAddress: params.destination.recipientAddress,
9400
9827
  }),
9401
- // Preserve useForwarder
9402
9828
  ...(useForwarder && {
9403
9829
  useForwarder: true,
9404
9830
  }),
@@ -9407,29 +9833,38 @@ async function bridge(params, provider) {
9407
9833
  config: params.config,
9408
9834
  provider: provider.name,
9409
9835
  };
9410
- // Context shared between steps
9411
9836
  let context = undefined;
9412
- // Create step executors based on useForwarder config
9837
+ let useBatched = false;
9838
+ try {
9839
+ useBatched = await shouldUseBatchedExecution(params);
9840
+ }
9841
+ catch {
9842
+ // Silently fall back to sequential
9843
+ }
9413
9844
  const executors = createStepExecutors(useForwarder);
9414
- // Execute each step in sequence
9415
- for (const { name, executor, updateContext } of executors) {
9845
+ if (useBatched) {
9846
+ const batchContext = await executeBatchedPath(params, provider, result, invocation);
9847
+ if (result.state === 'error') {
9848
+ return result;
9849
+ }
9850
+ context = batchContext;
9851
+ }
9852
+ const stepsToRun = useBatched
9853
+ ? executors.filter(({ name }) => name !== 'approve' && name !== 'burn')
9854
+ : executors;
9855
+ for (const { name, executor, updateContext } of stepsToRun) {
9416
9856
  try {
9417
9857
  const step = await executor(params, provider, context);
9418
9858
  if (step.state === 'error') {
9419
- // Ensure errorMessage is set with proper formatting if not already present
9420
- if (!step.errorMessage && step.error) {
9421
- step.errorMessage = `${name} step failed: ${getErrorMessage(step.error)}`;
9422
- }
9859
+ ensureStepErrorMessage(name, step);
9423
9860
  result.steps.push(step);
9424
9861
  result.state = 'error';
9425
- // Dispatch event even for error steps
9426
9862
  dispatchStepEvent(name, step, provider, invocation);
9427
9863
  return result;
9428
9864
  }
9429
- // Merge new context with existing context to preserve data from previous steps
9430
9865
  const newContext = updateContext?.(step);
9431
9866
  if (newContext) {
9432
- context = { ...(context ?? {}), ...newContext };
9867
+ context = { ...context, ...newContext };
9433
9868
  }
9434
9869
  dispatchStepEvent(name, step, provider, invocation);
9435
9870
  result.steps.push(step);