@circle-fin/app-kit 1.8.1 → 1.10.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 (48) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/README.md +3 -3
  3. package/bridge.cjs +1102 -260
  4. package/bridge.d.cts +161 -10
  5. package/bridge.d.mts +161 -10
  6. package/bridge.d.ts +161 -10
  7. package/bridge.mjs +1102 -260
  8. package/chains.cjs +102 -2
  9. package/chains.d.cts +3 -0
  10. package/chains.d.mts +3 -0
  11. package/chains.d.ts +3 -0
  12. package/chains.mjs +102 -2
  13. package/context.cjs +1 -0
  14. package/context.d.cts +166 -12
  15. package/context.d.mts +166 -12
  16. package/context.d.ts +166 -12
  17. package/context.mjs +1 -0
  18. package/earn.cjs +1074 -454
  19. package/earn.d.cts +546 -99
  20. package/earn.d.mts +546 -99
  21. package/earn.d.ts +546 -99
  22. package/earn.mjs +1074 -455
  23. package/estimateBridge.cjs +1102 -260
  24. package/estimateBridge.d.cts +161 -10
  25. package/estimateBridge.d.mts +161 -10
  26. package/estimateBridge.d.ts +161 -10
  27. package/estimateBridge.mjs +1102 -260
  28. package/estimateSwap.cjs +915 -96
  29. package/estimateSwap.d.cts +161 -10
  30. package/estimateSwap.d.mts +161 -10
  31. package/estimateSwap.d.ts +161 -10
  32. package/estimateSwap.mjs +915 -96
  33. package/index.cjs +3029 -862
  34. package/index.d.cts +1277 -143
  35. package/index.d.mts +1277 -143
  36. package/index.d.ts +1277 -143
  37. package/index.mjs +3029 -862
  38. package/package.json +12 -6
  39. package/swap.cjs +915 -96
  40. package/swap.d.cts +161 -10
  41. package/swap.d.mts +161 -10
  42. package/swap.d.ts +161 -10
  43. package/swap.mjs +915 -96
  44. package/unifiedBalance.cjs +822 -115
  45. package/unifiedBalance.d.cts +224 -4
  46. package/unifiedBalance.d.mts +224 -4
  47. package/unifiedBalance.d.ts +224 -4
  48. package/unifiedBalance.mjs +822 -115
@@ -20,6 +20,7 @@ import { z } from 'zod';
20
20
  import pino from 'pino';
21
21
  import { formatUnits as formatUnits$1, parseUnits as parseUnits$1 } from '@ethersproject/units';
22
22
  import { hexlify, hexZeroPad } from '@ethersproject/bytes';
23
+ import '@ethersproject/abi';
23
24
  import { getAddress } from '@ethersproject/address';
24
25
  import bs58 from 'bs58';
25
26
  import { PublicKey } from '@solana/web3.js';
@@ -2472,6 +2473,8 @@ class KitError extends Error {
2472
2473
  Blockchain["Celo_Alfajores_Testnet"] = "Celo_Alfajores_Testnet";
2473
2474
  Blockchain["Codex"] = "Codex";
2474
2475
  Blockchain["Codex_Testnet"] = "Codex_Testnet";
2476
+ Blockchain["Cronos"] = "Cronos";
2477
+ Blockchain["Cronos_Testnet"] = "Cronos_Testnet";
2475
2478
  Blockchain["Edge"] = "Edge";
2476
2479
  Blockchain["Edge_Testnet"] = "Edge_Testnet";
2477
2480
  Blockchain["Ethereum"] = "Ethereum";
@@ -2554,6 +2557,7 @@ var BridgeChain;
2554
2557
  BridgeChain["Avalanche"] = "Avalanche";
2555
2558
  BridgeChain["Base"] = "Base";
2556
2559
  BridgeChain["Codex"] = "Codex";
2560
+ BridgeChain["Cronos"] = "Cronos";
2557
2561
  BridgeChain["Edge"] = "Edge";
2558
2562
  BridgeChain["Ethereum"] = "Ethereum";
2559
2563
  BridgeChain["HyperEVM"] = "HyperEVM";
@@ -2578,6 +2582,7 @@ var BridgeChain;
2578
2582
  BridgeChain["Avalanche_Fuji"] = "Avalanche_Fuji";
2579
2583
  BridgeChain["Base_Sepolia"] = "Base_Sepolia";
2580
2584
  BridgeChain["Codex_Testnet"] = "Codex_Testnet";
2585
+ BridgeChain["Cronos_Testnet"] = "Cronos_Testnet";
2581
2586
  BridgeChain["Edge_Testnet"] = "Edge_Testnet";
2582
2587
  BridgeChain["Ethereum_Sepolia"] = "Ethereum_Sepolia";
2583
2588
  BridgeChain["HyperEVM_Testnet"] = "HyperEVM_Testnet";
@@ -3089,7 +3094,10 @@ var EarnChain;
3089
3094
  contracts: {
3090
3095
  v1: {
3091
3096
  wallet: GATEWAY_WALLET_EVM_TESTNET,
3092
- minter: GATEWAY_MINTER_EVM_TESTNET
3097
+ minter: GATEWAY_MINTER_EVM_TESTNET,
3098
+ // DepositForHandler the GenericExecutor calls to run a fast cross-chain
3099
+ // deposit into the GatewayWallet above.
3100
+ depositForHandler: '0xD05E7D2E7d30b92c5F17d7d0fC575fce231F1A48'
3093
3101
  }
3094
3102
  },
3095
3103
  forwarderSupported: {
@@ -3623,6 +3631,96 @@ var EarnChain;
3623
3631
  }
3624
3632
  });
3625
3633
 
3634
+ /**
3635
+ * Cronos Mainnet chain definition
3636
+ * @remarks
3637
+ * This represents the official production network for the Cronos blockchain.
3638
+ * Cronos is an EVM-compatible blockchain.
3639
+ */ const Cronos = defineChain({
3640
+ type: 'evm',
3641
+ chain: Blockchain.Cronos,
3642
+ name: 'Cronos',
3643
+ title: 'Cronos Mainnet',
3644
+ nativeCurrency: {
3645
+ name: 'Cronos',
3646
+ symbol: 'CRO',
3647
+ decimals: 18
3648
+ },
3649
+ chainId: 25,
3650
+ isTestnet: false,
3651
+ explorerUrl: 'https://cronoscan.com/tx/{hash}',
3652
+ rpcEndpoints: [
3653
+ 'https://evm.cronos.org'
3654
+ ],
3655
+ eurcAddress: '0xA6dE01a2d62C6B5f3525d768f34d276652C554c8',
3656
+ usdcAddress: '0x3D7F2C478aAfdB65542BCB44bCeeC05849999d2D',
3657
+ usdtAddress: null,
3658
+ cctp: {
3659
+ domain: 32,
3660
+ contracts: {
3661
+ v2: {
3662
+ type: 'split',
3663
+ tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
3664
+ messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
3665
+ confirmations: 1,
3666
+ fastConfirmations: 1
3667
+ }
3668
+ },
3669
+ forwarderSupported: {
3670
+ source: false,
3671
+ destination: false
3672
+ }
3673
+ },
3674
+ kitContracts: {
3675
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET
3676
+ }
3677
+ });
3678
+
3679
+ /**
3680
+ * Cronos Testnet chain definition
3681
+ * @remarks
3682
+ * This represents the official test network for the Cronos blockchain.
3683
+ * Cronos is an EVM-compatible blockchain.
3684
+ */ const CronosTestnet = defineChain({
3685
+ type: 'evm',
3686
+ chain: Blockchain.Cronos_Testnet,
3687
+ name: 'Cronos Testnet',
3688
+ title: 'Cronos Testnet',
3689
+ nativeCurrency: {
3690
+ name: 'CRO',
3691
+ symbol: 'tCRO',
3692
+ decimals: 18
3693
+ },
3694
+ chainId: 338,
3695
+ isTestnet: true,
3696
+ explorerUrl: 'https://explorer.cronos.org/testnet/tx/{hash}',
3697
+ rpcEndpoints: [
3698
+ 'https://evm-t3.cronos.org'
3699
+ ],
3700
+ eurcAddress: '0x31f7538adb53cF16350e6B0c89d03D91b7D12c46',
3701
+ usdcAddress: '0xEb33dc5fac03833e132593659e1dE7256aB59794',
3702
+ usdtAddress: null,
3703
+ cctp: {
3704
+ domain: 32,
3705
+ contracts: {
3706
+ v2: {
3707
+ type: 'split',
3708
+ tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
3709
+ messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
3710
+ confirmations: 1,
3711
+ fastConfirmations: 1
3712
+ }
3713
+ },
3714
+ forwarderSupported: {
3715
+ source: false,
3716
+ destination: false
3717
+ }
3718
+ },
3719
+ kitContracts: {
3720
+ bridge: BRIDGE_CONTRACT_EVM_TESTNET
3721
+ }
3722
+ });
3723
+
3626
3724
  /**
3627
3725
  * Edge Mainnet chain definition
3628
3726
  * @remarks
@@ -5974,6 +6072,8 @@ var Chains = /*#__PURE__*/Object.freeze({
5974
6072
  CeloAlfajoresTestnet: CeloAlfajoresTestnet,
5975
6073
  Codex: Codex,
5976
6074
  CodexTestnet: CodexTestnet,
6075
+ Cronos: Cronos,
6076
+ CronosTestnet: CronosTestnet,
5977
6077
  Edge: Edge,
5978
6078
  EdgeTestnet: EdgeTestnet,
5979
6079
  Ethereum: Ethereum,
@@ -6115,7 +6215,10 @@ var Chains = /*#__PURE__*/Object.freeze({
6115
6215
  minter: z.string({
6116
6216
  required_error: 'Gateway minter address is required. Please provide a valid contract address.',
6117
6217
  invalid_type_error: 'Gateway minter address must be a string.'
6118
- }).min(1, 'Gateway minter address cannot be empty.')
6218
+ }).min(1, 'Gateway minter address cannot be empty.'),
6219
+ depositForHandler: z.string({
6220
+ invalid_type_error: 'Gateway depositForHandler address must be a string.'
6221
+ }).min(1, 'Gateway depositForHandler address cannot be empty.').optional()
6119
6222
  }).strict() // Reject any additional properties not defined in the schema
6120
6223
  ;
6121
6224
  /**
@@ -6637,21 +6740,31 @@ const swapTokenEnumSchema = z.enum([
6637
6740
  * returning the appropriate address based on the requested contract type.
6638
6741
  *
6639
6742
  * @param chain - The chain definition to resolve the contract address for
6640
- * @param contractType - The type of contract address to resolve ('tokenMessenger' or 'messageTransmitter')
6743
+ * @param contractType - The type of contract address to resolve ('tokenMessenger', 'messageTransmitter', or 'tokenMessengerWithFees')
6641
6744
  * @returns The contract address for the specified contract type
6642
6745
  * @throws Error when chain does not support CCTP v2 or has unsupported contract configuration
6746
+ * @throws Error when 'tokenMessengerWithFees' is requested but not configured on the chain
6643
6747
  */ const resolveCCTPV2ContractAddress = (chain, contractType)=>{
6644
6748
  // Handle custom bridge contract for tokenMessenger (burn transaction)
6645
- if (hasCustomContractSupport(chain, 'bridge') && chain.kitContracts?.bridge !== undefined) {
6749
+ if (contractType === 'tokenMessenger' && hasCustomContractSupport(chain, 'bridge') && chain.kitContracts?.bridge !== undefined) {
6646
6750
  return chain.kitContracts.bridge;
6647
6751
  }
6648
6752
  // At this point we know CCTP v2 is supported, so contracts exist
6649
6753
  const cctpConfig = chain.cctp;
6650
6754
  const contracts = cctpConfig.contracts.v2;
6755
+ // The `TokenMessengerWithFees` wrapper (prepaid FORWARD path) is an optional
6756
+ // deployment carried alongside both split and merged configurations.
6757
+ if (contractType === 'tokenMessengerWithFees') {
6758
+ const wrapper = contracts.tokenMessengerWithFees;
6759
+ if (wrapper === undefined || wrapper === '') {
6760
+ throw new Error(`TokenMessengerWithFees is not configured on chain ${chain.name}. The prepaid FORWARD path is unavailable on this chain.`);
6761
+ }
6762
+ return wrapper;
6763
+ }
6651
6764
  // Handle different contract types with explicit type checking
6652
6765
  switch(contracts.type){
6653
6766
  case 'split':
6654
- return contracts.tokenMessenger ;
6767
+ return contractType === 'tokenMessenger' ? contracts.tokenMessenger : contracts.messageTransmitter;
6655
6768
  case 'merged':
6656
6769
  return contracts.contract;
6657
6770
  default:
@@ -8039,6 +8152,7 @@ const swapTokenEnumSchema = z.enum([
8039
8152
  [Blockchain.Base]: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
8040
8153
  [Blockchain.Celo]: '0xcebA9300f2b948710d2653dD7B07f33A8B32118C',
8041
8154
  [Blockchain.Codex]: '0xd996633a415985DBd7D6D12f4A4343E31f5037cf',
8155
+ [Blockchain.Cronos]: '0x3D7F2C478aAfdB65542BCB44bCeeC05849999d2D',
8042
8156
  [Blockchain.Edge]: '0x98d2919b9A214E6Fa5384AC81E6864bA686Ad74c',
8043
8157
  [Blockchain.Ethereum]: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
8044
8158
  [Blockchain.Hedera]: '0.0.456858',
@@ -8072,6 +8186,7 @@ const swapTokenEnumSchema = z.enum([
8072
8186
  [Blockchain.Avalanche_Fuji]: '0x5425890298aed601595a70AB815c96711a31Bc65',
8073
8187
  [Blockchain.Base_Sepolia]: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',
8074
8188
  [Blockchain.Codex_Testnet]: '0x6d7f141b6819C2c9CC2f818e6ad549E7Ca090F8f',
8189
+ [Blockchain.Cronos_Testnet]: '0xEb33dc5fac03833e132593659e1dE7256aB59794',
8075
8190
  [Blockchain.Edge_Testnet]: '0x2d9F7CAD728051AA35Ecdc472a14cf8cDF5CFD6B',
8076
8191
  [Blockchain.Ethereum_Sepolia]: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238',
8077
8192
  [Blockchain.Hedera_Testnet]: '0.0.429274',
@@ -8144,6 +8259,7 @@ const swapTokenEnumSchema = z.enum([
8144
8259
  // =========================================================================
8145
8260
  [Blockchain.Avalanche]: '0xc891EB4cbdEFf6e073e859e987815Ed1505c2ACD',
8146
8261
  [Blockchain.Base]: '0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42',
8262
+ [Blockchain.Cronos]: '0xA6dE01a2d62C6B5f3525d768f34d276652C554c8',
8147
8263
  [Blockchain.Ethereum]: '0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c',
8148
8264
  [Blockchain.Solana]: 'HzwqbKZw8HxMN6bF2yFZNrht3c2iXXzpKcFu7uBEDKtr',
8149
8265
  [Blockchain.World_Chain]: '0x1C60ba0A0eD1019e8Eb035E6daF4155A5cE2380B',
@@ -8152,6 +8268,7 @@ const swapTokenEnumSchema = z.enum([
8152
8268
  // =========================================================================
8153
8269
  [Blockchain.Arc_Testnet]: '0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a',
8154
8270
  [Blockchain.Base_Sepolia]: '0x808456652fdb597867f38412077A9182bf77359F',
8271
+ [Blockchain.Cronos_Testnet]: '0x31f7538adb53cF16350e6B0c89d03D91b7D12c46',
8155
8272
  [Blockchain.Ethereum_Sepolia]: '0x08210F9170F89Ab7658F0B5E3fF39b0E03C594D4'
8156
8273
  }
8157
8274
  };
@@ -9502,7 +9619,7 @@ function resolveOptions(options) {
9502
9619
  }
9503
9620
 
9504
9621
  var name$2 = "@circle-fin/bridge-kit";
9505
- var version$3 = "1.11.1";
9622
+ var version$3 = "1.12.1";
9506
9623
  var pkg$3 = {
9507
9624
  name: name$2,
9508
9625
  version: version$3};
@@ -12006,6 +12123,144 @@ const FAST_TIER_FINALITY_THRESHOLD = 1000;
12006
12123
  return false;
12007
12124
  };
12008
12125
 
12126
+ /**
12127
+ * The zero address, denoting a native-currency fee in a signed quote.
12128
+ */ const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
12129
+ /**
12130
+ * Resolve the fee payment channel for a prepaid-FORWARD burn via `TokenMessengerWithFees`.
12131
+ *
12132
+ * Determines the native `msg.value` and the ERC-20 approvals required, honouring
12133
+ * the quote's `feeToken`:
12134
+ * - Native fee (`feeToken` is the zero address): attach exactly `feeTotalAmount`
12135
+ * as `msg.value`; approve only the burn amount.
12136
+ * - ERC-20 fee equal to the burn token (both USDC — the `isBurnTokenFee` case):
12137
+ * approve a single combined `amount + feeTotalAmount` and skip the redundant
12138
+ * second approval.
12139
+ * - ERC-20 fee different from the burn token: approve the burn amount and the fee
12140
+ * amount separately.
12141
+ *
12142
+ * This encodes only balance/allowance intent; it does not fetch balances. The
12143
+ * caller is responsible for a balance preflight against the fresh quote.
12144
+ *
12145
+ * @param params - The fee token, burn token, burn amount, and total fee amount.
12146
+ * @returns The resolved fee payment plan.
12147
+ * @throws KitError if `amount` or `feeTotalAmount` is negative.
12148
+ *
12149
+ * @example
12150
+ * ```typescript
12151
+ * // Native fee
12152
+ * resolveFeePayment({
12153
+ * feeToken: '0x0000000000000000000000000000000000000000',
12154
+ * burnToken: '0xUSDC...',
12155
+ * amount: 1_000_000n,
12156
+ * feeTotalAmount: 3_500_000n,
12157
+ * })
12158
+ * // → { isNativeFee: true, isBurnTokenFee: false, nativeValue: 3_500_000n,
12159
+ * // approvals: [{ token: '0xUSDC...', amount: 1_000_000n }] }
12160
+ * ```
12161
+ */ const resolveFeePayment = (params)=>{
12162
+ const { feeToken, burnToken, amount, feeTotalAmount } = params;
12163
+ if (typeof amount !== 'bigint' || amount < 0n) {
12164
+ throw createValidationFailedError$1('amount', amount, 'Must be a non-negative bigint');
12165
+ }
12166
+ if (typeof feeTotalAmount !== 'bigint' || feeTotalAmount < 0n) {
12167
+ throw createValidationFailedError$1('feeTotalAmount', feeTotalAmount, 'Must be a non-negative bigint');
12168
+ }
12169
+ const isNativeFee = feeToken.toLowerCase() === ZERO_ADDRESS;
12170
+ const isBurnTokenFee = !isNativeFee && feeToken.toLowerCase() === burnToken.toLowerCase();
12171
+ if (isNativeFee) {
12172
+ return {
12173
+ isNativeFee: true,
12174
+ isBurnTokenFee: false,
12175
+ nativeValue: feeTotalAmount,
12176
+ approvals: [
12177
+ {
12178
+ token: burnToken,
12179
+ amount
12180
+ }
12181
+ ]
12182
+ };
12183
+ }
12184
+ if (isBurnTokenFee) {
12185
+ // Fee and burn draw on the same token — a single combined approval covers
12186
+ // both; the redundant second approval is skipped.
12187
+ return {
12188
+ isNativeFee: false,
12189
+ isBurnTokenFee: true,
12190
+ nativeValue: 0n,
12191
+ approvals: [
12192
+ {
12193
+ token: burnToken,
12194
+ amount: amount + feeTotalAmount
12195
+ }
12196
+ ]
12197
+ };
12198
+ }
12199
+ return {
12200
+ isNativeFee: false,
12201
+ isBurnTokenFee: false,
12202
+ nativeValue: 0n,
12203
+ approvals: [
12204
+ {
12205
+ token: burnToken,
12206
+ amount
12207
+ },
12208
+ {
12209
+ token: feeToken,
12210
+ amount: feeTotalAmount
12211
+ }
12212
+ ]
12213
+ };
12214
+ };
12215
+
12216
+ /**
12217
+ * The ASCII "cctp-forward" magic, hex-encoded (no `0x`), that a forward-friendly
12218
+ * hookData must start with.
12219
+ */ const CCTP_FORWARD_MAGIC_HEX = Buffer.from(CCTP_FORWARD_MAGIC_PREFIX, 'ascii').toString('hex');
12220
+ /**
12221
+ * Determine whether a hookData blob begins with the `cctp-forward` envelope.
12222
+ *
12223
+ * The prepaid FORWARD path requires the GenericExecutor blob to be wrapped in a
12224
+ * `cctp-forward` frame; without it the wrapper reverts `ForwardFeeWithoutHook`.
12225
+ *
12226
+ * @param hookData - The 0x-prefixed hookData hex string.
12227
+ * @returns True when the hookData starts with the `cctp-forward` magic.
12228
+ *
12229
+ * @example
12230
+ * ```typescript
12231
+ * hasForwardHook('0x636374702d666f7277617264...') // true
12232
+ * hasForwardHook('0xdeadbeef') // false
12233
+ * ```
12234
+ */ const hasForwardHook = (hookData)=>{
12235
+ if (typeof hookData !== 'string') {
12236
+ return false;
12237
+ }
12238
+ const normalized = (hookData.startsWith('0x') ? hookData.slice(2) : hookData).toLowerCase();
12239
+ return normalized.startsWith(CCTP_FORWARD_MAGIC_HEX);
12240
+ };
12241
+ /**
12242
+ * Assert that a hookData blob is forward-friendly for the prepaid FORWARD path.
12243
+ *
12244
+ * The prepaid FORWARD path always requests a FORWARD fee item, so the wrapper
12245
+ * requires the hookData to start with a `cctp-forward` frame. Validating this
12246
+ * before the burn surfaces the guaranteed `ForwardFeeWithoutHook` revert as a
12247
+ * typed input error instead of an on-chain failure.
12248
+ *
12249
+ * @param hookData - The 0x-prefixed hookData hex string.
12250
+ * @throws KitError (`INPUT_VALIDATION_FAILED`) if the hookData is missing or lacks
12251
+ * the `cctp-forward` frame.
12252
+ *
12253
+ * @example
12254
+ * ```typescript
12255
+ * assertForwardHookData(geForwardHookData) // ok
12256
+ * assertForwardHookData('0xdeadbeef') // throws — would revert ForwardFeeWithoutHook
12257
+ * ```
12258
+ */ const assertForwardHookData = (hookData)=>{
12259
+ if (!hasForwardHook(hookData)) {
12260
+ throw createValidationFailedError$1('hookData', hookData, 'Prepaid FORWARD burns require a cctp-forward-wrapped hookData; without it the TokenMessengerWithFees wrapper reverts ForwardFeeWithoutHook');
12261
+ }
12262
+ };
12263
+
12009
12264
  /**
12010
12265
  * Type guard to validate the forwardFee object structure.
12011
12266
  *
@@ -12162,6 +12417,15 @@ const CUSTOM_BURN_GAS_ESTIMATE_EVM = 201_525n // p99 and max are same here: 201_
12162
12417
  ;
12163
12418
  const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_839n) / 2 = 237_401n
12164
12419
  ;
12420
+ // Hard execution caps: observed max + ~30% buffer, used as gasLimit overrides on
12421
+ // chains whose eth_estimateGas under-reports (e.g. Cronos EIP-7623 calldata floor).
12422
+ // Kept separate from the fee-estimate averages above.
12423
+ const APPROVE_GAS_LIMIT_EVM = 100_000n // ERC-20 approve observed max ~46k
12424
+ ;
12425
+ const DEPOSIT_FOR_BURN_GAS_LIMIT_EVM = 300_000n // observed max 226_506 + ~30%
12426
+ ;
12427
+ const RECEIVE_MESSAGE_GAS_LIMIT_EVM = 400_000n // observed max 310_839 + ~30%
12428
+ ;
12165
12429
  /**
12166
12430
  * The minimum finality threshold for CCTPv2 transfers.
12167
12431
  *
@@ -12190,6 +12454,27 @@ const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_8
12190
12454
  'Content-Type': 'application/json'
12191
12455
  }
12192
12456
  };
12457
+ /**
12458
+ * Merges caller-provided polling overrides on top of {@link DEFAULT_CONFIG}.
12459
+ *
12460
+ * Headers are merged independently so caller-supplied headers augment the
12461
+ * defaults (such as `Content-Type`) rather than replacing them wholesale.
12462
+ *
12463
+ * @param config - Caller-provided polling configuration overrides
12464
+ * @param internalDefaults - Internal defaults applied before `config` (for example a
12465
+ * reduced `maxRetries` for one-shot requests); `config` still wins on conflict
12466
+ * @returns The effective polling configuration
12467
+ * @internal
12468
+ */ const mergeAttestationConfig = (config, internalDefaults = {})=>({
12469
+ ...DEFAULT_CONFIG,
12470
+ ...internalDefaults,
12471
+ ...config,
12472
+ headers: {
12473
+ ...DEFAULT_CONFIG.headers,
12474
+ ...internalDefaults.headers,
12475
+ ...config.headers
12476
+ }
12477
+ });
12193
12478
  /**
12194
12479
  * Type guard that verifies if an unknown value matches the AttestationMessage shape
12195
12480
  * and has all required properties.
@@ -12336,10 +12621,7 @@ const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_8
12336
12621
  * ```
12337
12622
  */ const fetchAttestation = async (sourceDomainId, transactionHash, isTestnet, config = {})=>{
12338
12623
  const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
12339
- const effectiveConfig = {
12340
- ...DEFAULT_CONFIG,
12341
- ...config
12342
- };
12624
+ const effectiveConfig = mergeAttestationConfig(config);
12343
12625
  return await pollApiGet(url, isAttestationResponse, effectiveConfig);
12344
12626
  };
12345
12627
  /**
@@ -12382,11 +12664,9 @@ const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_8
12382
12664
  */ const fetchAttestationWithoutStatusCheck = async (sourceDomainId, transactionHash, isTestnet, config = {})=>{
12383
12665
  const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
12384
12666
  // Use minimal retries since we're just fetching existing data
12385
- const effectiveConfig = {
12386
- ...DEFAULT_CONFIG,
12387
- maxRetries: 3,
12388
- ...config
12389
- };
12667
+ const effectiveConfig = mergeAttestationConfig(config, {
12668
+ maxRetries: 3
12669
+ });
12390
12670
  return await pollApiGet(url, isAttestationResponseWithoutStatusCheck, effectiveConfig);
12391
12671
  };
12392
12672
  /**
@@ -12446,10 +12726,7 @@ const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_8
12446
12726
  * ```
12447
12727
  */ const fetchReAttestedAttestation = async (sourceDomainId, transactionHash, isTestnet, config = {})=>{
12448
12728
  const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
12449
- const effectiveConfig = {
12450
- ...DEFAULT_CONFIG,
12451
- ...config
12452
- };
12729
+ const effectiveConfig = mergeAttestationConfig(config);
12453
12730
  return await pollApiGet(url, isReAttestedAttestationResponse, effectiveConfig);
12454
12731
  };
12455
12732
  /**
@@ -12515,81 +12792,206 @@ const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_8
12515
12792
  */ const requestReAttestation = async (nonce, isTestnet, config = {})=>{
12516
12793
  const url = buildReAttestUrl(nonce, isTestnet);
12517
12794
  // Use minimal retries since we're just submitting a request, not polling for state
12518
- const effectiveConfig = {
12519
- ...DEFAULT_CONFIG,
12520
- maxRetries: 3,
12521
- ...config
12522
- };
12795
+ const effectiveConfig = mergeAttestationConfig(config, {
12796
+ maxRetries: 3
12797
+ });
12523
12798
  return await pollApiPost(url, {}, isReAttestationResponse, effectiveConfig);
12524
12799
  };
12525
12800
 
12526
- const assertCCTPv2WalletContextSymbol = Symbol('assertCCTPv2WalletContext');
12527
12801
  /**
12528
- * Asserts that the provided parameters match the CCTPv2 wallet context interface.
12529
- * The validation includes:
12530
- * - Basic wallet context validation (adapter, address, chain)
12531
- * - CCTPv2-specific chain validation (must be an EVM chain)
12532
- *
12533
- * @param params - The parameters to validate
12534
- * @throws {KitError} If validation fails with INPUT_VALIDATION_FAILED code (1098), with details about which properties failed
12535
- *
12536
- * @example
12537
- * ```typescript
12538
- * import { assertCCTPv2WalletContext } from '@circle-fin/provider-cctp-v2'
12539
- * import { Ethereum } from '@core/chains'
12802
+ * Type guard that checks if the relayer has confirmed the mint transaction.
12540
12803
  *
12541
- * // Prepare wallet context
12542
- * const context = {
12543
- * adapter: {
12544
- * prepare: async () => ({ data: 'prepared transaction' }),
12545
- * waitForTransaction: async () => ({ status: 'confirmed' })
12546
- * },
12547
- * address: '0x1234567890123456789012345678901234567890',
12548
- * chain: {
12549
- * ...Ethereum,
12550
- * usdcAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
12551
- * cctp: {
12552
- * domain: 1,
12553
- * contracts: {
12554
- * v2: {
12555
- * tokenMessenger: '0xTokenMessenger',
12556
- * messageTransmitter: '0xMessageTransmitter'
12557
- * }
12558
- * }
12559
- * }
12560
- * }
12561
- * }
12804
+ * This function validates that:
12805
+ * 1. The response has valid AttestationResponse structure
12806
+ * 2. At least one message has forwardState === 'CONFIRMED' (or 'COMPLETE') and a valid forwardTxHash
12562
12807
  *
12563
- * // This will throw if validation fails
12564
- * assertCCTPv2WalletContext(context)
12808
+ * If forwardState is 'FAILED', throws a non-retryable KitError.
12809
+ * If forwardState is 'PENDING' or not present, throws a RETRYABLE KitError to continue polling.
12565
12810
  *
12566
- * // If we get here, context is guaranteed to be valid
12567
- * console.log('CCTPv2 wallet context is valid')
12568
- * ```
12569
- */ function assertCCTPv2WalletContext(params) {
12570
- // First validate basic wallet context
12571
- validateWithStateTracking(params, walletContextSchema, 'CCTPv2 wallet context', assertCCTPv2WalletContextSymbol);
12572
- // After validation, we know params is WalletContext
12573
- const context = params;
12574
- // Validate USDC support
12575
- if (context.chain.usdcAddress === null) {
12576
- throw createInvalidChainError(context.chain.name, 'Does not have USDC configured');
12811
+ * @param obj - The value to check, typically a parsed JSON response
12812
+ * @returns True if the relayer has confirmed the mint
12813
+ * @throws {KitError} With FATAL recoverability if structure is invalid
12814
+ * @throws {KitError} With RESUMABLE recoverability if forwardState is 'FAILED'
12815
+ * @throws {KitError} With RETRYABLE recoverability if still pending
12816
+ * @internal
12817
+ */ const isRelayerMintConfirmed = (obj)=>{
12818
+ // First check if the structure is valid
12819
+ if (!hasValidAttestationStructure(obj)) {
12820
+ throw new KitError({
12821
+ ...InputError.VALIDATION_FAILED,
12822
+ recoverability: 'FATAL',
12823
+ message: 'Invalid attestation response structure from IRIS API.'
12824
+ });
12577
12825
  }
12578
- // Validate CCTPv2 support
12579
- if (!isCCTPV2Supported(context.chain)) {
12580
- throw createInvalidChainError(context.chain.name, 'Does not support CCTPv2');
12826
+ // Find the first message (typically there's only one)
12827
+ const message = obj.messages[0];
12828
+ if (!message) {
12829
+ throw new KitError({
12830
+ ...InputError.VALIDATION_FAILED,
12831
+ recoverability: 'FATAL',
12832
+ message: 'No attestation messages found in IRIS API response.'
12833
+ });
12581
12834
  }
12582
- }
12583
-
12584
- const assertCCTPv2BridgeParamsSymbol = Symbol('assertCCTPv2BridgeParams');
12835
+ // Check for FAILED state - this is a permanent failure
12836
+ if (message.forwardState === 'FAILED') {
12837
+ throw new KitError({
12838
+ ...NetworkError.RELAYER_FORWARD_FAILED,
12839
+ recoverability: 'RESUMABLE',
12840
+ message: 'Circle relayer failed to forward the mint transaction. The mint may still have succeeded if another party submitted it. Check the recipient wallet balance before retrying. If the mint did not occur, you can manually submit it using the attestation data in the error cause.',
12841
+ cause: {
12842
+ trace: {
12843
+ eventNonce: message.eventNonce,
12844
+ attestation: message.attestation,
12845
+ message: message.message
12846
+ }
12847
+ }
12848
+ });
12849
+ }
12850
+ // Check if mint is confirmed (or complete) with a valid transaction hash
12851
+ // We accept both CONFIRMED and COMPLETE since COMPLETE implies CONFIRMED
12852
+ if ((message.forwardState === 'CONFIRMED' || message.forwardState === 'COMPLETE') && typeof message.forwardTxHash === 'string' && message.forwardTxHash.trim().length > 0) {
12853
+ return true;
12854
+ }
12855
+ // Still pending or not yet processed - throw RETRYABLE error to continue polling
12856
+ throw new KitError({
12857
+ ...NetworkError.RELAYER_PENDING,
12858
+ recoverability: 'RETRYABLE',
12859
+ message: 'Relayer mint not ready. Waiting for confirmation.'
12860
+ });
12861
+ };
12585
12862
  /**
12586
- * Asserts that the provided parameters match the CCTPv2 bridge parameters interface.
12587
- * The validation includes:
12588
- * - Basic parameter structure and types
12589
- * - Amount validation (non-empty numeric string \> 0)
12590
- * - Wallet address format validation (must be valid Ethereum address)
12591
- * - Chain definition validation (must be a valid chain with required properties)
12592
- * - Adapter validation (must implement required methods)
12863
+ * Polls the attestation API until the relayer's mint transaction is confirmed.
12864
+ *
12865
+ * This function is used when `useForwarder` is enabled. Instead of the user
12866
+ * submitting the mint transaction, Circle's Orbit relayer handles it automatically.
12867
+ * This function polls until the relayer has submitted and confirmed the mint transaction.
12868
+ *
12869
+ * @remarks
12870
+ * - Uses a 20-minute timeout by default (600 retries × 2 seconds)
12871
+ * - Throws immediately if `forwardState` is 'FAILED'
12872
+ * - Waits for `forwardState` to be 'CONFIRMED' or 'COMPLETE' (COMPLETE implies CONFIRMED)
12873
+ * - Returns the attestation message with `forwardTxHash` populated
12874
+ *
12875
+ * @param sourceDomainId - The CCTP domain ID of the source chain
12876
+ * @param transactionHash - The transaction hash of the burn operation
12877
+ * @param isTestnet - Whether this is for a testnet chain (true) or mainnet (false)
12878
+ * @param config - Optional configuration overrides for polling behavior
12879
+ * @returns The attestation message with confirmed forwardTxHash
12880
+ * @throws {KitError} With code 'NETWORK_RELAYER_FORWARD_FAILED' if relayer failed
12881
+ * @throws {KitError} If timeout is reached while still pending
12882
+ *
12883
+ * @example
12884
+ * ```typescript
12885
+ * const attestation = await fetchRelayerMint(0, '0xabc...', false)
12886
+ * console.log('Relayer mint tx:', attestation.forwardTxHash)
12887
+ * ```
12888
+ */ const fetchRelayerMint = async (sourceDomainId, transactionHash, isTestnet, config = {})=>{
12889
+ const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
12890
+ const effectiveConfig = mergeAttestationConfig(config);
12891
+ let response;
12892
+ try {
12893
+ response = await pollApiGet(url, isRelayerMintConfirmed, effectiveConfig);
12894
+ } catch (error) {
12895
+ // Enrich RELAYER_FORWARD_FAILED errors with the burn transaction hash
12896
+ if (error instanceof KitError && error.name === 'NETWORK_RELAYER_FORWARD_FAILED') {
12897
+ throw new KitError({
12898
+ ...NetworkError.RELAYER_FORWARD_FAILED,
12899
+ recoverability: error.recoverability,
12900
+ message: error.message,
12901
+ cause: {
12902
+ ...error.cause,
12903
+ trace: {
12904
+ ...error.cause?.trace,
12905
+ burnTxHash: transactionHash
12906
+ }
12907
+ }
12908
+ });
12909
+ }
12910
+ throw error;
12911
+ }
12912
+ // Return the first message (which should have forwardTxHash)
12913
+ // Note: This check is needed for TypeScript type safety even though
12914
+ // isRelayerMintConfirmed validates messages[0] exists. The type guard
12915
+ // narrows the type at the call site, but TypeScript can't infer that
12916
+ // the array still has elements after pollApiGet returns.
12917
+ const message = response.messages[0];
12918
+ if (!message) {
12919
+ throw new KitError({
12920
+ ...InputError.VALIDATION_FAILED,
12921
+ recoverability: 'FATAL',
12922
+ message: 'No attestation messages found in response after polling.'
12923
+ });
12924
+ }
12925
+ return message;
12926
+ };
12927
+
12928
+ const assertCCTPv2WalletContextSymbol = Symbol('assertCCTPv2WalletContext');
12929
+ /**
12930
+ * Asserts that the provided parameters match the CCTPv2 wallet context interface.
12931
+ * The validation includes:
12932
+ * - Basic wallet context validation (adapter, address, chain)
12933
+ * - CCTPv2-specific chain validation (must be an EVM chain)
12934
+ *
12935
+ * @param params - The parameters to validate
12936
+ * @throws {KitError} If validation fails with INPUT_VALIDATION_FAILED code (1098), with details about which properties failed
12937
+ *
12938
+ * @example
12939
+ * ```typescript
12940
+ * import { assertCCTPv2WalletContext } from '@circle-fin/provider-cctp-v2'
12941
+ * import { Ethereum } from '@core/chains'
12942
+ *
12943
+ * // Prepare wallet context
12944
+ * const context = {
12945
+ * adapter: {
12946
+ * prepare: async () => ({ data: 'prepared transaction' }),
12947
+ * waitForTransaction: async () => ({ status: 'confirmed' })
12948
+ * },
12949
+ * address: '0x1234567890123456789012345678901234567890',
12950
+ * chain: {
12951
+ * ...Ethereum,
12952
+ * usdcAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
12953
+ * cctp: {
12954
+ * domain: 1,
12955
+ * contracts: {
12956
+ * v2: {
12957
+ * tokenMessenger: '0xTokenMessenger',
12958
+ * messageTransmitter: '0xMessageTransmitter'
12959
+ * }
12960
+ * }
12961
+ * }
12962
+ * }
12963
+ * }
12964
+ *
12965
+ * // This will throw if validation fails
12966
+ * assertCCTPv2WalletContext(context)
12967
+ *
12968
+ * // If we get here, context is guaranteed to be valid
12969
+ * console.log('CCTPv2 wallet context is valid')
12970
+ * ```
12971
+ */ function assertCCTPv2WalletContext(params) {
12972
+ // First validate basic wallet context
12973
+ validateWithStateTracking(params, walletContextSchema, 'CCTPv2 wallet context', assertCCTPv2WalletContextSymbol);
12974
+ // After validation, we know params is WalletContext
12975
+ const context = params;
12976
+ // Validate USDC support
12977
+ if (context.chain.usdcAddress === null) {
12978
+ throw createInvalidChainError(context.chain.name, 'Does not have USDC configured');
12979
+ }
12980
+ // Validate CCTPv2 support
12981
+ if (!isCCTPV2Supported(context.chain)) {
12982
+ throw createInvalidChainError(context.chain.name, 'Does not support CCTPv2');
12983
+ }
12984
+ }
12985
+
12986
+ const assertCCTPv2BridgeParamsSymbol = Symbol('assertCCTPv2BridgeParams');
12987
+ /**
12988
+ * Asserts that the provided parameters match the CCTPv2 bridge parameters interface.
12989
+ * The validation includes:
12990
+ * - Basic parameter structure and types
12991
+ * - Amount validation (non-empty numeric string \> 0)
12992
+ * - Wallet address format validation (must be valid Ethereum address)
12993
+ * - Chain definition validation (must be a valid chain with required properties)
12994
+ * - Adapter validation (must implement required methods)
12593
12995
  * - Optional config validation (transfer speed and max fee)
12594
12996
  * - Network compatibility (source and destination chains must both be testnet or both mainnet)
12595
12997
  * - CCTPv2-specific wallet context validations
@@ -12790,6 +13192,109 @@ const assertCCTPv2BridgeParamsSymbol = Symbol('assertCCTPv2BridgeParams');
12790
13192
  }
12791
13193
  }
12792
13194
 
13195
+ /**
13196
+ * Assert that `params` is a well-formed {@link BurnWithFeesParams} object.
13197
+ *
13198
+ * Validates the full public-boundary input before any field destructuring,
13199
+ * `BigInt()` coercion, or adapter preparation runs, so malformed JS-caller
13200
+ * inputs always produce typed `KitError` validation failures.
13201
+ *
13202
+ * Checks performed (in order):
13203
+ * - `params` must be a non-null plain object
13204
+ * - `source` — valid CCTP v2 wallet context (via `assertCCTPv2WalletContext`)
13205
+ * - `destinationChain` — present and supports CCTP v2
13206
+ * - source and destination chains must both be testnet or both mainnet
13207
+ * - source and destination chains must differ
13208
+ * - `executor` — non-empty string
13209
+ * - `amount` — bigint or non-empty string coercible to bigint
13210
+ * - `feeTotalAmount` — bigint or non-empty string coercible to bigint
13211
+ * - `feeToken` — valid EVM address (`0x` + 40 hex chars)
13212
+ * - `claim.signedQuote` — valid `0x`-prefixed hex string
13213
+ * - `claim.refundAddress` — valid EVM address
13214
+ * - `hookData` — valid `0x`-prefixed hex string when present
13215
+ *
13216
+ * @param params - The value to validate.
13217
+ * @throws {KitError} If any field is missing or invalid.
13218
+ *
13219
+ * @example
13220
+ * ```typescript
13221
+ * assertBurnWithFeesParams(params)
13222
+ * // params is now typed as BurnWithFeesParams and safe to use
13223
+ * const { source, destinationChain, amount } = params
13224
+ * ```
13225
+ */ function assertBurnWithFeesParams(params) {
13226
+ if (params === null || typeof params !== 'object' || Array.isArray(params)) {
13227
+ throw createValidationFailedError$1('params', params, 'Must be a non-null plain object');
13228
+ }
13229
+ const p = params;
13230
+ // Source wallet context
13231
+ assertCCTPv2WalletContext(p['source']);
13232
+ const source = p['source'];
13233
+ // destinationChain
13234
+ const destinationChain = p['destinationChain'];
13235
+ if (destinationChain === null || destinationChain === undefined || typeof destinationChain !== 'object') {
13236
+ throw createValidationFailedError$1('destinationChain', destinationChain, 'Must be a chain definition object');
13237
+ }
13238
+ if (!isCCTPV2Supported(destinationChain)) {
13239
+ throw createValidationFailedError$1('destinationChain', destinationChain, 'destinationChain must support CCTP v2');
13240
+ }
13241
+ const dest = destinationChain;
13242
+ // Testnet / mainnet mismatch
13243
+ if (source.chain.isTestnet !== dest.isTestnet) {
13244
+ throw createNetworkMismatchError(source.chain, dest);
13245
+ }
13246
+ // Same-chain guard
13247
+ if (source.chain.name === dest.name) {
13248
+ throw createUnsupportedRouteError(source.chain.name, dest.name);
13249
+ }
13250
+ // executor
13251
+ const executor = p['executor'];
13252
+ if (typeof executor !== 'string' || executor === '') {
13253
+ throw createValidationFailedError$1('executor', executor, 'A GenericExecutor address is required (used for both mintRecipient and destinationCaller)');
13254
+ }
13255
+ // amount
13256
+ const rawAmount = p['amount'];
13257
+ if (typeof rawAmount !== 'bigint' && typeof rawAmount !== 'string' || rawAmount === '') {
13258
+ throw createValidationFailedError$1('amount', rawAmount, 'Must be a bigint or a numeric string');
13259
+ }
13260
+ try {
13261
+ BigInt(rawAmount);
13262
+ } catch {
13263
+ throw createValidationFailedError$1('amount', rawAmount, 'Must be a valid integer value coercible to bigint');
13264
+ }
13265
+ // feeTotalAmount
13266
+ const rawFee = p['feeTotalAmount'];
13267
+ if (typeof rawFee !== 'bigint' && typeof rawFee !== 'string' || rawFee === '') {
13268
+ throw createValidationFailedError$1('feeTotalAmount', rawFee, 'Must be a bigint or a numeric string');
13269
+ }
13270
+ try {
13271
+ BigInt(rawFee);
13272
+ } catch {
13273
+ throw createValidationFailedError$1('feeTotalAmount', rawFee, 'Must be a valid integer value coercible to bigint');
13274
+ }
13275
+ // feeToken
13276
+ if (!evmAddressSchema.safeParse(p['feeToken']).success) {
13277
+ throw createValidationFailedError$1('feeToken', p['feeToken'], 'Must be a valid EVM address (use the zero address for a native fee)');
13278
+ }
13279
+ // claim
13280
+ const rawClaim = p['claim'];
13281
+ if (rawClaim === null || typeof rawClaim !== 'object' || Array.isArray(rawClaim)) {
13282
+ throw createValidationFailedError$1('claim', rawClaim, 'Must be an object with signedQuote and refundAddress');
13283
+ }
13284
+ const claim = rawClaim;
13285
+ if (!hexStringSchema.safeParse(claim['signedQuote']).success) {
13286
+ throw createValidationFailedError$1('claim.signedQuote', claim['signedQuote'], 'Must be a valid hex string starting with 0x');
13287
+ }
13288
+ if (!evmAddressSchema.safeParse(claim['refundAddress']).success) {
13289
+ throw createValidationFailedError$1('claim.refundAddress', claim['refundAddress'], 'Must be a valid EVM address');
13290
+ }
13291
+ // hookData (optional)
13292
+ const hookData = p['hookData'];
13293
+ if (hookData !== undefined && hookData !== '' && !hexStringSchema.safeParse(hookData).success) {
13294
+ throw createValidationFailedError$1('hookData', hookData, 'Must be a valid hex string starting with 0x');
13295
+ }
13296
+ }
13297
+
12793
13298
  /**
12794
13299
  * CCTP bridge step names that can occur in the bridging flow.
12795
13300
  *
@@ -13397,6 +13902,8 @@ function hasPendingState(analysis, result) {
13397
13902
  * - `adapter`: The adapter that will execute the transaction
13398
13903
  * - `confirmations`: The number of confirmations to wait for (defaults to 1)
13399
13904
  * - `timeout`: The timeout for the request in milliseconds
13905
+ * - `gasLimit`: Optional explicit gas limit (number) forwarded to EVM execute,
13906
+ * bypassing `eth_estimateGas`; ignored for non-EVM requests
13400
13907
  * @returns The bridge step with the transaction details and explorer URL
13401
13908
  * @throws If the transaction execution fails
13402
13909
  *
@@ -13411,7 +13918,7 @@ function hasPendingState(analysis, result) {
13411
13918
  * })
13412
13919
  * console.log('Transaction hash:', step.txHash)
13413
13920
  * ```
13414
- */ async function executePreparedChainRequest({ name, request, adapter, chain, confirmations = 1, timeout }) {
13921
+ */ async function executePreparedChainRequest({ name, request, adapter, chain, confirmations = 1, timeout, gasLimit }) {
13415
13922
  const step = {
13416
13923
  name,
13417
13924
  state: 'pending'
@@ -13424,7 +13931,9 @@ function hasPendingState(analysis, result) {
13424
13931
  step.state = 'noop';
13425
13932
  return step;
13426
13933
  }
13427
- const txHash = await request.execute();
13934
+ const txHash = request.type === 'evm' && gasLimit !== undefined ? await request.execute({
13935
+ gasLimit
13936
+ }) : await request.execute();
13428
13937
  step.txHash = txHash;
13429
13938
  const retryOptions = {
13430
13939
  isRetryable: (err)=>isRetryableError$1(parseBlockchainError(err, {
@@ -13496,7 +14005,8 @@ function hasPendingState(analysis, result) {
13496
14005
  name: 'approve',
13497
14006
  adapter: params.source.adapter,
13498
14007
  chain: params.source.chain,
13499
- request: await provider.approve(params.source, approvalAmount)
14008
+ request: await provider.approve(params.source, approvalAmount),
14009
+ gasLimit: Number(APPROVE_GAS_LIMIT_EVM)
13500
14010
  });
13501
14011
  }
13502
14012
 
@@ -13523,7 +14033,8 @@ function hasPendingState(analysis, result) {
13523
14033
  name: 'burn',
13524
14034
  adapter: params.source.adapter,
13525
14035
  chain: params.source.chain,
13526
- request: await provider.burn(params)
14036
+ request: await provider.burn(params),
14037
+ gasLimit: Number(DEPOSIT_FOR_BURN_GAS_LIMIT_EVM)
13527
14038
  });
13528
14039
  }
13529
14040
 
@@ -13609,11 +14120,18 @@ function hasPendingState(analysis, result) {
13609
14120
  */ async function bridgeMint({ params, provider }, attestation) {
13610
14121
  // Validate attestation message matches transfer params
13611
14122
  await assertCCTPv2AttestationParams(attestation, params);
14123
+ const mintRequest = await provider.mint(params.source, params.destination, attestation);
13612
14124
  const step = await executePreparedChainRequest({
13613
14125
  name: 'mint',
13614
14126
  adapter: params.destination.adapter,
13615
14127
  chain: params.destination.chain,
13616
- request: await provider.mint(params.source, params.destination, attestation)
14128
+ request: mintRequest,
14129
+ // Some chains (e.g. Cronos) enforce an EIP-7623 calldata gas floor that
14130
+ // eth_estimateGas does not account for, returning a below-floor value
14131
+ // without reverting. Pinning to a value above the observed execution max
14132
+ // (310_839) bypasses re-estimation and guarantees we clear both the floor
14133
+ // and the actual execution cost.
14134
+ gasLimit: Number(RECEIVE_MESSAGE_GAS_LIMIT_EVM)
13617
14135
  });
13618
14136
  // Add forwarded: false for non-relayer mints
13619
14137
  return {
@@ -13651,136 +14169,6 @@ const mockAttestationMessage = {
13651
14169
  delayReason: null
13652
14170
  };
13653
14171
 
13654
- /**
13655
- * Type guard that checks if the relayer has confirmed the mint transaction.
13656
- *
13657
- * This function validates that:
13658
- * 1. The response has valid AttestationResponse structure
13659
- * 2. At least one message has forwardState === 'CONFIRMED' (or 'COMPLETE') and a valid forwardTxHash
13660
- *
13661
- * If forwardState is 'FAILED', throws a non-retryable KitError.
13662
- * If forwardState is 'PENDING' or not present, throws a RETRYABLE KitError to continue polling.
13663
- *
13664
- * @param obj - The value to check, typically a parsed JSON response
13665
- * @returns True if the relayer has confirmed the mint
13666
- * @throws {KitError} With FATAL recoverability if structure is invalid
13667
- * @throws {KitError} With RESUMABLE recoverability if forwardState is 'FAILED'
13668
- * @throws {KitError} With RETRYABLE recoverability if still pending
13669
- * @internal
13670
- */ const isRelayerMintConfirmed = (obj)=>{
13671
- // First check if the structure is valid
13672
- if (!hasValidAttestationStructure(obj)) {
13673
- throw new KitError({
13674
- ...InputError.VALIDATION_FAILED,
13675
- recoverability: 'FATAL',
13676
- message: 'Invalid attestation response structure from IRIS API.'
13677
- });
13678
- }
13679
- // Find the first message (typically there's only one)
13680
- const message = obj.messages[0];
13681
- if (!message) {
13682
- throw new KitError({
13683
- ...InputError.VALIDATION_FAILED,
13684
- recoverability: 'FATAL',
13685
- message: 'No attestation messages found in IRIS API response.'
13686
- });
13687
- }
13688
- // Check for FAILED state - this is a permanent failure
13689
- if (message.forwardState === 'FAILED') {
13690
- throw new KitError({
13691
- ...NetworkError.RELAYER_FORWARD_FAILED,
13692
- recoverability: 'RESUMABLE',
13693
- message: 'Circle relayer failed to forward the mint transaction. The mint may still have succeeded if another party submitted it. Check the recipient wallet balance before retrying. If the mint did not occur, you can manually submit it using the attestation data in the error cause.',
13694
- cause: {
13695
- trace: {
13696
- eventNonce: message.eventNonce,
13697
- attestation: message.attestation,
13698
- message: message.message
13699
- }
13700
- }
13701
- });
13702
- }
13703
- // Check if mint is confirmed (or complete) with a valid transaction hash
13704
- // We accept both CONFIRMED and COMPLETE since COMPLETE implies CONFIRMED
13705
- if ((message.forwardState === 'CONFIRMED' || message.forwardState === 'COMPLETE') && typeof message.forwardTxHash === 'string' && message.forwardTxHash.trim().length > 0) {
13706
- return true;
13707
- }
13708
- // Still pending or not yet processed - throw RETRYABLE error to continue polling
13709
- throw new KitError({
13710
- ...NetworkError.RELAYER_PENDING,
13711
- recoverability: 'RETRYABLE',
13712
- message: 'Relayer mint not ready. Waiting for confirmation.'
13713
- });
13714
- };
13715
- /**
13716
- * Polls the attestation API until the relayer's mint transaction is confirmed.
13717
- *
13718
- * This function is used when `useForwarder` is enabled. Instead of the user
13719
- * submitting the mint transaction, Circle's Orbit relayer handles it automatically.
13720
- * This function polls until the relayer has submitted and confirmed the mint transaction.
13721
- *
13722
- * @remarks
13723
- * - Uses a 20-minute timeout by default (600 retries × 2 seconds)
13724
- * - Throws immediately if `forwardState` is 'FAILED'
13725
- * - Waits for `forwardState` to be 'CONFIRMED' or 'COMPLETE' (COMPLETE implies CONFIRMED)
13726
- * - Returns the attestation message with `forwardTxHash` populated
13727
- *
13728
- * @param sourceDomainId - The CCTP domain ID of the source chain
13729
- * @param transactionHash - The transaction hash of the burn operation
13730
- * @param isTestnet - Whether this is for a testnet chain (true) or mainnet (false)
13731
- * @param config - Optional configuration overrides for polling behavior
13732
- * @returns The attestation message with confirmed forwardTxHash
13733
- * @throws {KitError} With code 'NETWORK_RELAYER_FORWARD_FAILED' if relayer failed
13734
- * @throws {KitError} If timeout is reached while still pending
13735
- *
13736
- * @example
13737
- * ```typescript
13738
- * const attestation = await fetchRelayerMint(0, '0xabc...', false)
13739
- * console.log('Relayer mint tx:', attestation.forwardTxHash)
13740
- * ```
13741
- */ const fetchRelayerMint = async (sourceDomainId, transactionHash, isTestnet, config = {})=>{
13742
- const url = buildIrisUrl(sourceDomainId, transactionHash, isTestnet);
13743
- const effectiveConfig = {
13744
- ...DEFAULT_CONFIG,
13745
- ...config
13746
- };
13747
- let response;
13748
- try {
13749
- response = await pollApiGet(url, isRelayerMintConfirmed, effectiveConfig);
13750
- } catch (error) {
13751
- // Enrich RELAYER_FORWARD_FAILED errors with the burn transaction hash
13752
- if (error instanceof KitError && error.name === 'NETWORK_RELAYER_FORWARD_FAILED') {
13753
- throw new KitError({
13754
- ...NetworkError.RELAYER_FORWARD_FAILED,
13755
- recoverability: error.recoverability,
13756
- message: error.message,
13757
- cause: {
13758
- ...error.cause,
13759
- trace: {
13760
- ...error.cause?.trace,
13761
- burnTxHash: transactionHash
13762
- }
13763
- }
13764
- });
13765
- }
13766
- throw error;
13767
- }
13768
- // Return the first message (which should have forwardTxHash)
13769
- // Note: This check is needed for TypeScript type safety even though
13770
- // isRelayerMintConfirmed validates messages[0] exists. The type guard
13771
- // narrows the type at the call site, but TypeScript can't infer that
13772
- // the array still has elements after pollApiGet returns.
13773
- const message = response.messages[0];
13774
- if (!message) {
13775
- throw new KitError({
13776
- ...InputError.VALIDATION_FAILED,
13777
- recoverability: 'FATAL',
13778
- message: 'No attestation messages found in response after polling.'
13779
- });
13780
- }
13781
- return message;
13782
- };
13783
-
13784
14172
  /**
13785
14173
  * Executes the mint step for forwarding mode where Circle's relayer handles the mint.
13786
14174
  *
@@ -13794,6 +14182,7 @@ const mockAttestationMessage = {
13794
14182
  * immediately after the API confirms the mint.
13795
14183
  *
13796
14184
  * @param params - The bridge parameters containing source, destination, amount and config
14185
+ * @param provider - The CCTP v2 bridging provider
13797
14186
  * @param context - The step context containing burnTxHash from the burn step
13798
14187
  * @returns Promise resolving to the bridge step with transaction details
13799
14188
  * @throws {KitError} If burnTxHash is not available in context
@@ -13802,10 +14191,13 @@ const mockAttestationMessage = {
13802
14191
  *
13803
14192
  * @example
13804
14193
  * ```typescript
13805
- * const step = await bridgeRelayerMint(params, { burnTxHash: '0x...' })
14194
+ * const step = await bridgeRelayerMint(
14195
+ * { params, provider },
14196
+ * { burnTxHash: '0x...' }
14197
+ * )
13806
14198
  * console.log('Relayer mint tx:', step.txHash)
13807
14199
  * ```
13808
- */ async function bridgeRelayerMint(params, context) {
14200
+ */ async function bridgeRelayerMint({ params, provider }, context) {
13809
14201
  const step = {
13810
14202
  name: 'mint',
13811
14203
  state: 'pending',
@@ -13821,7 +14213,7 @@ const mockAttestationMessage = {
13821
14213
  }
13822
14214
  try {
13823
14215
  // Poll attestation API until relayer confirms the mint
13824
- const attestation = await fetchRelayerMint(params.source.chain.cctp.domain, context.burnTxHash, params.source.chain.isTestnet ?? false);
14216
+ const attestation = await provider.fetchRelayerMint(params.source, context.burnTxHash);
13825
14217
  // Extract the relayer's mint transaction hash
13826
14218
  const forwardTxHash = attestation.forwardTxHash;
13827
14219
  if (!forwardTxHash) {
@@ -13915,7 +14307,7 @@ const mockAttestationMessage = {
13915
14307
  name: 'mint',
13916
14308
  // bridgeRelayerMint validates context.burnTxHash internally with a KitError
13917
14309
  // bridgeMint requires attestationData which is validated here
13918
- executor: useForwarder ? async (params, _provider, context)=>{
14310
+ executor: useForwarder ? async (params, provider, context)=>{
13919
14311
  if (!context) {
13920
14312
  throw new KitError({
13921
14313
  ...InputError.VALIDATION_FAILED,
@@ -13923,7 +14315,10 @@ const mockAttestationMessage = {
13923
14315
  message: 'Step context is required for relayer mint'
13924
14316
  });
13925
14317
  }
13926
- return bridgeRelayerMint(params, context);
14318
+ return bridgeRelayerMint({
14319
+ params,
14320
+ provider
14321
+ }, context);
13927
14322
  } : async (params, provider, context)=>{
13928
14323
  if (!context?.attestationData) {
13929
14324
  throw new KitError({
@@ -14108,10 +14503,15 @@ const mockAttestationMessage = {
14108
14503
  const burnCallData = burnRequest.getCallData();
14109
14504
  // batchExecute may throw before submission (wallet declined) but never
14110
14505
  // after — post-submission errors are returned as empty receipts.
14506
+ // The sender is threaded for adapters whose execution is routed through a
14507
+ // signing strategy (which has no wallet account to read it from); the
14508
+ // wallet-client path ignores it.
14111
14509
  const batchResult = await adapter.batchExecute([
14112
14510
  approveCallData,
14113
14511
  burnCallData
14114
- ], chain);
14512
+ ], chain, {
14513
+ fromAddress: params.source.address
14514
+ });
14115
14515
  const approveReceipt = batchResult.receipts[0];
14116
14516
  const burnReceipt = batchResult.receipts[1];
14117
14517
  const approveStep = await buildBatchedStep('approve', approveReceipt, batchResult.batchId, adapter, chain, batchResult.statusCode, batchResult.error);
@@ -14254,7 +14654,7 @@ const mockAttestationMessage = {
14254
14654
  return step;
14255
14655
  }
14256
14656
 
14257
- var version$2 = "1.8.5";
14657
+ var version$2 = "1.10.0";
14258
14658
  var pkg$2 = {
14259
14659
  version: version$2};
14260
14660
 
@@ -14960,6 +15360,39 @@ var pkg$2 = {
14960
15360
  }
14961
15361
  }
14962
15362
 
15363
+ function isPlainObject(value) {
15364
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
15365
+ return false;
15366
+ }
15367
+ const prototype = Object.getPrototypeOf(value);
15368
+ return prototype === Object.prototype || prototype === null;
15369
+ }
15370
+ function assertHeadersConfig(headers, field) {
15371
+ if (headers === undefined) {
15372
+ return;
15373
+ }
15374
+ if (!isPlainObject(headers)) {
15375
+ throw createValidationFailedError$1(field, headers, `${field} must be a plain object with string header values when provided`);
15376
+ }
15377
+ for (const [name, value] of Object.entries(headers)){
15378
+ if (typeof value !== 'string') {
15379
+ throw createValidationFailedError$1(`${field}.${name}`, value, 'header values must be strings');
15380
+ }
15381
+ }
15382
+ }
15383
+ function assertCCTPV2Config(config) {
15384
+ if (!isPlainObject(config)) {
15385
+ throw createValidationFailedError$1('config', config, 'config must be a plain object when provided');
15386
+ }
15387
+ assertHeadersConfig(config['headers'], 'config.headers');
15388
+ const attestation = config['attestation'];
15389
+ if (attestation !== undefined) {
15390
+ if (!isPlainObject(attestation)) {
15391
+ throw createValidationFailedError$1('config.attestation', attestation, 'config.attestation must be a plain object when provided');
15392
+ }
15393
+ assertHeadersConfig(attestation['headers'], 'config.attestation.headers');
15394
+ }
15395
+ }
14963
15396
  /**
14964
15397
  * All chains that are supported by the CCTP v2 provider.
14965
15398
  *
@@ -15028,9 +15461,38 @@ var pkg$2 = {
15028
15461
  * @param config - Optional configuration overrides for the provider
15029
15462
  */ constructor(config = {}){
15030
15463
  super();
15464
+ assertCCTPV2Config(config);
15031
15465
  this.config = config;
15032
15466
  }
15033
15467
  /**
15468
+ * Resolves the effective polling configuration for an attestation request.
15469
+ *
15470
+ * Precedence (lowest to highest): provider `config.attestation`, then the
15471
+ * per-call `config`. Headers merge independently across
15472
+ * `config.attestation.headers`, the provider-level `config.headers`, and any
15473
+ * per-call `config.headers`, so a more specific header augments rather than
15474
+ * replaces the broader ones. The `headers` key is omitted entirely when no
15475
+ * headers are configured, leaving the attestation fetchers' defaults intact.
15476
+ *
15477
+ * @param config - Optional per-call polling configuration overrides
15478
+ * @returns The merged polling configuration passed to the attestation fetchers
15479
+ */ resolveAttestationConfig(config) {
15480
+ const headers = {
15481
+ ...this.config?.attestation?.headers,
15482
+ ...this.config?.headers,
15483
+ ...config?.headers
15484
+ };
15485
+ // Polling fields follow normal precedence; headers are merged separately
15486
+ // below so narrower config layers augment rather than replace broader ones.
15487
+ return {
15488
+ ...this.config?.attestation,
15489
+ ...config,
15490
+ ...Object.keys(headers).length > 0 ? {
15491
+ headers
15492
+ } : {}
15493
+ };
15494
+ }
15495
+ /**
15034
15496
  * Execute a cross-chain USDC bridge operation using the CCTP v2 protocol.
15035
15497
  *
15036
15498
  * This method orchestrates the complete CCTP v2 bridge flow including validation,
@@ -15347,7 +15809,7 @@ var pkg$2 = {
15347
15809
  throw new Error(`Failed to resolve operation context: ${error instanceof Error ? error.message : String(error)}`);
15348
15810
  }
15349
15811
  // Resolve spender address with proper error handling
15350
- const spenderAddress = resolveCCTPV2ContractAddress(chain);
15812
+ const spenderAddress = resolveCCTPV2ContractAddress(chain, 'tokenMessenger');
15351
15813
  // Prepare action parameters
15352
15814
  const actionParams = {
15353
15815
  amount: BigInt(amount),
@@ -15456,11 +15918,7 @@ var pkg$2 = {
15456
15918
  */ async fetchAttestation(source, transactionHash, config) {
15457
15919
  assertCCTPv2WalletContext(source);
15458
15920
  try {
15459
- // Merge configs: defaults <- global config <- per-call config
15460
- const effectiveConfig = {
15461
- ...this.config?.attestation,
15462
- ...config
15463
- };
15921
+ const effectiveConfig = this.resolveAttestationConfig(config);
15464
15922
  const response = await fetchAttestation(source.chain.cctp.domain, transactionHash, source.chain.isTestnet, effectiveConfig);
15465
15923
  const message = response.messages[0];
15466
15924
  if (!message) {
@@ -15477,6 +15935,49 @@ var pkg$2 = {
15477
15935
  }
15478
15936
  }
15479
15937
  /**
15938
+ * Polls attestation data until Circle's relayer mint transaction is confirmed.
15939
+ *
15940
+ * This method is used by forwarded transfers. It polls the same Iris
15941
+ * attestation endpoint as {@link CCTPV2BridgingProvider.fetchAttestation},
15942
+ * but waits for a completed relayer forward state and returns the attestation
15943
+ * message containing `forwardTxHash`.
15944
+ *
15945
+ * @typeParam TFromAdapterCapabilities - The type representing the capabilities of the source adapter
15946
+ * @param source - The source wallet context containing the chain definition and wallet address
15947
+ * @param transactionHash - The transaction hash of the burn operation
15948
+ * @param config - Optional polling configuration overrides for timeout, retries, delay, and headers
15949
+ * @returns A promise that resolves to the attestation message with `forwardTxHash`
15950
+ * @throws KitError If the relayer forward fails, the response is invalid, or polling times out
15951
+ *
15952
+ * @example
15953
+ * ```typescript
15954
+ * import { CCTPV2BridgingProvider } from '@circle-fin/provider-cctp-v2'
15955
+ * import { Chains } from '@core/chains'
15956
+ *
15957
+ * const provider = new CCTPV2BridgingProvider({
15958
+ * headers: { 'X-Partner-UUID': '00000000-0000-0000-0000-000000000000' },
15959
+ * })
15960
+ *
15961
+ * const attestation = await provider.fetchRelayerMint(
15962
+ * {
15963
+ * adapter,
15964
+ * chain: Chains.EthereumSepolia,
15965
+ * address: '0x1234...',
15966
+ * },
15967
+ * '0xabc123...',
15968
+ * )
15969
+ *
15970
+ * console.log('Relayer mint tx:', attestation.forwardTxHash)
15971
+ * ```
15972
+ */ async fetchRelayerMint(source, transactionHash, config) {
15973
+ assertCCTPv2WalletContext(source);
15974
+ if (typeof transactionHash !== 'string' || transactionHash.trim() === '') {
15975
+ throw createValidationFailedError$1('transactionHash', transactionHash, 'transactionHash must be a non-empty string');
15976
+ }
15977
+ const effectiveConfig = this.resolveAttestationConfig(config);
15978
+ return await fetchRelayerMint(source.chain.cctp.domain, transactionHash, source.chain.isTestnet, effectiveConfig);
15979
+ }
15980
+ /**
15480
15981
  * Requests a fresh attestation for an expired attestation.
15481
15982
  *
15482
15983
  * This method is used when the original attestation has expired before the mint
@@ -15530,11 +16031,7 @@ var pkg$2 = {
15530
16031
  throw new Error('Failed to re-attest: Invalid transaction hash');
15531
16032
  }
15532
16033
  try {
15533
- // Merge configs: defaults <- global config <- per-call config
15534
- const effectiveConfig = {
15535
- ...this.config?.attestation,
15536
- ...config
15537
- };
16034
+ const effectiveConfig = this.resolveAttestationConfig(config);
15538
16035
  // Step 1: Get existing attestation data to extract nonce
15539
16036
  const existingAttestation = await fetchAttestationWithoutStatusCheck(source.chain.cctp.domain, transactionHash, source.chain.isTestnet, effectiveConfig);
15540
16037
  const nonce = existingAttestation.messages[0]?.eventNonce;
@@ -15792,6 +16289,106 @@ var pkg$2 = {
15792
16289
  return await source.adapter.prepareAction('cctp.v2.depositForBurn', actionParams, resolvedContext);
15793
16290
  }
15794
16291
  /**
16292
+ * Prepare a prepaid-FORWARD burn through the `TokenMessengerWithFees` wrapper.
16293
+ *
16294
+ * Builds the source-chain `depositForBurnWithHookAndFees` call for the
16295
+ * GenericExecutor FORWARD path: fees are collected up front on the source chain
16296
+ * against a signed quote, `mintRecipient` and `destinationCaller` are both set to
16297
+ * the GenericExecutor, and the GE `hookData` is passed through unchanged.
16298
+ *
16299
+ * This is the low-level on-chain primitive behind the UBK `fastCrossChainDeposit`
16300
+ * and Bridge Kit `bridge({ deposit })` flows. The `hookData` and signed-quote
16301
+ * `claim` are produced elsewhere and passed in here:
16302
+ * - `hookData`: `buildForwardingHookDataWithPayload(version,
16303
+ * buildDepositForGenericExecutorPayload(...).hookData)` from `@core/utils`.
16304
+ * - `claim.signedQuote` / `feeToken` / `feeTotalAmount`: from `fetchFeeQuote`
16305
+ * (`@circle-fin/provider-fee-v1`), whose FORWARD item must be bound to the
16306
+ * SAME `hookData` and executor `destinationCaller` used here.
16307
+ *
16308
+ * The returned approvals and burn are NOT executed — the caller executes the
16309
+ * approvals first (in order) and then the burn. The fee payment channel matches
16310
+ * the quote's `feeToken`:
16311
+ * - Native fee: exactly `feeTotalAmount` is attached as the burn's `msg.value`;
16312
+ * only the burn amount is approved.
16313
+ * - USDC fee (same token as the burn): a single combined `amount + feeTotalAmount`
16314
+ * approval covers both; the redundant second approval is skipped.
16315
+ *
16316
+ * @typeParam TFromAdapterCapabilities - The source adapter's capabilities.
16317
+ * @param params - The burn amount, executor, hookData, signed-quote claim, and fee.
16318
+ * @returns The prepared approvals, the prepared burn, and the resolved fee plan.
16319
+ * @throws {KitError} If the wallet context is invalid, `destinationChain` does not
16320
+ * support CCTP v2, the executor is missing, `amount` or `feeTotalAmount` is not
16321
+ * a bigint or a numeric string coercible to bigint, the hookData lacks a
16322
+ * `cctp-forward` frame (guaranteed `ForwardFeeWithoutHook`), or the operation
16323
+ * context cannot be resolved.
16324
+ *
16325
+ * @example
16326
+ * ```typescript
16327
+ * const { approvals, burn } = await provider.burnWithFees({
16328
+ * source,
16329
+ * destinationChain: Arc,
16330
+ * amount: 1_000_000n,
16331
+ * executor: genericExecutorAddress,
16332
+ * hookData: geForwardHookData,
16333
+ * claim: { signedQuote: '0x01...', refundAddress: userAddress },
16334
+ * feeToken: '0x0000000000000000000000000000000000000000', // native
16335
+ * feeTotalAmount: 3_500_000n,
16336
+ * })
16337
+ * for (const approval of approvals) await approval.execute()
16338
+ * const txHash = await burn.execute()
16339
+ * ```
16340
+ */ async burnWithFees(params) {
16341
+ assertBurnWithFeesParams(params);
16342
+ const { source, destinationChain, executor, hookData, claim, feeToken } = params;
16343
+ const amount = BigInt(params.amount);
16344
+ const feeTotalAmount = BigInt(params.feeTotalAmount);
16345
+ // Coupling guard: the prepaid FORWARD path always requests a FORWARD fee item,
16346
+ // so the hookData must carry a cctp-forward frame; otherwise the wrapper
16347
+ // reverts ForwardFeeWithoutHook. Surface it as a typed input error up front.
16348
+ assertForwardHookData(hookData);
16349
+ const burnToken = source.chain.usdcAddress;
16350
+ const feePayment = resolveFeePayment({
16351
+ feeToken,
16352
+ burnToken,
16353
+ amount,
16354
+ feeTotalAmount
16355
+ });
16356
+ // Resolve operation context from the source wallet context.
16357
+ const operationContext = this.extractOperationContext(source);
16358
+ let resolvedContext;
16359
+ try {
16360
+ resolvedContext = await resolveOperationContext(source.adapter, operationContext);
16361
+ } catch (error) {
16362
+ throw createValidationFailedError$1('source.adapter', undefined, `Failed to resolve operation context: ${error instanceof Error ? error.message : String(error)}`);
16363
+ }
16364
+ const context = resolvedContext;
16365
+ const wrapperAddress = resolveCCTPV2ContractAddress(source.chain, 'tokenMessengerWithFees');
16366
+ // Build the ERC-20 approvals to the wrapper (burn token, plus a distinct fee
16367
+ // token only when the fee is not paid in the burn token).
16368
+ const approvals = await Promise.all(feePayment.approvals.map(async (approval)=>source.adapter.prepareAction('token.approve', {
16369
+ tokenAddress: approval.token,
16370
+ delegate: wrapperAddress,
16371
+ amount: approval.amount
16372
+ }, context)));
16373
+ // Build the burn: mintRecipient AND destinationCaller are both the executor.
16374
+ const burn = await source.adapter.prepareAction('cctp.v2.depositForBurnWithFees', {
16375
+ fromChain: source.chain,
16376
+ toChain: destinationChain,
16377
+ amount,
16378
+ mintRecipient: executor,
16379
+ destinationCaller: executor,
16380
+ hookData,
16381
+ claim,
16382
+ feeToken,
16383
+ feeTotalAmount
16384
+ }, context);
16385
+ return {
16386
+ approvals,
16387
+ burn,
16388
+ feePayment
16389
+ };
16390
+ }
16391
+ /**
15795
16392
  * Waits for a transaction to be mined and confirmed on the blockchain.
15796
16393
  *
15797
16394
  * This method should block until the transaction is confirmed on the blockchain.
@@ -15819,8 +16416,13 @@ var pkg$2 = {
15819
16416
  /**
15820
16417
  * The default providers that will be used in addition to the providers provided
15821
16418
  * to the BridgeKit constructor.
15822
- */ const getDefaultProviders = ()=>[
15823
- new CCTPV2BridgingProvider()
16419
+ *
16420
+ * @param config - Optional configuration forwarded to the default providers
16421
+ * @returns The default bridging providers
16422
+ */ const getDefaultProviders = (config = {})=>[
16423
+ new CCTPV2BridgingProvider(config.headers ? {
16424
+ headers: config.headers
16425
+ } : {})
15824
16426
  ];
15825
16427
 
15826
16428
  /**
@@ -16063,7 +16665,9 @@ var pkg$2 = {
16063
16665
  * ```
16064
16666
  */ constructor(config = {}){
16065
16667
  // Handle provider configuration
16066
- const defaultProviders = getDefaultProviders();
16668
+ const defaultProviders = getDefaultProviders(config.headers ? {
16669
+ headers: config.headers
16670
+ } : {});
16067
16671
  this.providers = [
16068
16672
  ...defaultProviders,
16069
16673
  ...config.providers ?? []
@@ -16643,6 +17247,9 @@ registerKit(`${pkg$3.name}/${pkg$3.version}`);
16643
17247
  const kit = new BridgeKit({
16644
17248
  ...context.disableErrorReporting != null && {
16645
17249
  disableErrorReporting: context.disableErrorReporting
17250
+ },
17251
+ ...context.headers != null && {
17252
+ headers: context.headers
16646
17253
  }
16647
17254
  });
16648
17255
  if (hasBoth) {
@@ -16661,7 +17268,7 @@ registerKit(`${pkg$3.name}/${pkg$3.version}`);
16661
17268
  };
16662
17269
 
16663
17270
  var name$1 = "@circle-fin/swap-kit";
16664
- var version$1 = "1.3.1";
17271
+ var version$1 = "1.4.0";
16665
17272
  var pkg$1 = {
16666
17273
  name: name$1,
16667
17274
  version: version$1};
@@ -16726,7 +17333,10 @@ const optionalChainIdentifierField = chainIdentifierField.optional();
16726
17333
  }).min(1, 'kitKey must be a non-empty string').optional(),
16727
17334
  provider: z.string({
16728
17335
  invalid_type_error: 'provider must be a string'
16729
- }).min(1, 'provider must be a non-empty string').optional()
17336
+ }).min(1, 'provider must be a non-empty string').optional(),
17337
+ batchTransactions: z.boolean({
17338
+ invalid_type_error: 'batchTransactions must be a boolean'
17339
+ }).optional()
16730
17340
  });
16731
17341
  /**
16732
17342
  * Zod schema for adapter context.
@@ -17165,7 +17775,7 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
17165
17775
  /**
17166
17776
  * Circle Stablecoin Service API Key.
17167
17777
  * Must be a valid API key format.
17168
- */ apiKey: apiKeySchema
17778
+ */ apiKey: apiKeySchema.optional()
17169
17779
  }).superRefine(requireCrossChainQuoteToAddress);
17170
17780
  /**
17171
17781
  * Zod schema for validating CreateSwapRequest parameters.
@@ -17223,7 +17833,7 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
17223
17833
  /**
17224
17834
  * Circle Stablecoin Service API Key.
17225
17835
  * Must be a valid API key format.
17226
- */ apiKey: apiKeySchema
17836
+ */ apiKey: apiKeySchema.optional()
17227
17837
  });
17228
17838
  /**
17229
17839
  * Zod schema for validating GetSwapStatusResponse data.
@@ -17259,7 +17869,7 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
17259
17869
  toChain: z.string({
17260
17870
  invalid_type_error: 'toChain must be a string'
17261
17871
  }).min(1, 'toChain must be a non-empty string if provided').optional(),
17262
- apiKey: apiKeySchema
17872
+ apiKey: apiKeySchema.optional()
17263
17873
  });
17264
17874
  /**
17265
17875
  * Zod schema for validating CreateSwapResponse payloads.
@@ -17268,13 +17878,15 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
17268
17878
  required_error: 'fee token is required',
17269
17879
  invalid_type_error: 'fee token must be a string'
17270
17880
  }).min(1, 'fee token must be a non-empty string'),
17271
- amount: feeAmountSchema
17881
+ amount: feeAmountSchema,
17882
+ decimals: z.number().int('fee token decimals must be an integer').nonnegative('fee token decimals must be a non-negative integer').optional(),
17883
+ symbol: z.string({
17884
+ invalid_type_error: 'fee token symbol must be a string'
17885
+ }).min(1, 'fee token symbol must be a non-empty string').optional()
17272
17886
  });
17273
17887
  /**
17274
17888
  * Developer fee item schema with basis field.
17275
- */ const createSwapDeveloperFeeItemSchema = z.object({
17276
- token: z.string().min(1, 'fee token must be a non-empty string'),
17277
- amount: feeAmountSchema,
17889
+ */ const createSwapDeveloperFeeItemSchema = createSwapFeeItemSchema.extend({
17278
17890
  basis: z.enum([
17279
17891
  'inputAmount',
17280
17892
  'estimatedAmount'
@@ -17366,7 +17978,7 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
17366
17978
  addresses: z.array(z.string({
17367
17979
  invalid_type_error: 'addresses entries must be strings'
17368
17980
  }).min(1, 'addresses entries must be non-empty strings')).min(1, 'addresses must contain at least one entry when provided').max(MAX_RATE_ADDRESSES_PER_REQUEST, `addresses supports at most ${String(MAX_RATE_ADDRESSES_PER_REQUEST)} values per request`).optional(),
17369
- apiKey: apiKeySchema
17981
+ apiKey: apiKeySchema.optional()
17370
17982
  });
17371
17983
  /**
17372
17984
  * Zod schema for validating GetTokenRatesResponse payloads.
@@ -18589,7 +19201,7 @@ new Set(Object.values(Blockchain));
18589
19201
  registerKit(`${pkg$1.name}/${pkg$1.version}`);
18590
19202
 
18591
19203
  var name = "@circle-fin/earn-kit";
18592
- var version = "1.2.1";
19204
+ var version = "1.3.0";
18593
19205
  var pkg = {
18594
19206
  name: name,
18595
19207
  version: version};
@@ -18719,7 +19331,11 @@ const bridgeFeeTokenSchema = hexAddressSchema;
18719
19331
  asset: z.string(),
18720
19332
  assetAddress: z.string(),
18721
19333
  lltv: z.number(),
18722
- supplyUsd: z.number()
19334
+ supplyUsd: z.number(),
19335
+ // Optional during the expand/contract window (a backend that predates the
19336
+ // field omits the key), mirroring the `.optional()` facets on the base
19337
+ // schema; `null` when the product exposes no per-market allocation (V2).
19338
+ allocationPct: z.number().nullable().optional()
18723
19339
  });
18724
19340
  /**
18725
19341
  * Zod schema for a Morpho vault warning in the API response.
@@ -18733,7 +19349,74 @@ const bridgeFeeTokenSchema = hexAddressSchema;
18733
19349
  ])
18734
19350
  });
18735
19351
  /**
18736
- * Zod schema for a single vault info object in the API response.
19352
+ * Zod schema for the manager (curator) facet in the API response.
19353
+ *
19354
+ * @internal
19355
+ */ const managerSchema = z.object({
19356
+ name: z.string(),
19357
+ address: z.string().optional(),
19358
+ // Only 'curator' is emitted today (Morpho V1/V2). Additional manager roles
19359
+ // are added here as the providers that emit them land, rather than shipped
19360
+ // speculatively.
19361
+ type: z.enum([
19362
+ 'curator'
19363
+ ])
19364
+ });
19365
+ /**
19366
+ * Zod schema for the APY profile facet in the API response.
19367
+ *
19368
+ * @internal
19369
+ */ const apyProfileSchema = z.object({
19370
+ current: z.number(),
19371
+ native: z.number().nullable(),
19372
+ d7: z.number().nullable(),
19373
+ d30: z.number().nullable(),
19374
+ d90: z.number().nullable(),
19375
+ rewardShare: z.number().nullable(),
19376
+ source: z.string().optional(),
19377
+ asOf: z.string().optional()
19378
+ });
19379
+ /**
19380
+ * Zod schema for the fee split facet in the API response.
19381
+ *
19382
+ * @internal
19383
+ */ const feeInfoSchema = z.object({
19384
+ performance: z.number().nullable(),
19385
+ management: z.number().nullable()
19386
+ });
19387
+ /**
19388
+ * Zod schema for the liquidity profile facet in the API response.
19389
+ *
19390
+ * `totalSupply` is the outstanding vault share tokens (ERC4626 totalSupply);
19391
+ * it is validated as a raw JSON amount, like `totalDeposits`/`available`.
19392
+ *
19393
+ * @internal
19394
+ */ const liquidityProfileSchema = z.object({
19395
+ totalDeposits: amountJsonSchema,
19396
+ available: amountJsonSchema,
19397
+ totalSupply: amountJsonSchema,
19398
+ status: z.enum([
19399
+ 'active',
19400
+ 'low_liquidity'
19401
+ ])
19402
+ });
19403
+ /**
19404
+ * Zod schema for the risk signals facet in the API response.
19405
+ *
19406
+ * @internal
19407
+ */ const riskSignalsSchema = z.object({
19408
+ circleSentinel: z.boolean(),
19409
+ warnings: z.array(vaultWarningSchema).optional(),
19410
+ earnKitWarnings: z.array(z.string()).optional()
19411
+ });
19412
+ /**
19413
+ * Zod schema for the universal earn-opportunity base in the API response.
19414
+ *
19415
+ * Retains every existing deprecated flat field (kept validated through the
19416
+ * expand/contract window so default-strip does not drop them) and adds the
19417
+ * new nested facets. The nested facets are `.optional()` during the
19418
+ * transition so the SDK still validates against a not-yet-fully-deployed
19419
+ * backend; they become required after Expand ships.
18737
19420
  *
18738
19421
  * @internal
18739
19422
  */ const vaultInfoResponseSchema = z.object({
@@ -18758,6 +19441,96 @@ const bridgeFeeTokenSchema = hexAddressSchema;
18758
19441
  warnings: z.array(vaultWarningSchema).optional(),
18759
19442
  earnKitWarnings: z.array(z.string()).optional()
18760
19443
  });
19444
+ /**
19445
+ * Shared base schema: existing flat fields (kept) plus the new nested
19446
+ * facets and neutral identity. Facets are `.optional()` during the
19447
+ * transition; flip to required once the backend is confirmed emitting.
19448
+ *
19449
+ * @internal
19450
+ */ const earnBaseSchema = vaultInfoResponseSchema.extend({
19451
+ address: z.string().optional(),
19452
+ asOf: z.string().optional(),
19453
+ manager: managerSchema.nullable().optional(),
19454
+ apyProfile: apyProfileSchema.optional(),
19455
+ fee: feeInfoSchema.optional(),
19456
+ liquidityProfile: liquidityProfileSchema.optional(),
19457
+ riskSignals: riskSignalsSchema.optional()
19458
+ });
19459
+ /**
19460
+ * Zod schema for the `vault` opportunity variant.
19461
+ *
19462
+ * @internal
19463
+ */ const vaultOpportunitySchema = earnBaseSchema.extend({
19464
+ productType: z.literal('vault'),
19465
+ collateral: z.array(collateralSchema)
19466
+ });
19467
+ /**
19468
+ * Discriminated union over `productType`. Add union members here as new
19469
+ * product types (e.g. `lending_market`, `rwa_token`) land.
19470
+ *
19471
+ * @internal
19472
+ */ const earnOpportunityVariants = [
19473
+ vaultOpportunitySchema
19474
+ ];
19475
+ /** @internal */ const earnOpportunitySchema = z.discriminatedUnion('productType', earnOpportunityVariants);
19476
+ /** Product types this SDK version knows how to parse. */ const knownProductTypes = new Set(earnOpportunityVariants.map((variant)=>variant.shape.productType.value));
19477
+ /**
19478
+ * Tolerant list parser for earn opportunities.
19479
+ *
19480
+ * `z.discriminatedUnion` throws on an unrecognized discriminant and
19481
+ * `z.array` fails the whole array if any element fails. Two migration-window
19482
+ * cases are smoothed over here so neither breaks an already-shipped SDK:
19483
+ *
19484
+ * - A backend that predates `productType` omits it entirely. `'vault'` was the
19485
+ * only opportunity type then, so default a missing discriminant to `'vault'`
19486
+ * rather than dropping every vault the backend returns.
19487
+ * - A future backend adds a *second* `productType` this SDK version does not
19488
+ * know. Drop those elements (a present-but-unrecognized discriminant) instead
19489
+ * of rejecting the whole list.
19490
+ *
19491
+ * Only the drop above is a *tolerant* case. Anything that is not a plain object
19492
+ * with a present-but-unknown string `productType` — `null`, `undefined`,
19493
+ * primitives, or an object whose `productType` is malformed — is passed through
19494
+ * untouched so `z.array(earnOpportunitySchema)` reports it as a normal
19495
+ * validation failure. It is deliberately not silently dropped (which would hide
19496
+ * malformed backend data) and never throws here (an unguarded property read on
19497
+ * a non-object would escape `safeParse` as a raw `TypeError` instead of a
19498
+ * `ZodError`).
19499
+ *
19500
+ * @internal
19501
+ */ const earnOpportunityListSchema = z.preprocess((raw)=>{
19502
+ if (!Array.isArray(raw)) {
19503
+ return raw;
19504
+ }
19505
+ // Array.isArray narrows `raw` to `any[]`; view it as `unknown[]` so the
19506
+ // map/filter chain stays type-safe and no `any` leaks into the return.
19507
+ const entries = raw;
19508
+ return entries.map((entry)=>{
19509
+ // Only touch plain objects; non-objects fall through to fail validation.
19510
+ if (typeof entry !== 'object' || entry === null) {
19511
+ return entry;
19512
+ }
19513
+ const record = entry;
19514
+ // Older backend predating productType: default to the only type then.
19515
+ return record.productType === undefined ? {
19516
+ ...record,
19517
+ productType: 'vault'
19518
+ } : record;
19519
+ }).filter((entry)=>{
19520
+ // Drop ONLY a present-but-unknown string discriminant (a future
19521
+ // productType this SDK version doesn't know). Everything else —
19522
+ // non-objects, a non-string productType — flows through to
19523
+ // z.array(earnOpportunitySchema) and fails/passes validation normally.
19524
+ if (typeof entry !== 'object' || entry === null) {
19525
+ return true;
19526
+ }
19527
+ const productType = entry.productType;
19528
+ if (typeof productType !== 'string') {
19529
+ return true;
19530
+ }
19531
+ return knownProductTypes.has(productType);
19532
+ });
19533
+ }, z.array(earnOpportunitySchema));
18761
19534
  // ---------------------------------------------------------------------------
18762
19535
  // Position response schema
18763
19536
  // ---------------------------------------------------------------------------
@@ -18887,6 +19660,7 @@ const positionPnlSchema = z.discriminatedUnion('status', [
18887
19660
  *
18888
19661
  * @internal
18889
19662
  */ const depositPayloadSchema = z.object({
19663
+ execId: bridgeDepositExecIdSchema,
18890
19664
  executionParams: depositExecutionParamsSchema,
18891
19665
  signature: hexSignatureSchema
18892
19666
  });
@@ -18978,6 +19752,21 @@ const bridgeDepositPrepareReviewSchema = z.object({
18978
19752
  amount: amountJsonSchema,
18979
19753
  vaultAddress: hexAddressSchema
18980
19754
  }).passthrough();
19755
+ /** @internal */ const bridgeQuoteExpirySchema = z.discriminatedUnion('mode', [
19756
+ z.object({
19757
+ mode: z.literal('TIMESTAMP'),
19758
+ expiresAt: z.string().datetime({
19759
+ offset: true
19760
+ })
19761
+ }),
19762
+ z.object({
19763
+ mode: z.literal('BLOCK_NUMBER'),
19764
+ expiresAtBlock: z.number().int(),
19765
+ blockEstimatedAt: z.string().datetime({
19766
+ offset: true
19767
+ }).optional()
19768
+ })
19769
+ ]).optional().catch(undefined);
18981
19770
  /**
18982
19771
  * Zod schema for the bridge deposit prepare payload.
18983
19772
  *
@@ -18989,6 +19778,10 @@ const bridgeDepositPrepareReviewSchema = z.object({
18989
19778
  execId: bridgeDepositExecIdSchema,
18990
19779
  erc3009TypedData: bridgeDepositPreparedBundleSchema,
18991
19780
  expiresAt: z.string().datetime(),
19781
+ quoteIssuedAt: z.string().datetime({
19782
+ offset: true
19783
+ }).optional().catch(undefined),
19784
+ quoteExpiry: bridgeQuoteExpirySchema,
18992
19785
  review: bridgeDepositPrepareReviewSchema
18993
19786
  });
18994
19787
  /**
@@ -19054,6 +19847,7 @@ const bridgeDepositPrepareReviewSchema = z.object({
19054
19847
  *
19055
19848
  * @internal
19056
19849
  */ const withdrawPayloadSchema = z.object({
19850
+ execId: bridgeDepositExecIdSchema,
19057
19851
  executionParams: withdrawExecutionParamsSchema,
19058
19852
  signature: hexSignatureSchema
19059
19853
  });
@@ -19067,6 +19861,27 @@ const bridgeDepositPrepareReviewSchema = z.object({
19067
19861
  data: withdrawPayloadSchema
19068
19862
  });
19069
19863
  // ---------------------------------------------------------------------------
19864
+ // Transaction report response schema
19865
+ // ---------------------------------------------------------------------------
19866
+ /**
19867
+ * Zod schema for the transaction report payload inside the API `data` envelope.
19868
+ *
19869
+ * The Earn Service returns an empty payload (`{"data":{}}`) on success, so the
19870
+ * schema accepts any object shape and does not require specific fields.
19871
+ *
19872
+ * @internal
19873
+ */ const transactionReportPayloadSchema = z.object({}).passthrough();
19874
+ /**
19875
+ * Zod schema for the `POST /v1/earnKit/transactions/report` API response.
19876
+ *
19877
+ * The Earn Service API wraps the transaction report payload in a `data`
19878
+ * envelope.
19879
+ *
19880
+ * @internal
19881
+ */ z.object({
19882
+ data: transactionReportPayloadSchema
19883
+ });
19884
+ // ---------------------------------------------------------------------------
19070
19885
  // Claim rewards response schema
19071
19886
  // ---------------------------------------------------------------------------
19072
19887
  /**
@@ -19114,10 +19929,11 @@ const bridgeDepositPrepareReviewSchema = z.object({
19114
19929
  * Zod schema for a fee entry in an EarnKit API response.
19115
19930
  *
19116
19931
  * Shared across deposit and withdrawal responses (and reusable for real
19117
- * charged fees, not just quote estimates). `type` identifies the fee category
19118
- * for cross-chain deposit quotes this is the kits-proxy fee-quote item type
19119
- * (e.g. `'FORWARD'`, `'PRE_FINALITY'`). `status` qualifies the fee (e.g.
19120
- * `'estimated'` for a pre-sign cross-chain fee). Both are omitted on plain fees.
19932
+ * charged fees, not just quote estimates). `type` identifies the fee category.
19933
+ * For cross-chain deposit quotes this is the kits-proxy fee-quote item type
19934
+ * (e.g. `'FORWARD'`, `'PRE_FINALITY'`). For withdrawal quotes, Circle fees use
19935
+ * `type: 'circle'`. `status` qualifies the fee (e.g. `'estimated'` for a
19936
+ * pre-sign cross-chain fee). Both are omitted on plain fees.
19121
19937
  *
19122
19938
  * @internal
19123
19939
  */ const feeSchema = z.object({
@@ -19126,6 +19942,30 @@ const bridgeDepositPrepareReviewSchema = z.object({
19126
19942
  token: z.string(),
19127
19943
  amount: amountJsonSchema
19128
19944
  });
19945
+ /**
19946
+ * Zod schema for a native gas-fee entry in an EarnKit quote response.
19947
+ *
19948
+ * The Earn Service backend estimates gas server-side and returns one entry per
19949
+ * action (`Approve`, `Deposit`, `Withdraw`). A successful estimate carries
19950
+ * `fees` in the SDK `EstimatedGas` shape (`{ gas, gasPrice, fee }`), each a raw
19951
+ * integer string in the chain's native base units. When the backend cannot
19952
+ * estimate an action it returns `fees: null` with an `error` message instead.
19953
+ *
19954
+ * The schema deliberately validates almost nothing beyond the envelope: `name`
19955
+ * is optional and `fees` is entirely unvalidated (`unknown`). ALL validation
19956
+ * of `fees` — that it is an object at all, and that `gas`, `gasPrice`, and
19957
+ * `fee` are parseable integer strings — is deferred to {@link toQuoteGasFees},
19958
+ * which degrades a malformed entry to a `fees: null` soft failure. This is
19959
+ * intentional: gas is best-effort, so a single unparseable gas entry (a wrong
19960
+ * type such as `fees: 123` or `fees: 'bad'`, a missing field, or a non-numeric
19961
+ * `fee`) must never fail Zod validation and reject the entire quote.
19962
+ *
19963
+ * @internal
19964
+ */ const quoteGasFeeSchema = z.object({
19965
+ name: z.string().optional(),
19966
+ fees: z.unknown(),
19967
+ error: z.string().optional()
19968
+ }).passthrough();
19129
19969
  /**
19130
19970
  * Zod schema for the inner deposit quote payload.
19131
19971
  *
@@ -19141,7 +19981,8 @@ const bridgeDepositPrepareReviewSchema = z.object({
19141
19981
  expectedShares: amountJsonSchema,
19142
19982
  sharePrice: z.string(),
19143
19983
  currentApy: z.number(),
19144
- fees: z.array(feeSchema).optional()
19984
+ fees: z.array(feeSchema).optional(),
19985
+ gasFees: z.array(quoteGasFeeSchema).optional()
19145
19986
  });
19146
19987
  /**
19147
19988
  * Zod schema for the `POST /v1/earnKit/deposit/quote` API response.
@@ -19168,6 +20009,7 @@ const bridgeDepositPrepareReviewSchema = z.object({
19168
20009
  sharePrice: z.string(),
19169
20010
  maxWithdrawable: amountJsonSchema,
19170
20011
  fees: z.array(feeSchema),
20012
+ gasFees: z.array(quoteGasFeeSchema).optional(),
19171
20013
  warnings: z.array(z.string()).optional()
19172
20014
  });
19173
20015
  /**
@@ -19225,7 +20067,7 @@ const bridgeDepositPrepareReviewSchema = z.object({
19225
20067
  *
19226
20068
  * @internal
19227
20069
  */ const getVaultsPayloadSchema = z.object({
19228
- vaults: z.array(vaultInfoResponseSchema),
20070
+ vaults: earnOpportunityListSchema,
19229
20071
  errors: z.array(vaultErrorSchema)
19230
20072
  });
19231
20073
  /**
@@ -19255,7 +20097,7 @@ const bridgeDepositPrepareReviewSchema = z.object({
19255
20097
  *
19256
20098
  * @internal
19257
20099
  */ const exploreVaultsPayloadSchema = z.object({
19258
- vaults: z.array(vaultInfoResponseSchema),
20100
+ vaults: earnOpportunityListSchema,
19259
20101
  pagination: explorePaginationSchema
19260
20102
  });
19261
20103
  /**