@circle-fin/app-kit 1.16.0 → 1.18.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 (52) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/borrow.cjs +150 -32
  3. package/borrow.d.cts +38 -9
  4. package/borrow.d.mts +38 -9
  5. package/borrow.d.ts +38 -9
  6. package/borrow.mjs +150 -32
  7. package/bridge.cjs +454 -53
  8. package/bridge.d.cts +38 -9
  9. package/bridge.d.mts +38 -9
  10. package/bridge.d.ts +38 -9
  11. package/bridge.mjs +454 -53
  12. package/chains.cjs +10 -6
  13. package/chains.d.cts +1 -1
  14. package/chains.d.mts +1 -1
  15. package/chains.d.ts +1 -1
  16. package/chains.mjs +10 -6
  17. package/context.d.cts +38 -9
  18. package/context.d.mts +38 -9
  19. package/context.d.ts +38 -9
  20. package/earn.cjs +830 -43
  21. package/earn.d.cts +38 -9
  22. package/earn.d.mts +38 -9
  23. package/earn.d.ts +38 -9
  24. package/earn.mjs +830 -43
  25. package/estimateBridge.cjs +454 -53
  26. package/estimateBridge.d.cts +38 -9
  27. package/estimateBridge.d.mts +38 -9
  28. package/estimateBridge.d.ts +38 -9
  29. package/estimateBridge.mjs +454 -53
  30. package/estimateSwap.cjs +265 -45
  31. package/estimateSwap.d.cts +86 -9
  32. package/estimateSwap.d.mts +86 -9
  33. package/estimateSwap.d.ts +86 -9
  34. package/estimateSwap.mjs +265 -45
  35. package/index.cjs +1019 -73
  36. package/index.d.cts +373 -210
  37. package/index.d.mts +373 -210
  38. package/index.d.ts +373 -210
  39. package/index.mjs +1019 -73
  40. package/package.json +8 -8
  41. package/server.cjs +35 -9
  42. package/server.d.cts +29 -10
  43. package/server.d.mts +29 -10
  44. package/server.d.ts +29 -10
  45. package/server.mjs +35 -9
  46. package/swap.cjs +265 -45
  47. package/swap.d.cts +86 -9
  48. package/swap.d.mts +86 -9
  49. package/swap.d.ts +86 -9
  50. package/swap.mjs +265 -45
  51. package/unifiedBalance.cjs +139 -20
  52. package/unifiedBalance.mjs +139 -20
@@ -2404,22 +2404,73 @@ let activeRedactor = defaultMessageRedactor;
2404
2404
  '0x9147d463': 'Final balance is less than the initial balance',
2405
2405
  '0xf4b3b1bc': 'Native token transfer to the beneficiary failed'
2406
2406
  };
2407
+ /**
2408
+ * Matches revert data: a 4-byte selector followed by whole ABI-encoded bytes.
2409
+ */ const REVERT_DATA_PATTERN = /^0x[0-9a-f]{8}(?:[0-9a-f]{2})*$/i;
2410
+ /**
2411
+ * Returns the revert data held by one error in a `cause` chain.
2412
+ *
2413
+ * Browser wallets (EIP-1193) wrap the node's error, so the revert data sits
2414
+ * one level down as `data.data`. viem's `getContractError` reads it the same
2415
+ * way, then keeps it on `ContractFunctionRevertedError.raw` and drops the
2416
+ * node's error from the chain.
2417
+ *
2418
+ * @param link - One error in the `cause` chain
2419
+ * @returns The `raw` or `data` string, or undefined if there is none
2420
+ */ function getRevertData(link) {
2421
+ if ('raw' in link && typeof link.raw === 'string' && REVERT_DATA_PATTERN.test(link.raw)) {
2422
+ return link.raw;
2423
+ }
2424
+ if (!('data' in link)) {
2425
+ return undefined;
2426
+ }
2427
+ const { data } = link;
2428
+ if (typeof data === 'string') {
2429
+ return data;
2430
+ }
2431
+ if (data !== null && typeof data === 'object' && 'data' in data && typeof data.data === 'string') {
2432
+ return data.data;
2433
+ }
2434
+ return undefined;
2435
+ }
2436
+ /**
2437
+ * Finds the first revert data on the error or in its `cause` chain that
2438
+ * `accept` takes.
2439
+ *
2440
+ * viem keeps the node's revert data on an inner `RpcRequestError`, not on
2441
+ * the error it throws. The `seen` set stops a self-referencing `cause` chain.
2442
+ *
2443
+ * @param error - The error object
2444
+ * @param accept - Returns true for the data shape the caller wants
2445
+ * @returns The data as written on the error, or undefined if not found
2446
+ */ function findRevertData(error, accept) {
2447
+ const seen = new Set();
2448
+ let current = error;
2449
+ while(current !== null && typeof current === 'object' && !seen.has(current)){
2450
+ seen.add(current);
2451
+ const data = getRevertData(current);
2452
+ if (data !== undefined && accept(data)) {
2453
+ return data;
2454
+ }
2455
+ current = 'cause' in current ? current.cause : undefined;
2456
+ }
2457
+ return undefined;
2458
+ }
2407
2459
  /**
2408
2460
  * Attempts to extract an error selector from an error object or message.
2409
2461
  *
2410
2462
  * Checks multiple sources in priority order:
2411
- * 1. `error.data` field
2463
+ * 1. `raw` or `data` field (or nested `data.data`) on the error or anywhere
2464
+ * in its `cause` chain
2412
2465
  * 2. Message pattern: "custom error 0x8baa579f"
2413
2466
  *
2414
2467
  * @param msg - The error message
2415
2468
  * @param error - The error object (optional)
2416
2469
  * @returns The 4 bytes selector , or undefined if not found
2417
2470
  */ function extractErrorSelector(msg, error) {
2418
- // Check error.data field
2419
- if (error !== null && typeof error === 'object' && 'data' in error) {
2420
- if (typeof error.data === 'string' && /^0x[0-9a-f]{8}$/i.test(error.data)) {
2421
- return error.data.toLowerCase();
2422
- }
2471
+ const data = findRevertData(error, (value)=>/^0x[0-9a-f]{8}$/i.test(value));
2472
+ if (data !== undefined) {
2473
+ return data.toLowerCase();
2423
2474
  }
2424
2475
  // Check for "custom error 0x8baa579f" in the message
2425
2476
  const match = /custom error (0x[0-9a-f]{8})\b/i.exec(msg);
@@ -3644,6 +3695,8 @@ var SwapChain;
3644
3695
  SwapChain["HyperEVM"] = "HyperEVM";
3645
3696
  SwapChain["Monad"] = "Monad";
3646
3697
  SwapChain["Arc"] = "Arc";
3698
+ SwapChain["Cronos"] = "Cronos";
3699
+ SwapChain["Morph"] = "Morph";
3647
3700
  // Testnet chains with swap support
3648
3701
  SwapChain["Arc_Testnet"] = "Arc_Testnet";
3649
3702
  })(SwapChain || (SwapChain = {}));
@@ -4334,14 +4387,14 @@ var BorrowChain;
4334
4387
  symbol: 'USDC',
4335
4388
  // Arc uses native USDC with 18 decimals for gas payments (EVM standard).
4336
4389
  // Note: The ERC-20 USDC contract at usdcAddress uses 6 decimals.
4337
- // See: https://docs.arc.network/arc/references/contract-addresses
4390
+ // See: https://docs.arc.io/arc/references/contract-addresses
4338
4391
  decimals: 18
4339
4392
  },
4340
4393
  chainId: 5042002,
4341
4394
  isTestnet: true,
4342
4395
  explorerUrl: 'https://testnet.arcscan.app/tx/{hash}',
4343
4396
  rpcEndpoints: [
4344
- 'https://rpc.testnet.arc.network/'
4397
+ 'https://rpc.testnet.arc.io'
4345
4398
  ],
4346
4399
  eurcAddress: '0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a',
4347
4400
  usdcAddress: '0x3600000000000000000000000000000000000000',
@@ -4986,7 +5039,8 @@ var BorrowChain;
4986
5039
  }
4987
5040
  },
4988
5041
  kitContracts: {
4989
- bridge: BRIDGE_CONTRACT_EVM_MAINNET
5042
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
5043
+ adapter: ADAPTER_CONTRACT_EVM_MAINNET
4990
5044
  }
4991
5045
  });
4992
5046
 
@@ -5855,7 +5909,8 @@ var BorrowChain;
5855
5909
  }
5856
5910
  },
5857
5911
  kitContracts: {
5858
- bridge: BRIDGE_CONTRACT_EVM_MAINNET
5912
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
5913
+ adapter: ADAPTER_CONTRACT_EVM_MAINNET
5859
5914
  }
5860
5915
  });
5861
5916
 
@@ -8158,8 +8213,8 @@ var Chains = /*#__PURE__*/Object.freeze({
8158
8213
  * Zod schema for validating swap-specific chain identifiers.
8159
8214
  *
8160
8215
  * Validates chains based on:
8161
- * - CCTPv2 support (adapter contract deployed)
8162
- * - Mainnet only (no testnets)
8216
+ * - Membership in the SwapChain enum
8217
+ * - Mainnet or explicitly allowlisted testnet
8163
8218
  * - At least one supported token available
8164
8219
  *
8165
8220
  */ z.union([
@@ -10056,11 +10111,13 @@ function parseOrThrow(value, schema, context) {
10056
10111
  [Blockchain.Arbitrum]: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9',
10057
10112
  [Blockchain.Avalanche]: '0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7',
10058
10113
  [Blockchain.Celo]: '0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e',
10114
+ [Blockchain.Cronos]: '0x66e428c3f67a68878562e79A0234c1F83c208770',
10059
10115
  [Blockchain.Ethereum]: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
10060
10116
  [Blockchain.HyperEVM]: '0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb',
10061
10117
  [Blockchain.Ink]: '0x0200C29006150606B650577BBE7B6248F58470c1',
10062
10118
  [Blockchain.Linea]: '0xA219439258ca9da29E9Cc4cE5596924745e12B93',
10063
10119
  [Blockchain.Monad]: '0xe7cd86e13AC4309349F30B3435a9d337750fC82D',
10120
+ [Blockchain.Morph]: '0xe7cd86e13AC4309349F30B3435a9d337750fC82D',
10064
10121
  [Blockchain.NEAR]: 'usdt.tether-token.near',
10065
10122
  [Blockchain.Optimism]: '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58',
10066
10123
  [Blockchain.Polkadot_Asset_Hub]: '1984',
@@ -10119,6 +10176,7 @@ function parseOrThrow(value, schema, context) {
10119
10176
  [Blockchain.Arbitrum]: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1',
10120
10177
  [Blockchain.Avalanche]: '0xd586E7F844cEa2F87f50152665BCbc2C279D8d70',
10121
10178
  [Blockchain.Base]: '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb',
10179
+ [Blockchain.Cronos]: '0xF2001B145b43032AAF5Ee2884e456CCd805F677D',
10122
10180
  [Blockchain.Ethereum]: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
10123
10181
  [Blockchain.Linea]: '0x4AF15ec2A0BD43Db75dd04E62FAA3B8EF36b00d5',
10124
10182
  [Blockchain.Optimism]: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1',
@@ -10181,7 +10239,9 @@ function parseOrThrow(value, schema, context) {
10181
10239
  [Blockchain.Arbitrum]: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
10182
10240
  [Blockchain.Avalanche]: '0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB',
10183
10241
  [Blockchain.Base]: '0x4200000000000000000000000000000000000006',
10242
+ [Blockchain.Cronos]: '0xe44Fd7fCb2b1581822D0c862B68222998a0c299a',
10184
10243
  [Blockchain.Ethereum]: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
10244
+ [Blockchain.Morph]: '0x5300000000000000000000000000000000000011',
10185
10245
  [Blockchain.Optimism]: '0x4200000000000000000000000000000000000006',
10186
10246
  [Blockchain.Polygon]: '0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619',
10187
10247
  [Blockchain.Solana]: '7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs'
@@ -10200,6 +10260,7 @@ function parseOrThrow(value, schema, context) {
10200
10260
  locators: {
10201
10261
  [Blockchain.Ethereum]: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599',
10202
10262
  [Blockchain.Arbitrum]: '0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f',
10263
+ [Blockchain.Cronos]: '0x062E66477Faf219F25D27dCED647BF57C3107d52',
10203
10264
  [Blockchain.Avalanche]: '0x50b7545627a5162F82A992c33b87aDc75187B218'
10204
10265
  }
10205
10266
  };
@@ -11890,7 +11951,7 @@ function resolveOptions(options) {
11890
11951
  }
11891
11952
 
11892
11953
  var name = "@circle-fin/unified-balance-kit";
11893
- var version$1 = "1.8.0";
11954
+ var version$1 = "1.8.2";
11894
11955
  var pkg$1 = {
11895
11956
  name: name,
11896
11957
  version: version$1};
@@ -26483,6 +26544,24 @@ const FAST_TIER_FINALITY_THRESHOLD = 1000;
26483
26544
  }
26484
26545
  };
26485
26546
 
26547
+ /**
26548
+ * Check whether a forwarded burn to the given destination asks Circle's
26549
+ * Forwarding Service to set up the recipient's token account before minting.
26550
+ *
26551
+ * Must return `true` exactly for the destinations where
26552
+ * {@link getForwarderHookData} emits the `createAta` frame, so the forwarding
26553
+ * fee quote includes the setup rent whenever the burn requests it.
26554
+ *
26555
+ * @internal
26556
+ * @param chainType - The destination blockchain type.
26557
+ * @returns `true` for Solana destinations, `false` otherwise.
26558
+ *
26559
+ * @example
26560
+ * ```typescript
26561
+ * requiresRecipientSetup('solana') // true
26562
+ * requiresRecipientSetup('evm') // false
26563
+ * ```
26564
+ */ const requiresRecipientSetup = (chainType)=>chainType === 'solana';
26486
26565
  /**
26487
26566
  * Build the forwarding `hookData` for a forwarded (Orbit-relayed) CCTP v2 burn,
26488
26567
  * tailored to the destination chain.
@@ -26833,10 +26912,12 @@ const FAST_TIER_FINALITY_THRESHOLD = 1000;
26833
26912
  * @param sourceDomain - The source domain
26834
26913
  * @param destinationDomain - The destination domain
26835
26914
  * @param isTestnet - Whether the request is for a testnet chain
26915
+ * @param includeRecipientSetup - Whether the quote includes recipient account setup rent
26836
26916
  * @returns The complete API URL with forward=true query parameter
26837
- */ function buildForwardingFeeUrl(sourceDomain, destinationDomain, isTestnet) {
26917
+ */ function buildForwardingFeeUrl(sourceDomain, destinationDomain, isTestnet, includeRecipientSetup) {
26838
26918
  const baseUrl = isTestnet ? IRIS_API_SANDBOX_BASE_URL : IRIS_API_BASE_URL;
26839
- return `${baseUrl}/v2/burn/USDC/fees/${sourceDomain.toString()}/${destinationDomain.toString()}?forward=true`;
26919
+ const recipientSetupQuery = includeRecipientSetup ? '&includeRecipientSetup=true' : '';
26920
+ return `${baseUrl}/v2/burn/USDC/fees/${sourceDomain.toString()}/${destinationDomain.toString()}?forward=true${recipientSetupQuery}`;
26840
26921
  }
26841
26922
  /**
26842
26923
  * Fetches the forwarding fee for a USDC transfer operation with retry and timeout handling.
@@ -26853,6 +26934,10 @@ const FAST_TIER_FINALITY_THRESHOLD = 1000;
26853
26934
  *
26854
26935
  * The high fee tier is always used to provide buffer for destination gas fluctuations.
26855
26936
  *
26937
+ * Pass `includeRecipientSetup: true` when the burn asks the Forwarding Service to create
26938
+ * the recipient's token account (Solana destinations). The quote then includes the
26939
+ * account rent, which the quote without this flag leaves out.
26940
+ *
26856
26941
  * Implements timeout and retry logic to handle network issues and temporary API failures.
26857
26942
  * Each HTTP request is given a maximum of 2 seconds before it is aborted.
26858
26943
  * We retry up to 10 times, waiting 200ms between attempts.
@@ -26861,6 +26946,8 @@ const FAST_TIER_FINALITY_THRESHOLD = 1000;
26861
26946
  * @param destinationDomain - The destination domain
26862
26947
  * @param isTestnet - Whether the request is for a testnet chain
26863
26948
  * @param finalityThreshold - The finality threshold to select the fee tier (1000 for FAST, 2000 for SLOW)
26949
+ * @param includeRecipientSetup - Whether to include recipient account setup rent in the
26950
+ * quote. Defaults to `false`.
26864
26951
  * @returns The high-tier forwarding fee in USDC minor units (6 decimals)
26865
26952
  * @throws Error if the input domains are invalid, the API request fails, returns invalid data, or network errors occur
26866
26953
  *
@@ -26872,8 +26959,11 @@ const FAST_TIER_FINALITY_THRESHOLD = 1000;
26872
26959
  *
26873
26960
  * // SLOW transfer - use 2000ms finality threshold
26874
26961
  * const slowForwardingFee = await fetchForwardingFee(0, 6, false, 2000)
26962
+ *
26963
+ * // Ethereum -> Solana: include the recipient token account rent
26964
+ * const solanaForwardingFee = await fetchForwardingFee(0, 5, false, 1000, true)
26875
26965
  * ```
26876
- */ async function fetchForwardingFee(sourceDomain, destinationDomain, isTestnet, finalityThreshold) {
26966
+ */ async function fetchForwardingFee(sourceDomain, destinationDomain, isTestnet, finalityThreshold, includeRecipientSetup = false) {
26877
26967
  // Validate source domain
26878
26968
  if (!Number.isInteger(sourceDomain) || sourceDomain < 0) {
26879
26969
  throw new KitError({
@@ -26901,7 +26991,7 @@ const FAST_TIER_FINALITY_THRESHOLD = 1000;
26901
26991
  });
26902
26992
  }
26903
26993
  // Fetch the fee tiers from the API with forward=true
26904
- const url = buildForwardingFeeUrl(sourceDomain, destinationDomain, isTestnet);
26994
+ const url = buildForwardingFeeUrl(sourceDomain, destinationDomain, isTestnet, includeRecipientSetup);
26905
26995
  const response = await pollApiGet(url, isForwardingFeeResponse, DEFAULT_CONFIG$1);
26906
26996
  // Validate that we have at least one fee tier
26907
26997
  if (response.length === 0) {
@@ -29237,7 +29327,7 @@ const mockAttestationMessage = {
29237
29327
  return step;
29238
29328
  }
29239
29329
 
29240
- var version = "1.14.1";
29330
+ var version = "1.14.3";
29241
29331
  var pkg = {
29242
29332
  version: version};
29243
29333
 
@@ -30156,9 +30246,34 @@ function assertCCTPV2Config(config) {
30156
30246
  tokenAddress: source.chain.usdcAddress,
30157
30247
  operationContext: sourceOperationContext
30158
30248
  });
30249
+ await this.assertAmountCoversRecipientSetupFee(params);
30159
30250
  return bridge(params, this);
30160
30251
  }
30161
30252
  /**
30253
+ * Reject a forwarded transfer to Solana whose amount does not exceed the fee.
30254
+ *
30255
+ * CCTP v2 reverts a burn whose `maxFee` is not less than its amount, and the Solana
30256
+ * forwarding fee includes the recipient token account rent. Checking before the
30257
+ * approval avoids sending a transaction that can never be followed by a burn.
30258
+ * Skipped when the caller provides `config.maxFee`.
30259
+ *
30260
+ * @param params - The validated CCTP v2 bridge parameters.
30261
+ * @returns A promise that resolves when the amount covers the fee.
30262
+ * @throws {KitError} INPUT_INVALID_AMOUNT (FATAL) when the fee is greater than or equal
30263
+ * to the amount.
30264
+ */ async assertAmountCoversRecipientSetupFee(params) {
30265
+ const { destination, amount, config } = params;
30266
+ if (destination.useForwarder !== true || config.maxFee !== undefined || !requiresRecipientSetup(destination.chain.type)) {
30267
+ return;
30268
+ }
30269
+ const { providerFee, forwarderFee } = await this.getMaxFee(params);
30270
+ const maxFee = providerFee + forwarderFee;
30271
+ if (maxFee >= BigInt(amount)) {
30272
+ // Amounts and fees are in USDC minor units (6 decimals)
30273
+ throw createInvalidAmountError(formatUnits(amount, 6), `Amount must be greater than the ${formatUnits(String(maxFee), 6)} USDC fee for forwarded transfers to ${destination.chain.name}, which includes creating the recipient's token account`);
30274
+ }
30275
+ }
30276
+ /**
30162
30277
  * Retry a failed or incomplete CCTP v2 bridge operation.
30163
30278
  *
30164
30279
  * Analyzes the bridge result to determine where to continue and executes
@@ -30875,6 +30990,9 @@ function assertCCTPV2Config(config) {
30875
30990
  * | FAST | true | No | maxFee = calculatedBurnFee + forwardingFee |
30876
30991
  * | FAST | true | Yes | maxFee = config.maxFee |
30877
30992
  *
30993
+ * For forwarded burns to Solana, the forwarding fee includes the rent for creating the
30994
+ * recipient's token account. A caller-provided `maxFee` must cover it too.
30995
+ *
30878
30996
  * @param params - The bridge parameters object containing:
30879
30997
  * - `source`: The source wallet context with chain definition and adapter
30880
30998
  * - `destination`: The destination wallet context with chain definition and adapter
@@ -30913,6 +31031,7 @@ function assertCCTPV2Config(config) {
30913
31031
  }
30914
31032
  // Get the min finality threshold based on the transfer speed
30915
31033
  const minFinalityThreshold = CCTPv2MinFinalityThreshold[transferSpeed];
31034
+ const includeRecipientSetup = requiresRecipientSetup(destination.chain.type);
30916
31035
  // SLOW transfer without forwarder: no fee
30917
31036
  if (transferSpeed === TransferSpeed.SLOW && !useForwarder) {
30918
31037
  return {
@@ -30922,7 +31041,7 @@ function assertCCTPV2Config(config) {
30922
31041
  }
30923
31042
  // SLOW transfer with forwarder: only forwarding fee
30924
31043
  if (transferSpeed === TransferSpeed.SLOW && useForwarder) {
30925
- const forwarderFee = await fetchForwardingFee(source.chain.cctp.domain, destination.chain.cctp.domain, source.chain.isTestnet, minFinalityThreshold);
31044
+ const forwarderFee = await fetchForwardingFee(source.chain.cctp.domain, destination.chain.cctp.domain, source.chain.isTestnet, minFinalityThreshold, includeRecipientSetup);
30926
31045
  return {
30927
31046
  providerFee: 0n,
30928
31047
  forwarderFee
@@ -30932,7 +31051,7 @@ function assertCCTPV2Config(config) {
30932
31051
  // Fetch fees in parallel when forwarder is enabled
30933
31052
  const [scaledBps, forwarderFee] = await Promise.all([
30934
31053
  fetchUsdcFastBurnFee(source.chain.cctp.domain, destination.chain.cctp.domain, source.chain.isTestnet),
30935
- useForwarder ? fetchForwardingFee(source.chain.cctp.domain, destination.chain.cctp.domain, source.chain.isTestnet, minFinalityThreshold) : Promise.resolve(0n)
31054
+ useForwarder ? fetchForwardingFee(source.chain.cctp.domain, destination.chain.cctp.domain, source.chain.isTestnet, minFinalityThreshold, includeRecipientSetup) : Promise.resolve(0n)
30936
31055
  ]);
30937
31056
  // Calculate fee proportional to the transfer amount
30938
31057
  const amountInMinorUnits = BigInt(amount);