@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
@@ -2411,22 +2411,73 @@ let activeRedactor = defaultMessageRedactor;
2411
2411
  '0x9147d463': 'Final balance is less than the initial balance',
2412
2412
  '0xf4b3b1bc': 'Native token transfer to the beneficiary failed'
2413
2413
  };
2414
+ /**
2415
+ * Matches revert data: a 4-byte selector followed by whole ABI-encoded bytes.
2416
+ */ const REVERT_DATA_PATTERN = /^0x[0-9a-f]{8}(?:[0-9a-f]{2})*$/i;
2417
+ /**
2418
+ * Returns the revert data held by one error in a `cause` chain.
2419
+ *
2420
+ * Browser wallets (EIP-1193) wrap the node's error, so the revert data sits
2421
+ * one level down as `data.data`. viem's `getContractError` reads it the same
2422
+ * way, then keeps it on `ContractFunctionRevertedError.raw` and drops the
2423
+ * node's error from the chain.
2424
+ *
2425
+ * @param link - One error in the `cause` chain
2426
+ * @returns The `raw` or `data` string, or undefined if there is none
2427
+ */ function getRevertData(link) {
2428
+ if ('raw' in link && typeof link.raw === 'string' && REVERT_DATA_PATTERN.test(link.raw)) {
2429
+ return link.raw;
2430
+ }
2431
+ if (!('data' in link)) {
2432
+ return undefined;
2433
+ }
2434
+ const { data } = link;
2435
+ if (typeof data === 'string') {
2436
+ return data;
2437
+ }
2438
+ if (data !== null && typeof data === 'object' && 'data' in data && typeof data.data === 'string') {
2439
+ return data.data;
2440
+ }
2441
+ return undefined;
2442
+ }
2443
+ /**
2444
+ * Finds the first revert data on the error or in its `cause` chain that
2445
+ * `accept` takes.
2446
+ *
2447
+ * viem keeps the node's revert data on an inner `RpcRequestError`, not on
2448
+ * the error it throws. The `seen` set stops a self-referencing `cause` chain.
2449
+ *
2450
+ * @param error - The error object
2451
+ * @param accept - Returns true for the data shape the caller wants
2452
+ * @returns The data as written on the error, or undefined if not found
2453
+ */ function findRevertData(error, accept) {
2454
+ const seen = new Set();
2455
+ let current = error;
2456
+ while(current !== null && typeof current === 'object' && !seen.has(current)){
2457
+ seen.add(current);
2458
+ const data = getRevertData(current);
2459
+ if (data !== undefined && accept(data)) {
2460
+ return data;
2461
+ }
2462
+ current = 'cause' in current ? current.cause : undefined;
2463
+ }
2464
+ return undefined;
2465
+ }
2414
2466
  /**
2415
2467
  * Attempts to extract an error selector from an error object or message.
2416
2468
  *
2417
2469
  * Checks multiple sources in priority order:
2418
- * 1. `error.data` field
2470
+ * 1. `raw` or `data` field (or nested `data.data`) on the error or anywhere
2471
+ * in its `cause` chain
2419
2472
  * 2. Message pattern: "custom error 0x8baa579f"
2420
2473
  *
2421
2474
  * @param msg - The error message
2422
2475
  * @param error - The error object (optional)
2423
2476
  * @returns The 4 bytes selector , or undefined if not found
2424
2477
  */ function extractErrorSelector(msg, error) {
2425
- // Check error.data field
2426
- if (error !== null && typeof error === 'object' && 'data' in error) {
2427
- if (typeof error.data === 'string' && /^0x[0-9a-f]{8}$/i.test(error.data)) {
2428
- return error.data.toLowerCase();
2429
- }
2478
+ const data = findRevertData(error, (value)=>/^0x[0-9a-f]{8}$/i.test(value));
2479
+ if (data !== undefined) {
2480
+ return data.toLowerCase();
2430
2481
  }
2431
2482
  // Check for "custom error 0x8baa579f" in the message
2432
2483
  const match = /custom error (0x[0-9a-f]{8})\b/i.exec(msg);
@@ -3651,6 +3702,8 @@ var SwapChain;
3651
3702
  SwapChain["HyperEVM"] = "HyperEVM";
3652
3703
  SwapChain["Monad"] = "Monad";
3653
3704
  SwapChain["Arc"] = "Arc";
3705
+ SwapChain["Cronos"] = "Cronos";
3706
+ SwapChain["Morph"] = "Morph";
3654
3707
  // Testnet chains with swap support
3655
3708
  SwapChain["Arc_Testnet"] = "Arc_Testnet";
3656
3709
  })(SwapChain || (SwapChain = {}));
@@ -4341,14 +4394,14 @@ var BorrowChain;
4341
4394
  symbol: 'USDC',
4342
4395
  // Arc uses native USDC with 18 decimals for gas payments (EVM standard).
4343
4396
  // Note: The ERC-20 USDC contract at usdcAddress uses 6 decimals.
4344
- // See: https://docs.arc.network/arc/references/contract-addresses
4397
+ // See: https://docs.arc.io/arc/references/contract-addresses
4345
4398
  decimals: 18
4346
4399
  },
4347
4400
  chainId: 5042002,
4348
4401
  isTestnet: true,
4349
4402
  explorerUrl: 'https://testnet.arcscan.app/tx/{hash}',
4350
4403
  rpcEndpoints: [
4351
- 'https://rpc.testnet.arc.network/'
4404
+ 'https://rpc.testnet.arc.io'
4352
4405
  ],
4353
4406
  eurcAddress: '0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a',
4354
4407
  usdcAddress: '0x3600000000000000000000000000000000000000',
@@ -4993,7 +5046,8 @@ var BorrowChain;
4993
5046
  }
4994
5047
  },
4995
5048
  kitContracts: {
4996
- bridge: BRIDGE_CONTRACT_EVM_MAINNET
5049
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
5050
+ adapter: ADAPTER_CONTRACT_EVM_MAINNET
4997
5051
  }
4998
5052
  });
4999
5053
 
@@ -5862,7 +5916,8 @@ var BorrowChain;
5862
5916
  }
5863
5917
  },
5864
5918
  kitContracts: {
5865
- bridge: BRIDGE_CONTRACT_EVM_MAINNET
5919
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
5920
+ adapter: ADAPTER_CONTRACT_EVM_MAINNET
5866
5921
  }
5867
5922
  });
5868
5923
 
@@ -8165,8 +8220,8 @@ var Chains = {
8165
8220
  * Zod schema for validating swap-specific chain identifiers.
8166
8221
  *
8167
8222
  * Validates chains based on:
8168
- * - CCTPv2 support (adapter contract deployed)
8169
- * - Mainnet only (no testnets)
8223
+ * - Membership in the SwapChain enum
8224
+ * - Mainnet or explicitly allowlisted testnet
8170
8225
  * - At least one supported token available
8171
8226
  *
8172
8227
  */ zod.z.union([
@@ -10063,11 +10118,13 @@ function parseOrThrow(value, schema, context) {
10063
10118
  [Blockchain.Arbitrum]: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9',
10064
10119
  [Blockchain.Avalanche]: '0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7',
10065
10120
  [Blockchain.Celo]: '0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e',
10121
+ [Blockchain.Cronos]: '0x66e428c3f67a68878562e79A0234c1F83c208770',
10066
10122
  [Blockchain.Ethereum]: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
10067
10123
  [Blockchain.HyperEVM]: '0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb',
10068
10124
  [Blockchain.Ink]: '0x0200C29006150606B650577BBE7B6248F58470c1',
10069
10125
  [Blockchain.Linea]: '0xA219439258ca9da29E9Cc4cE5596924745e12B93',
10070
10126
  [Blockchain.Monad]: '0xe7cd86e13AC4309349F30B3435a9d337750fC82D',
10127
+ [Blockchain.Morph]: '0xe7cd86e13AC4309349F30B3435a9d337750fC82D',
10071
10128
  [Blockchain.NEAR]: 'usdt.tether-token.near',
10072
10129
  [Blockchain.Optimism]: '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58',
10073
10130
  [Blockchain.Polkadot_Asset_Hub]: '1984',
@@ -10126,6 +10183,7 @@ function parseOrThrow(value, schema, context) {
10126
10183
  [Blockchain.Arbitrum]: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1',
10127
10184
  [Blockchain.Avalanche]: '0xd586E7F844cEa2F87f50152665BCbc2C279D8d70',
10128
10185
  [Blockchain.Base]: '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb',
10186
+ [Blockchain.Cronos]: '0xF2001B145b43032AAF5Ee2884e456CCd805F677D',
10129
10187
  [Blockchain.Ethereum]: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
10130
10188
  [Blockchain.Linea]: '0x4AF15ec2A0BD43Db75dd04E62FAA3B8EF36b00d5',
10131
10189
  [Blockchain.Optimism]: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1',
@@ -10188,7 +10246,9 @@ function parseOrThrow(value, schema, context) {
10188
10246
  [Blockchain.Arbitrum]: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
10189
10247
  [Blockchain.Avalanche]: '0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB',
10190
10248
  [Blockchain.Base]: '0x4200000000000000000000000000000000000006',
10249
+ [Blockchain.Cronos]: '0xe44Fd7fCb2b1581822D0c862B68222998a0c299a',
10191
10250
  [Blockchain.Ethereum]: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
10251
+ [Blockchain.Morph]: '0x5300000000000000000000000000000000000011',
10192
10252
  [Blockchain.Optimism]: '0x4200000000000000000000000000000000000006',
10193
10253
  [Blockchain.Polygon]: '0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619',
10194
10254
  [Blockchain.Solana]: '7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs'
@@ -10207,6 +10267,7 @@ function parseOrThrow(value, schema, context) {
10207
10267
  locators: {
10208
10268
  [Blockchain.Ethereum]: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599',
10209
10269
  [Blockchain.Arbitrum]: '0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f',
10270
+ [Blockchain.Cronos]: '0x062E66477Faf219F25D27dCED647BF57C3107d52',
10210
10271
  [Blockchain.Avalanche]: '0x50b7545627a5162F82A992c33b87aDc75187B218'
10211
10272
  }
10212
10273
  };
@@ -11897,7 +11958,7 @@ function resolveOptions(options) {
11897
11958
  }
11898
11959
 
11899
11960
  var name = "@circle-fin/unified-balance-kit";
11900
- var version$1 = "1.8.0";
11961
+ var version$1 = "1.8.2";
11901
11962
  var pkg$1 = {
11902
11963
  name: name,
11903
11964
  version: version$1};
@@ -26490,6 +26551,24 @@ const FAST_TIER_FINALITY_THRESHOLD = 1000;
26490
26551
  }
26491
26552
  };
26492
26553
 
26554
+ /**
26555
+ * Check whether a forwarded burn to the given destination asks Circle's
26556
+ * Forwarding Service to set up the recipient's token account before minting.
26557
+ *
26558
+ * Must return `true` exactly for the destinations where
26559
+ * {@link getForwarderHookData} emits the `createAta` frame, so the forwarding
26560
+ * fee quote includes the setup rent whenever the burn requests it.
26561
+ *
26562
+ * @internal
26563
+ * @param chainType - The destination blockchain type.
26564
+ * @returns `true` for Solana destinations, `false` otherwise.
26565
+ *
26566
+ * @example
26567
+ * ```typescript
26568
+ * requiresRecipientSetup('solana') // true
26569
+ * requiresRecipientSetup('evm') // false
26570
+ * ```
26571
+ */ const requiresRecipientSetup = (chainType)=>chainType === 'solana';
26493
26572
  /**
26494
26573
  * Build the forwarding `hookData` for a forwarded (Orbit-relayed) CCTP v2 burn,
26495
26574
  * tailored to the destination chain.
@@ -26840,10 +26919,12 @@ const FAST_TIER_FINALITY_THRESHOLD = 1000;
26840
26919
  * @param sourceDomain - The source domain
26841
26920
  * @param destinationDomain - The destination domain
26842
26921
  * @param isTestnet - Whether the request is for a testnet chain
26922
+ * @param includeRecipientSetup - Whether the quote includes recipient account setup rent
26843
26923
  * @returns The complete API URL with forward=true query parameter
26844
- */ function buildForwardingFeeUrl(sourceDomain, destinationDomain, isTestnet) {
26924
+ */ function buildForwardingFeeUrl(sourceDomain, destinationDomain, isTestnet, includeRecipientSetup) {
26845
26925
  const baseUrl = isTestnet ? IRIS_API_SANDBOX_BASE_URL : IRIS_API_BASE_URL;
26846
- return `${baseUrl}/v2/burn/USDC/fees/${sourceDomain.toString()}/${destinationDomain.toString()}?forward=true`;
26926
+ const recipientSetupQuery = includeRecipientSetup ? '&includeRecipientSetup=true' : '';
26927
+ return `${baseUrl}/v2/burn/USDC/fees/${sourceDomain.toString()}/${destinationDomain.toString()}?forward=true${recipientSetupQuery}`;
26847
26928
  }
26848
26929
  /**
26849
26930
  * Fetches the forwarding fee for a USDC transfer operation with retry and timeout handling.
@@ -26860,6 +26941,10 @@ const FAST_TIER_FINALITY_THRESHOLD = 1000;
26860
26941
  *
26861
26942
  * The high fee tier is always used to provide buffer for destination gas fluctuations.
26862
26943
  *
26944
+ * Pass `includeRecipientSetup: true` when the burn asks the Forwarding Service to create
26945
+ * the recipient's token account (Solana destinations). The quote then includes the
26946
+ * account rent, which the quote without this flag leaves out.
26947
+ *
26863
26948
  * Implements timeout and retry logic to handle network issues and temporary API failures.
26864
26949
  * Each HTTP request is given a maximum of 2 seconds before it is aborted.
26865
26950
  * We retry up to 10 times, waiting 200ms between attempts.
@@ -26868,6 +26953,8 @@ const FAST_TIER_FINALITY_THRESHOLD = 1000;
26868
26953
  * @param destinationDomain - The destination domain
26869
26954
  * @param isTestnet - Whether the request is for a testnet chain
26870
26955
  * @param finalityThreshold - The finality threshold to select the fee tier (1000 for FAST, 2000 for SLOW)
26956
+ * @param includeRecipientSetup - Whether to include recipient account setup rent in the
26957
+ * quote. Defaults to `false`.
26871
26958
  * @returns The high-tier forwarding fee in USDC minor units (6 decimals)
26872
26959
  * @throws Error if the input domains are invalid, the API request fails, returns invalid data, or network errors occur
26873
26960
  *
@@ -26879,8 +26966,11 @@ const FAST_TIER_FINALITY_THRESHOLD = 1000;
26879
26966
  *
26880
26967
  * // SLOW transfer - use 2000ms finality threshold
26881
26968
  * const slowForwardingFee = await fetchForwardingFee(0, 6, false, 2000)
26969
+ *
26970
+ * // Ethereum -> Solana: include the recipient token account rent
26971
+ * const solanaForwardingFee = await fetchForwardingFee(0, 5, false, 1000, true)
26882
26972
  * ```
26883
- */ async function fetchForwardingFee(sourceDomain, destinationDomain, isTestnet, finalityThreshold) {
26973
+ */ async function fetchForwardingFee(sourceDomain, destinationDomain, isTestnet, finalityThreshold, includeRecipientSetup = false) {
26884
26974
  // Validate source domain
26885
26975
  if (!Number.isInteger(sourceDomain) || sourceDomain < 0) {
26886
26976
  throw new KitError({
@@ -26908,7 +26998,7 @@ const FAST_TIER_FINALITY_THRESHOLD = 1000;
26908
26998
  });
26909
26999
  }
26910
27000
  // Fetch the fee tiers from the API with forward=true
26911
- const url = buildForwardingFeeUrl(sourceDomain, destinationDomain, isTestnet);
27001
+ const url = buildForwardingFeeUrl(sourceDomain, destinationDomain, isTestnet, includeRecipientSetup);
26912
27002
  const response = await pollApiGet(url, isForwardingFeeResponse, DEFAULT_CONFIG$1);
26913
27003
  // Validate that we have at least one fee tier
26914
27004
  if (response.length === 0) {
@@ -29244,7 +29334,7 @@ const mockAttestationMessage = {
29244
29334
  return step;
29245
29335
  }
29246
29336
 
29247
- var version = "1.14.1";
29337
+ var version = "1.14.3";
29248
29338
  var pkg = {
29249
29339
  version: version};
29250
29340
 
@@ -30163,9 +30253,34 @@ function assertCCTPV2Config(config) {
30163
30253
  tokenAddress: source.chain.usdcAddress,
30164
30254
  operationContext: sourceOperationContext
30165
30255
  });
30256
+ await this.assertAmountCoversRecipientSetupFee(params);
30166
30257
  return bridge(params, this);
30167
30258
  }
30168
30259
  /**
30260
+ * Reject a forwarded transfer to Solana whose amount does not exceed the fee.
30261
+ *
30262
+ * CCTP v2 reverts a burn whose `maxFee` is not less than its amount, and the Solana
30263
+ * forwarding fee includes the recipient token account rent. Checking before the
30264
+ * approval avoids sending a transaction that can never be followed by a burn.
30265
+ * Skipped when the caller provides `config.maxFee`.
30266
+ *
30267
+ * @param params - The validated CCTP v2 bridge parameters.
30268
+ * @returns A promise that resolves when the amount covers the fee.
30269
+ * @throws {KitError} INPUT_INVALID_AMOUNT (FATAL) when the fee is greater than or equal
30270
+ * to the amount.
30271
+ */ async assertAmountCoversRecipientSetupFee(params) {
30272
+ const { destination, amount, config } = params;
30273
+ if (destination.useForwarder !== true || config.maxFee !== undefined || !requiresRecipientSetup(destination.chain.type)) {
30274
+ return;
30275
+ }
30276
+ const { providerFee, forwarderFee } = await this.getMaxFee(params);
30277
+ const maxFee = providerFee + forwarderFee;
30278
+ if (maxFee >= BigInt(amount)) {
30279
+ // Amounts and fees are in USDC minor units (6 decimals)
30280
+ 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`);
30281
+ }
30282
+ }
30283
+ /**
30169
30284
  * Retry a failed or incomplete CCTP v2 bridge operation.
30170
30285
  *
30171
30286
  * Analyzes the bridge result to determine where to continue and executes
@@ -30882,6 +30997,9 @@ function assertCCTPV2Config(config) {
30882
30997
  * | FAST | true | No | maxFee = calculatedBurnFee + forwardingFee |
30883
30998
  * | FAST | true | Yes | maxFee = config.maxFee |
30884
30999
  *
31000
+ * For forwarded burns to Solana, the forwarding fee includes the rent for creating the
31001
+ * recipient's token account. A caller-provided `maxFee` must cover it too.
31002
+ *
30885
31003
  * @param params - The bridge parameters object containing:
30886
31004
  * - `source`: The source wallet context with chain definition and adapter
30887
31005
  * - `destination`: The destination wallet context with chain definition and adapter
@@ -30920,6 +31038,7 @@ function assertCCTPV2Config(config) {
30920
31038
  }
30921
31039
  // Get the min finality threshold based on the transfer speed
30922
31040
  const minFinalityThreshold = CCTPv2MinFinalityThreshold[transferSpeed];
31041
+ const includeRecipientSetup = requiresRecipientSetup(destination.chain.type);
30923
31042
  // SLOW transfer without forwarder: no fee
30924
31043
  if (transferSpeed === TransferSpeed.SLOW && !useForwarder) {
30925
31044
  return {
@@ -30929,7 +31048,7 @@ function assertCCTPV2Config(config) {
30929
31048
  }
30930
31049
  // SLOW transfer with forwarder: only forwarding fee
30931
31050
  if (transferSpeed === TransferSpeed.SLOW && useForwarder) {
30932
- const forwarderFee = await fetchForwardingFee(source.chain.cctp.domain, destination.chain.cctp.domain, source.chain.isTestnet, minFinalityThreshold);
31051
+ const forwarderFee = await fetchForwardingFee(source.chain.cctp.domain, destination.chain.cctp.domain, source.chain.isTestnet, minFinalityThreshold, includeRecipientSetup);
30933
31052
  return {
30934
31053
  providerFee: 0n,
30935
31054
  forwarderFee
@@ -30939,7 +31058,7 @@ function assertCCTPV2Config(config) {
30939
31058
  // Fetch fees in parallel when forwarder is enabled
30940
31059
  const [scaledBps, forwarderFee] = await Promise.all([
30941
31060
  fetchUsdcFastBurnFee(source.chain.cctp.domain, destination.chain.cctp.domain, source.chain.isTestnet),
30942
- useForwarder ? fetchForwardingFee(source.chain.cctp.domain, destination.chain.cctp.domain, source.chain.isTestnet, minFinalityThreshold) : Promise.resolve(0n)
31061
+ useForwarder ? fetchForwardingFee(source.chain.cctp.domain, destination.chain.cctp.domain, source.chain.isTestnet, minFinalityThreshold, includeRecipientSetup) : Promise.resolve(0n)
30943
31062
  ]);
30944
31063
  // Calculate fee proportional to the transfer amount
30945
31064
  const amountInMinorUnits = BigInt(amount);