@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
package/bridge.mjs CHANGED
@@ -2515,22 +2515,73 @@ let activeRedactor = defaultMessageRedactor;
2515
2515
  '0x9147d463': 'Final balance is less than the initial balance',
2516
2516
  '0xf4b3b1bc': 'Native token transfer to the beneficiary failed'
2517
2517
  };
2518
+ /**
2519
+ * Matches revert data: a 4-byte selector followed by whole ABI-encoded bytes.
2520
+ */ const REVERT_DATA_PATTERN = /^0x[0-9a-f]{8}(?:[0-9a-f]{2})*$/i;
2521
+ /**
2522
+ * Returns the revert data held by one error in a `cause` chain.
2523
+ *
2524
+ * Browser wallets (EIP-1193) wrap the node's error, so the revert data sits
2525
+ * one level down as `data.data`. viem's `getContractError` reads it the same
2526
+ * way, then keeps it on `ContractFunctionRevertedError.raw` and drops the
2527
+ * node's error from the chain.
2528
+ *
2529
+ * @param link - One error in the `cause` chain
2530
+ * @returns The `raw` or `data` string, or undefined if there is none
2531
+ */ function getRevertData(link) {
2532
+ if ('raw' in link && typeof link.raw === 'string' && REVERT_DATA_PATTERN.test(link.raw)) {
2533
+ return link.raw;
2534
+ }
2535
+ if (!('data' in link)) {
2536
+ return undefined;
2537
+ }
2538
+ const { data } = link;
2539
+ if (typeof data === 'string') {
2540
+ return data;
2541
+ }
2542
+ if (data !== null && typeof data === 'object' && 'data' in data && typeof data.data === 'string') {
2543
+ return data.data;
2544
+ }
2545
+ return undefined;
2546
+ }
2547
+ /**
2548
+ * Finds the first revert data on the error or in its `cause` chain that
2549
+ * `accept` takes.
2550
+ *
2551
+ * viem keeps the node's revert data on an inner `RpcRequestError`, not on
2552
+ * the error it throws. The `seen` set stops a self-referencing `cause` chain.
2553
+ *
2554
+ * @param error - The error object
2555
+ * @param accept - Returns true for the data shape the caller wants
2556
+ * @returns The data as written on the error, or undefined if not found
2557
+ */ function findRevertData(error, accept) {
2558
+ const seen = new Set();
2559
+ let current = error;
2560
+ while(current !== null && typeof current === 'object' && !seen.has(current)){
2561
+ seen.add(current);
2562
+ const data = getRevertData(current);
2563
+ if (data !== undefined && accept(data)) {
2564
+ return data;
2565
+ }
2566
+ current = 'cause' in current ? current.cause : undefined;
2567
+ }
2568
+ return undefined;
2569
+ }
2518
2570
  /**
2519
2571
  * Attempts to extract an error selector from an error object or message.
2520
2572
  *
2521
2573
  * Checks multiple sources in priority order:
2522
- * 1. `error.data` field
2574
+ * 1. `raw` or `data` field (or nested `data.data`) on the error or anywhere
2575
+ * in its `cause` chain
2523
2576
  * 2. Message pattern: "custom error 0x8baa579f"
2524
2577
  *
2525
2578
  * @param msg - The error message
2526
2579
  * @param error - The error object (optional)
2527
2580
  * @returns The 4 bytes selector , or undefined if not found
2528
2581
  */ function extractErrorSelector(msg, error) {
2529
- // Check error.data field
2530
- if (error !== null && typeof error === 'object' && 'data' in error) {
2531
- if (typeof error.data === 'string' && /^0x[0-9a-f]{8}$/i.test(error.data)) {
2532
- return error.data.toLowerCase();
2533
- }
2582
+ const data = findRevertData(error, (value)=>/^0x[0-9a-f]{8}$/i.test(value));
2583
+ if (data !== undefined) {
2584
+ return data.toLowerCase();
2534
2585
  }
2535
2586
  // Check for "custom error 0x8baa579f" in the message
2536
2587
  const match = /custom error (0x[0-9a-f]{8})\b/i.exec(msg);
@@ -3803,6 +3854,8 @@ var SwapChain;
3803
3854
  SwapChain["HyperEVM"] = "HyperEVM";
3804
3855
  SwapChain["Monad"] = "Monad";
3805
3856
  SwapChain["Arc"] = "Arc";
3857
+ SwapChain["Cronos"] = "Cronos";
3858
+ SwapChain["Morph"] = "Morph";
3806
3859
  // Testnet chains with swap support
3807
3860
  SwapChain["Arc_Testnet"] = "Arc_Testnet";
3808
3861
  })(SwapChain || (SwapChain = {}));
@@ -4483,14 +4536,14 @@ var BorrowChain;
4483
4536
  symbol: 'USDC',
4484
4537
  // Arc uses native USDC with 18 decimals for gas payments (EVM standard).
4485
4538
  // Note: The ERC-20 USDC contract at usdcAddress uses 6 decimals.
4486
- // See: https://docs.arc.network/arc/references/contract-addresses
4539
+ // See: https://docs.arc.io/arc/references/contract-addresses
4487
4540
  decimals: 18
4488
4541
  },
4489
4542
  chainId: 5042002,
4490
4543
  isTestnet: true,
4491
4544
  explorerUrl: 'https://testnet.arcscan.app/tx/{hash}',
4492
4545
  rpcEndpoints: [
4493
- 'https://rpc.testnet.arc.network/'
4546
+ 'https://rpc.testnet.arc.io'
4494
4547
  ],
4495
4548
  eurcAddress: '0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a',
4496
4549
  usdcAddress: '0x3600000000000000000000000000000000000000',
@@ -5135,7 +5188,8 @@ var BorrowChain;
5135
5188
  }
5136
5189
  },
5137
5190
  kitContracts: {
5138
- bridge: BRIDGE_CONTRACT_EVM_MAINNET
5191
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
5192
+ adapter: ADAPTER_CONTRACT_EVM_MAINNET
5139
5193
  }
5140
5194
  });
5141
5195
 
@@ -6004,7 +6058,8 @@ var BorrowChain;
6004
6058
  }
6005
6059
  },
6006
6060
  kitContracts: {
6007
- bridge: BRIDGE_CONTRACT_EVM_MAINNET
6061
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
6062
+ adapter: ADAPTER_CONTRACT_EVM_MAINNET
6008
6063
  }
6009
6064
  });
6010
6065
 
@@ -8302,8 +8357,8 @@ var Chains = /*#__PURE__*/Object.freeze({
8302
8357
  * Zod schema for validating swap-specific chain identifiers.
8303
8358
  *
8304
8359
  * Validates chains based on:
8305
- * - CCTPv2 support (adapter contract deployed)
8306
- * - Mainnet only (no testnets)
8360
+ * - Membership in the SwapChain enum
8361
+ * - Mainnet or explicitly allowlisted testnet
8307
8362
  * - At least one supported token available
8308
8363
  *
8309
8364
  */ const swapChainIdentifierSchema = z.union([
@@ -8782,7 +8837,7 @@ const swapTokenEnumSchema = z.enum([
8782
8837
  *
8783
8838
  * A chain supports swaps if ALL conditions are met:
8784
8839
  * 1. Is a member of the {@link SwapChain} enum
8785
- * 2. Has CCTPv2 support (adapter contract deployed)
8840
+ * 2. Has an Adapter contract configured directly or through CCTP v2
8786
8841
  *
8787
8842
  * Testnets are allowed only when explicitly listed in the
8788
8843
  * {@link SwapChain} enum (e.g., Arc_Testnet).
@@ -8803,14 +8858,14 @@ const swapTokenEnumSchema = z.enum([
8803
8858
  * @remarks
8804
8859
  * Chains automatically become swap-enabled when:
8805
8860
  * - Added to the {@link SwapChain} enum
8806
- * - Adapter contract is deployed (CCTPv2)
8861
+ * - An Adapter contract is deployed and configured
8807
8862
  *
8808
8863
  * No code changes needed when new chains are added!
8809
8864
  */ function isSwapSupportedChain(chain) {
8810
8865
  if (!(chain.chain in SwapChain)) {
8811
8866
  return false;
8812
8867
  }
8813
- if (!isCCTPV2Supported(chain)) {
8868
+ if (!isCCTPV2Supported(chain) && !hasCustomContractSupport(chain, 'adapter')) {
8814
8869
  return false;
8815
8870
  }
8816
8871
  return true;
@@ -8821,7 +8876,7 @@ const swapTokenEnumSchema = z.enum([
8821
8876
  *
8822
8877
  * Dynamically filter chain definitions to include only those that:
8823
8878
  * - Are members of the {@link SwapChain} enum
8824
- * - Have CCTPv2 support
8879
+ * - Have an Adapter contract configured directly or through CCTP v2
8825
8880
  *
8826
8881
  * This function is pure and accepts chains as parameter to avoid
8827
8882
  * circular dependencies.
@@ -10255,11 +10310,13 @@ function parseOrThrow(value, schema, context) {
10255
10310
  [Blockchain.Arbitrum]: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9',
10256
10311
  [Blockchain.Avalanche]: '0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7',
10257
10312
  [Blockchain.Celo]: '0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e',
10313
+ [Blockchain.Cronos]: '0x66e428c3f67a68878562e79A0234c1F83c208770',
10258
10314
  [Blockchain.Ethereum]: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
10259
10315
  [Blockchain.HyperEVM]: '0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb',
10260
10316
  [Blockchain.Ink]: '0x0200C29006150606B650577BBE7B6248F58470c1',
10261
10317
  [Blockchain.Linea]: '0xA219439258ca9da29E9Cc4cE5596924745e12B93',
10262
10318
  [Blockchain.Monad]: '0xe7cd86e13AC4309349F30B3435a9d337750fC82D',
10319
+ [Blockchain.Morph]: '0xe7cd86e13AC4309349F30B3435a9d337750fC82D',
10263
10320
  [Blockchain.NEAR]: 'usdt.tether-token.near',
10264
10321
  [Blockchain.Optimism]: '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58',
10265
10322
  [Blockchain.Polkadot_Asset_Hub]: '1984',
@@ -10318,6 +10375,7 @@ function parseOrThrow(value, schema, context) {
10318
10375
  [Blockchain.Arbitrum]: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1',
10319
10376
  [Blockchain.Avalanche]: '0xd586E7F844cEa2F87f50152665BCbc2C279D8d70',
10320
10377
  [Blockchain.Base]: '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb',
10378
+ [Blockchain.Cronos]: '0xF2001B145b43032AAF5Ee2884e456CCd805F677D',
10321
10379
  [Blockchain.Ethereum]: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
10322
10380
  [Blockchain.Linea]: '0x4AF15ec2A0BD43Db75dd04E62FAA3B8EF36b00d5',
10323
10381
  [Blockchain.Optimism]: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1',
@@ -10380,7 +10438,9 @@ function parseOrThrow(value, schema, context) {
10380
10438
  [Blockchain.Arbitrum]: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
10381
10439
  [Blockchain.Avalanche]: '0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB',
10382
10440
  [Blockchain.Base]: '0x4200000000000000000000000000000000000006',
10441
+ [Blockchain.Cronos]: '0xe44Fd7fCb2b1581822D0c862B68222998a0c299a',
10383
10442
  [Blockchain.Ethereum]: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
10443
+ [Blockchain.Morph]: '0x5300000000000000000000000000000000000011',
10384
10444
  [Blockchain.Optimism]: '0x4200000000000000000000000000000000000006',
10385
10445
  [Blockchain.Polygon]: '0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619',
10386
10446
  [Blockchain.Solana]: '7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs'
@@ -10399,6 +10459,7 @@ function parseOrThrow(value, schema, context) {
10399
10459
  locators: {
10400
10460
  [Blockchain.Ethereum]: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599',
10401
10461
  [Blockchain.Arbitrum]: '0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f',
10462
+ [Blockchain.Cronos]: '0x062E66477Faf219F25D27dCED647BF57C3107d52',
10402
10463
  [Blockchain.Avalanche]: '0x50b7545627a5162F82A992c33b87aDc75187B218'
10403
10464
  }
10404
10465
  };
@@ -12119,6 +12180,39 @@ function resolveOptions(options) {
12119
12180
  }
12120
12181
  };
12121
12182
  }
12183
+ /**
12184
+ * Emit telemetry for a completed operation without affecting its caller.
12185
+ *
12186
+ * No-ops when `config.disabled` is `true`. Like {@link withErrorTelemetry},
12187
+ * failures while constructing or submitting the telemetry payload are reported
12188
+ * as a soft warning and never change a completed operation's result.
12189
+ *
12190
+ * @param eventType - The telemetry event type for the completed operation.
12191
+ * @param config - Per-kit SDK identity and disabled flag.
12192
+ * @param context - Optional chain, token, and transaction context.
12193
+ * @returns Nothing.
12194
+ * @throws Never — telemetry failures are reported as warnings.
12195
+ *
12196
+ * @example
12197
+ * ```typescript
12198
+ * import { emitSuccessTelemetry } from '@core/utils'
12199
+ *
12200
+ * emitSuccessTelemetry(
12201
+ * 'bridge_bridge',
12202
+ * { sdkName: 'bridge-kit', sdkVersion: '1.0.0', disabled: false },
12203
+ * { sourceChain: 'Ethereum', destinationChain: 'Base', tokenIn: 'USDC' },
12204
+ * )
12205
+ * ```
12206
+ */ function emitSuccessTelemetry(eventType, config, context) {
12207
+ if (config.disabled) {
12208
+ return;
12209
+ }
12210
+ try {
12211
+ void emitAnalyticsLog(buildPayload(config, eventType, undefined, context));
12212
+ } catch (telemetryError) {
12213
+ warnTelemetryDrop(eventType, telemetryError);
12214
+ }
12215
+ }
12122
12216
  /**
12123
12217
  * Wrap an async operation with error telemetry.
12124
12218
  *
@@ -12229,7 +12323,7 @@ function resolveOptions(options) {
12229
12323
  }
12230
12324
 
12231
12325
  var name$3 = "@circle-fin/bridge-kit";
12232
- var version$4 = "1.15.2";
12326
+ var version$4 = "1.16.0";
12233
12327
  var pkg$4 = {
12234
12328
  name: name$3,
12235
12329
  version: version$4};
@@ -13490,6 +13584,53 @@ const EURC_MAINNET_TOKEN_ID = '0x6ca9e29fa53becc29becaf4a90b9ca7a995ad4d2234880d
13490
13584
  }
13491
13585
  };
13492
13586
 
13587
+ /**
13588
+ * Map a CCTPx bytes32 token id back to its known display symbol.
13589
+ *
13590
+ * The inverse of the provider's internal symbol -> id resolution: a bridge
13591
+ * resolves a symbol such as `'cirBTC'` to its canonical bytes32 id and reports
13592
+ * that id on `result.token`, dropping the symbol. This helper recovers the
13593
+ * symbol for display. It is scoped to {@link KNOWN_TOKEN_SYMBOLS} — the same
13594
+ * known set the forward resolution covers — and is keyed by network, because a
13595
+ * symbol names a separate registration on each network, whose ids are
13596
+ * unrelated.
13597
+ *
13598
+ * The lookup is best-effort by design: an arbitrary registry token, a
13599
+ * non-canonical bridge of a known symbol, or any unrecognized id has no known
13600
+ * symbol and yields `undefined`. The id is matched case-insensitively (bytes32
13601
+ * ids have no checksum casing).
13602
+ *
13603
+ * @param id - The bytes32 token id to decode (e.g. a `result.token` value).
13604
+ * @param network - The network the id belongs to (`'testnet'` or `'mainnet'`);
13605
+ * derive it from the source chain, e.g. `chain.isTestnet ? 'testnet' :
13606
+ * 'mainnet'`.
13607
+ * @returns The matching {@link KnownTokenSymbol}, or `undefined` when `id` is
13608
+ * not one of the known canonical ids for `network`.
13609
+ * @throws A `KitError` (`INPUT_VALIDATION_FAILED`, FATAL) when `id` is not a
13610
+ * string or `network` is not a recognized network.
13611
+ *
13612
+ * @example
13613
+ * ```typescript
13614
+ * import { resolveToKnownTokenSymbol } from '@circle-fin/provider-cctpx'
13615
+ *
13616
+ * const result = await provider.bridge(params) // params.token === 'cirBTC'
13617
+ * const network = result.source.chain.isTestnet ? 'testnet' : 'mainnet'
13618
+ *
13619
+ * resolveToKnownTokenSymbol(result.token, network) // 'cirBTC'
13620
+ * resolveToKnownTokenSymbol('0xdeadbeef…', network) // undefined (not known)
13621
+ * ```
13622
+ */ function resolveToKnownTokenSymbol(id, network) {
13623
+ if (typeof id !== 'string') {
13624
+ throw createValidationFailedError$1('id', id, 'must be a string');
13625
+ }
13626
+ if (network !== 'testnet' && network !== 'mainnet') {
13627
+ throw createValidationFailedError$1('network', network, "must be 'testnet' or 'mainnet'");
13628
+ }
13629
+ const row = KNOWN_TOKEN_IDS_BY_NETWORK[network];
13630
+ const target = id.toLowerCase();
13631
+ return KNOWN_TOKEN_SYMBOLS.find((symbol)=>row[symbol].toLowerCase() === target);
13632
+ }
13633
+
13493
13634
  /**
13494
13635
  * Default clock implementation.
13495
13636
  *
@@ -14794,7 +14935,11 @@ function createNoopLogger() {
14794
14935
  tokenId: bytes32Hex,
14795
14936
  domainId: nonNegativeInteger,
14796
14937
  tokenAddress: z.string(),
14797
- tokenManagerAddress: z.string()
14938
+ tokenManagerAddress: z.string(),
14939
+ // Operator-controlled per-chain kill switch (CCTPE-3979). Optional for
14940
+ // back-compat with older IRIS versions that do not surface it. Downstream
14941
+ // consumers in `findCCTPXDeployment` treat a missing value as `true`.
14942
+ enabled: z.boolean().optional()
14798
14943
  });
14799
14944
  /**
14800
14945
  * Schema for one token entry in the token registry response.
@@ -15278,17 +15423,23 @@ function createNoopLogger() {
15278
15423
  /**
15279
15424
  * Find a token's deployment on the given CCTP domain.
15280
15425
  *
15426
+ * A deployment row with `enabled === false` is treated as missing: an IRIS
15427
+ * operator has flipped the per-chain kill switch (CCTPE-3979), so the SDK must
15428
+ * not route through it even though the row still exists in the registry. A
15429
+ * missing or `true` `enabled` value remains routable — the field is optional on
15430
+ * older IRIS versions and absence is read as "allowed".
15431
+ *
15281
15432
  * @param token - The registry token whose deployments are searched.
15282
15433
  * @param domain - The CCTP domain id to match.
15283
15434
  * @returns The matching {@link CCTPXTokenDeployment}, or `undefined` when the
15284
- * token has no deployment on that domain.
15435
+ * token has no deployment on that domain or the deployment is disabled.
15285
15436
  *
15286
15437
  * @example
15287
15438
  * ```typescript
15288
15439
  * const deployment = findCCTPXDeployment(token, 0)
15289
15440
  * ```
15290
15441
  */ function findCCTPXDeployment(token, domain) {
15291
- return token.deployments.find((deployment)=>deployment.domainId === domain);
15442
+ return token.deployments.find((deployment)=>deployment.domainId === domain && deployment.enabled !== false);
15292
15443
  }
15293
15444
 
15294
15445
  /**
@@ -15390,6 +15541,24 @@ function createNoopLogger() {
15390
15541
  }
15391
15542
  }
15392
15543
  })
15544
+ ],
15545
+ // The token is not deployed on one of the route chains, or an operator has
15546
+ // flipped the per-chain `enabled` kill switch on the IRIS deployment row.
15547
+ // Either way the route is permanently unavailable until the operator intervenes;
15548
+ // a retry at the same source/destination will keep returning this error.
15549
+ [
15550
+ 'TOKEN_NOT_SUPPORTED_ON_ROUTE',
15551
+ ({ sourceDomain, destinationDomain })=>new KitError({
15552
+ ...InputError.UNSUPPORTED_ROUTE,
15553
+ recoverability: 'FATAL',
15554
+ message: 'CCTPx fee quote rejected the route: the token is not supported on ' + 'the source or destination chain (missing deployment or operator-disabled).',
15555
+ cause: {
15556
+ trace: {
15557
+ sourceDomain,
15558
+ destinationDomain
15559
+ }
15560
+ }
15561
+ })
15393
15562
  ]
15394
15563
  ]);
15395
15564
  /**
@@ -20646,6 +20815,24 @@ const FAST_TIER_FINALITY_THRESHOLD = 1000;
20646
20815
  }
20647
20816
  };
20648
20817
 
20818
+ /**
20819
+ * Check whether a forwarded burn to the given destination asks Circle's
20820
+ * Forwarding Service to set up the recipient's token account before minting.
20821
+ *
20822
+ * Must return `true` exactly for the destinations where
20823
+ * {@link getForwarderHookData} emits the `createAta` frame, so the forwarding
20824
+ * fee quote includes the setup rent whenever the burn requests it.
20825
+ *
20826
+ * @internal
20827
+ * @param chainType - The destination blockchain type.
20828
+ * @returns `true` for Solana destinations, `false` otherwise.
20829
+ *
20830
+ * @example
20831
+ * ```typescript
20832
+ * requiresRecipientSetup('solana') // true
20833
+ * requiresRecipientSetup('evm') // false
20834
+ * ```
20835
+ */ const requiresRecipientSetup = (chainType)=>chainType === 'solana';
20649
20836
  /**
20650
20837
  * Build the forwarding `hookData` for a forwarded (Orbit-relayed) CCTP v2 burn,
20651
20838
  * tailored to the destination chain.
@@ -20996,10 +21183,12 @@ const FAST_TIER_FINALITY_THRESHOLD = 1000;
20996
21183
  * @param sourceDomain - The source domain
20997
21184
  * @param destinationDomain - The destination domain
20998
21185
  * @param isTestnet - Whether the request is for a testnet chain
21186
+ * @param includeRecipientSetup - Whether the quote includes recipient account setup rent
20999
21187
  * @returns The complete API URL with forward=true query parameter
21000
- */ function buildForwardingFeeUrl(sourceDomain, destinationDomain, isTestnet) {
21188
+ */ function buildForwardingFeeUrl(sourceDomain, destinationDomain, isTestnet, includeRecipientSetup) {
21001
21189
  const baseUrl = isTestnet ? IRIS_API_SANDBOX_BASE_URL$1 : IRIS_API_BASE_URL$1;
21002
- return `${baseUrl}/v2/burn/USDC/fees/${sourceDomain.toString()}/${destinationDomain.toString()}?forward=true`;
21190
+ const recipientSetupQuery = includeRecipientSetup ? '&includeRecipientSetup=true' : '';
21191
+ return `${baseUrl}/v2/burn/USDC/fees/${sourceDomain.toString()}/${destinationDomain.toString()}?forward=true${recipientSetupQuery}`;
21003
21192
  }
21004
21193
  /**
21005
21194
  * Fetches the forwarding fee for a USDC transfer operation with retry and timeout handling.
@@ -21016,6 +21205,10 @@ const FAST_TIER_FINALITY_THRESHOLD = 1000;
21016
21205
  *
21017
21206
  * The high fee tier is always used to provide buffer for destination gas fluctuations.
21018
21207
  *
21208
+ * Pass `includeRecipientSetup: true` when the burn asks the Forwarding Service to create
21209
+ * the recipient's token account (Solana destinations). The quote then includes the
21210
+ * account rent, which the quote without this flag leaves out.
21211
+ *
21019
21212
  * Implements timeout and retry logic to handle network issues and temporary API failures.
21020
21213
  * Each HTTP request is given a maximum of 2 seconds before it is aborted.
21021
21214
  * We retry up to 10 times, waiting 200ms between attempts.
@@ -21024,6 +21217,8 @@ const FAST_TIER_FINALITY_THRESHOLD = 1000;
21024
21217
  * @param destinationDomain - The destination domain
21025
21218
  * @param isTestnet - Whether the request is for a testnet chain
21026
21219
  * @param finalityThreshold - The finality threshold to select the fee tier (1000 for FAST, 2000 for SLOW)
21220
+ * @param includeRecipientSetup - Whether to include recipient account setup rent in the
21221
+ * quote. Defaults to `false`.
21027
21222
  * @returns The high-tier forwarding fee in USDC minor units (6 decimals)
21028
21223
  * @throws Error if the input domains are invalid, the API request fails, returns invalid data, or network errors occur
21029
21224
  *
@@ -21035,8 +21230,11 @@ const FAST_TIER_FINALITY_THRESHOLD = 1000;
21035
21230
  *
21036
21231
  * // SLOW transfer - use 2000ms finality threshold
21037
21232
  * const slowForwardingFee = await fetchForwardingFee(0, 6, false, 2000)
21233
+ *
21234
+ * // Ethereum -> Solana: include the recipient token account rent
21235
+ * const solanaForwardingFee = await fetchForwardingFee(0, 5, false, 1000, true)
21038
21236
  * ```
21039
- */ async function fetchForwardingFee(sourceDomain, destinationDomain, isTestnet, finalityThreshold) {
21237
+ */ async function fetchForwardingFee(sourceDomain, destinationDomain, isTestnet, finalityThreshold, includeRecipientSetup = false) {
21040
21238
  // Validate source domain
21041
21239
  if (!Number.isInteger(sourceDomain) || sourceDomain < 0) {
21042
21240
  throw new KitError({
@@ -21064,7 +21262,7 @@ const FAST_TIER_FINALITY_THRESHOLD = 1000;
21064
21262
  });
21065
21263
  }
21066
21264
  // Fetch the fee tiers from the API with forward=true
21067
- const url = buildForwardingFeeUrl(sourceDomain, destinationDomain, isTestnet);
21265
+ const url = buildForwardingFeeUrl(sourceDomain, destinationDomain, isTestnet, includeRecipientSetup);
21068
21266
  const response = await pollApiGet(url, isForwardingFeeResponse, DEFAULT_CONFIG$1);
21069
21267
  // Validate that we have at least one fee tier
21070
21268
  if (response.length === 0) {
@@ -23400,7 +23598,7 @@ const mockAttestationMessage = {
23400
23598
  return step;
23401
23599
  }
23402
23600
 
23403
- var version$3 = "1.14.1";
23601
+ var version$3 = "1.14.3";
23404
23602
  var pkg$3 = {
23405
23603
  version: version$3};
23406
23604
 
@@ -24319,9 +24517,34 @@ function assertCCTPV2Config(config) {
24319
24517
  tokenAddress: source.chain.usdcAddress,
24320
24518
  operationContext: sourceOperationContext
24321
24519
  });
24520
+ await this.assertAmountCoversRecipientSetupFee(params);
24322
24521
  return bridge$1(params, this);
24323
24522
  }
24324
24523
  /**
24524
+ * Reject a forwarded transfer to Solana whose amount does not exceed the fee.
24525
+ *
24526
+ * CCTP v2 reverts a burn whose `maxFee` is not less than its amount, and the Solana
24527
+ * forwarding fee includes the recipient token account rent. Checking before the
24528
+ * approval avoids sending a transaction that can never be followed by a burn.
24529
+ * Skipped when the caller provides `config.maxFee`.
24530
+ *
24531
+ * @param params - The validated CCTP v2 bridge parameters.
24532
+ * @returns A promise that resolves when the amount covers the fee.
24533
+ * @throws {KitError} INPUT_INVALID_AMOUNT (FATAL) when the fee is greater than or equal
24534
+ * to the amount.
24535
+ */ async assertAmountCoversRecipientSetupFee(params) {
24536
+ const { destination, amount, config } = params;
24537
+ if (destination.useForwarder !== true || config.maxFee !== undefined || !requiresRecipientSetup(destination.chain.type)) {
24538
+ return;
24539
+ }
24540
+ const { providerFee, forwarderFee } = await this.getMaxFee(params);
24541
+ const maxFee = providerFee + forwarderFee;
24542
+ if (maxFee >= BigInt(amount)) {
24543
+ // Amounts and fees are in USDC minor units (6 decimals)
24544
+ 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`);
24545
+ }
24546
+ }
24547
+ /**
24325
24548
  * Retry a failed or incomplete CCTP v2 bridge operation.
24326
24549
  *
24327
24550
  * Analyzes the bridge result to determine where to continue and executes
@@ -25038,6 +25261,9 @@ function assertCCTPV2Config(config) {
25038
25261
  * | FAST | true | No | maxFee = calculatedBurnFee + forwardingFee |
25039
25262
  * | FAST | true | Yes | maxFee = config.maxFee |
25040
25263
  *
25264
+ * For forwarded burns to Solana, the forwarding fee includes the rent for creating the
25265
+ * recipient's token account. A caller-provided `maxFee` must cover it too.
25266
+ *
25041
25267
  * @param params - The bridge parameters object containing:
25042
25268
  * - `source`: The source wallet context with chain definition and adapter
25043
25269
  * - `destination`: The destination wallet context with chain definition and adapter
@@ -25076,6 +25302,7 @@ function assertCCTPV2Config(config) {
25076
25302
  }
25077
25303
  // Get the min finality threshold based on the transfer speed
25078
25304
  const minFinalityThreshold = CCTPv2MinFinalityThreshold[transferSpeed];
25305
+ const includeRecipientSetup = requiresRecipientSetup(destination.chain.type);
25079
25306
  // SLOW transfer without forwarder: no fee
25080
25307
  if (transferSpeed === TransferSpeed.SLOW && !useForwarder) {
25081
25308
  return {
@@ -25085,7 +25312,7 @@ function assertCCTPV2Config(config) {
25085
25312
  }
25086
25313
  // SLOW transfer with forwarder: only forwarding fee
25087
25314
  if (transferSpeed === TransferSpeed.SLOW && useForwarder) {
25088
- const forwarderFee = await fetchForwardingFee(source.chain.cctp.domain, destination.chain.cctp.domain, source.chain.isTestnet, minFinalityThreshold);
25315
+ const forwarderFee = await fetchForwardingFee(source.chain.cctp.domain, destination.chain.cctp.domain, source.chain.isTestnet, minFinalityThreshold, includeRecipientSetup);
25089
25316
  return {
25090
25317
  providerFee: 0n,
25091
25318
  forwarderFee
@@ -25095,7 +25322,7 @@ function assertCCTPV2Config(config) {
25095
25322
  // Fetch fees in parallel when forwarder is enabled
25096
25323
  const [scaledBps, forwarderFee] = await Promise.all([
25097
25324
  fetchUsdcFastBurnFee(source.chain.cctp.domain, destination.chain.cctp.domain, source.chain.isTestnet),
25098
- useForwarder ? fetchForwardingFee(source.chain.cctp.domain, destination.chain.cctp.domain, source.chain.isTestnet, minFinalityThreshold) : Promise.resolve(0n)
25325
+ useForwarder ? fetchForwardingFee(source.chain.cctp.domain, destination.chain.cctp.domain, source.chain.isTestnet, minFinalityThreshold, includeRecipientSetup) : Promise.resolve(0n)
25099
25326
  ]);
25100
25327
  // Calculate fee proportional to the transfer amount
25101
25328
  const amountInMinorUnits = BigInt(amount);
@@ -25645,6 +25872,21 @@ function assertCCTPV2Config(config) {
25645
25872
  'bridge_forward'
25646
25873
  ]
25647
25874
  ];
25875
+ /**
25876
+ * Provider step names whose `txHash` is the source-chain transaction that
25877
+ * moves funds into the bridge protocol.
25878
+ *
25879
+ * @remarks
25880
+ * The success event carries this hash so the backend can join it to the
25881
+ * on-chain CCTP / CCTPx event for the same transfer. `burn` is the CCTP v2
25882
+ * step (including batched and source-fee flows); `transfer` is the CCTPx
25883
+ * `CrossChainTokenService` step.
25884
+ *
25885
+ * @internal
25886
+ */ const BRIDGE_SOURCE_TX_STEP_NAMES = [
25887
+ 'burn',
25888
+ 'transfer'
25889
+ ];
25648
25890
 
25649
25891
  /**
25650
25892
  * Base URL for Circle's Quote API (hosted in Iris) on mainnet/production.
@@ -26702,6 +26944,85 @@ async function resolveExecutionQuote(binding, suppliedQuote, refundAddress) {
26702
26944
  }
26703
26945
  return undefined;
26704
26946
  }
26947
+ /**
26948
+ * Pick the source-chain transaction hash from a bridge result.
26949
+ *
26950
+ * Walk the steps from the end and return the hash of the most recent
26951
+ * successful source step (`burn` for CCTP v2, `transfer` for CCTPx). Walking
26952
+ * backwards selects the attempt that landed when a retry appended a new
26953
+ * source step after a failed one.
26954
+ *
26955
+ * @param steps - All steps from the bridge result.
26956
+ * @returns The source transaction hash, or `undefined` if no source step recorded one.
26957
+ *
26958
+ * @internal
26959
+ */ function pickSourceTxHash(steps) {
26960
+ for(let i = steps.length - 1; i >= 0; i--){
26961
+ const step = steps[i];
26962
+ if (step?.txHash != null && step.state === 'success' && BRIDGE_SOURCE_TX_STEP_NAMES.includes(step.name)) {
26963
+ return step.txHash;
26964
+ }
26965
+ }
26966
+ return undefined;
26967
+ }
26968
+ /**
26969
+ * Resolve the token value reported in telemetry.
26970
+ *
26971
+ * CCTPx reports `result.token` as a bytes32 token id, while the backend's
26972
+ * canonical tables key tokens by symbol. Map the canonical id of a known CCTPx
26973
+ * token back to its symbol and pass any other value (USDC, or a registry token
26974
+ * without a known symbol) through unchanged. Registry symbols are not used
26975
+ * because they are not unique across CCTPx tokens. Never throws.
26976
+ *
26977
+ * @param token - The token from a bridge result.
26978
+ * @param sourceChain - The result's source chain; selects testnet or mainnet ids.
26979
+ * @returns The known symbol, or `token` unchanged.
26980
+ *
26981
+ * @internal
26982
+ */ function toTelemetryToken(token, sourceChain) {
26983
+ try {
26984
+ const network = sourceChain.isTestnet ? 'testnet' : 'mainnet';
26985
+ return resolveToKnownTokenSymbol(token, network) ?? token;
26986
+ } catch {
26987
+ return token;
26988
+ }
26989
+ }
26990
+ /**
26991
+ * Emit the `bridge_bridge` success event for a completed transfer.
26992
+ *
26993
+ * No-ops unless `result.state` is `'success'`. Failed results are already
26994
+ * reported by error telemetry. Never throws: a malformed result (for example
26995
+ * from a custom provider) is reported as a telemetry drop warning and the
26996
+ * caller's result is returned unchanged.
26997
+ *
26998
+ * @param result - The human-readable bridge result returned to the caller.
26999
+ * @param config - Analytics telemetry identity, gated by `disableAnalytics`.
27000
+ *
27001
+ * @internal
27002
+ */ function emitBridgeSuccessTelemetry(result, config) {
27003
+ if (config.disabled || result.state !== 'success') {
27004
+ return;
27005
+ }
27006
+ try {
27007
+ const txHash = pickSourceTxHash(result.steps);
27008
+ // `JSON.stringify` turns a non-finite number into `null`, which the logs
27009
+ // endpoint's strict schema rejects along with the whole event.
27010
+ const amountIn = Number.parseFloat(result.amount);
27011
+ emitSuccessTelemetry(BRIDGE_EVENT_TYPES.BRIDGE, config, {
27012
+ sourceChain: result.source.chain.chain,
27013
+ destinationChain: result.destination.chain.chain,
27014
+ tokenIn: toTelemetryToken(result.token, result.source.chain),
27015
+ ...Number.isFinite(amountIn) && {
27016
+ amountIn
27017
+ },
27018
+ ...txHash != null && {
27019
+ txHash
27020
+ }
27021
+ });
27022
+ } catch (telemetryError) {
27023
+ warnTelemetryDrop(BRIDGE_EVENT_TYPES.BRIDGE, telemetryError);
27024
+ }
27025
+ }
26705
27026
  /**
26706
27027
  * BridgeKit caller component for retry and estimate operations.
26707
27028
  */ const BRIDGE_KIT_CALLER = {
@@ -26832,6 +27153,10 @@ async function resolveExecutionQuote(binding, suppliedQuote, refundAddress) {
26832
27153
  /** Whether error telemetry is disabled. */ disableErrorReporting;
26833
27154
  /** Per-kit telemetry identity for shared helpers. */ telemetryConfig;
26834
27155
  /**
27156
+ * Per-kit telemetry identity for success events. Gated by `disableAnalytics`
27157
+ * independently of `disableErrorReporting`.
27158
+ */ analyticsTelemetryConfig;
27159
+ /**
26835
27160
  * Create a new BridgeKit instance.
26836
27161
  *
26837
27162
  * @param config - The configuration containing the CCTPv2 provider
@@ -26858,6 +27183,11 @@ async function resolveExecutionQuote(binding, suppliedQuote, refundAddress) {
26858
27183
  sdkVersion: pkg$4.version,
26859
27184
  disabled: this.disableErrorReporting
26860
27185
  };
27186
+ this.analyticsTelemetryConfig = {
27187
+ sdkName: SDK_NAME,
27188
+ sdkVersion: pkg$4.version,
27189
+ disabled: config.disableAnalytics === true
27190
+ };
26861
27191
  for (const provider of this.providers){
26862
27192
  provider.registerDispatcher(this.actionDispatcher);
26863
27193
  }
@@ -26871,7 +27201,7 @@ async function resolveExecutionQuote(binding, suppliedQuote, refundAddress) {
26871
27201
  this.actionDispatcher.off(actionOrWildCard, handler);
26872
27202
  }
26873
27203
  async bridge(params) {
26874
- return withErrorTelemetry(async ()=>{
27204
+ const bridgeResult = await withErrorTelemetry(async ()=>{
26875
27205
  // First validate the parameters
26876
27206
  assertBridgeParams(params, bridgeParamsWithChainIdentifierSchema);
26877
27207
  // Then resolve chain definitions (includes adapter chain support validation)
@@ -26908,7 +27238,7 @@ async function resolveExecutionQuote(binding, suppliedQuote, refundAddress) {
26908
27238
  emitResultStepErrorTelemetry(failedStep, BRIDGE_STEP_EVENT_MAP, BRIDGE_EVENT_TYPES.BRIDGE, this.telemetryConfig, {
26909
27239
  sourceChain: resolveChainName(params.from.chain),
26910
27240
  destinationChain: resolveChainName(params.to.chain),
26911
- tokenIn: params.token,
27241
+ tokenIn: toTelemetryToken(result.token, result.source.chain),
26912
27242
  ...txHash != null && {
26913
27243
  txHash
26914
27244
  }
@@ -26920,6 +27250,8 @@ async function resolveExecutionQuote(binding, suppliedQuote, refundAddress) {
26920
27250
  destinationChain: resolveChainName(params.to.chain),
26921
27251
  tokenIn: params.token
26922
27252
  });
27253
+ emitBridgeSuccessTelemetry(bridgeResult, this.analyticsTelemetryConfig);
27254
+ return bridgeResult;
26923
27255
  }
26924
27256
  /**
26925
27257
  * Retry a failed or incomplete cross-chain USDC bridge operation.
@@ -26990,7 +27322,7 @@ async function resolveExecutionQuote(binding, suppliedQuote, refundAddress) {
26990
27322
  * )
26991
27323
  * ```
26992
27324
  */ async retry(result, context, invocationMeta) {
26993
- return withErrorTelemetry(async ()=>{
27325
+ const retriedResult = await withErrorTelemetry(async ()=>{
26994
27326
  // Cast the providers list to the wide view here. The tuple
26995
27327
  // type on `this.providers` retains concrete provider types
26996
27328
  // (CCTPv2 narrows `TToken` to `'USDC'`); calling `.retry`
@@ -27018,7 +27350,7 @@ async function resolveExecutionQuote(binding, suppliedQuote, refundAddress) {
27018
27350
  emitResultStepErrorTelemetry(failedStep, BRIDGE_STEP_EVENT_MAP, BRIDGE_EVENT_TYPES.RETRY, this.telemetryConfig, {
27019
27351
  sourceChain: result.source.chain.chain,
27020
27352
  destinationChain: result.destination.chain.chain,
27021
- tokenIn: result.token,
27353
+ tokenIn: toTelemetryToken(result.token, result.source.chain),
27022
27354
  ...txHash != null && {
27023
27355
  txHash
27024
27356
  }
@@ -27032,8 +27364,16 @@ async function resolveExecutionQuote(binding, suppliedQuote, refundAddress) {
27032
27364
  }, BRIDGE_EVENT_TYPES.RETRY, this.telemetryConfig, {
27033
27365
  sourceChain: result.source.chain.chain,
27034
27366
  destinationChain: result.destination.chain.chain,
27035
- tokenIn: result.token
27367
+ tokenIn: toTelemetryToken(result.token, result.source.chain)
27036
27368
  });
27369
+ // A retry that completes the transfer is the transfer's only success
27370
+ // event, so it reports the same `bridge_bridge` type as `bridge()`. An
27371
+ // input that already succeeded was reported by the call that completed it
27372
+ // (CCTP v2 returns such a result unchanged), so it is not reported again.
27373
+ if (result.state !== 'success') {
27374
+ emitBridgeSuccessTelemetry(retriedResult, this.analyticsTelemetryConfig);
27375
+ }
27376
+ return retriedResult;
27037
27377
  }
27038
27378
  async estimate(params) {
27039
27379
  return withErrorTelemetry(async ()=>{
@@ -27629,6 +27969,9 @@ registerKit(`${pkg$4.name}/${pkg$4.version}`);
27629
27969
  ...context.disableErrorReporting != null && {
27630
27970
  disableErrorReporting: context.disableErrorReporting
27631
27971
  },
27972
+ ...context.disableAnalytics != null && {
27973
+ disableAnalytics: context.disableAnalytics
27974
+ },
27632
27975
  ...context.headers != null && {
27633
27976
  headers: context.headers
27634
27977
  }
@@ -27651,7 +27994,7 @@ registerKit(`${pkg$4.name}/${pkg$4.version}`);
27651
27994
  };
27652
27995
 
27653
27996
  var name$2 = "@circle-fin/swap-kit";
27654
- var version$2 = "1.7.1";
27997
+ var version$2 = "1.8.1";
27655
27998
  var pkg$2 = {
27656
27999
  name: name$2,
27657
28000
  version: version$2};
@@ -27993,6 +28336,64 @@ const optionalChainIdentifierField = chainIdentifierField.optional();
27993
28336
  }, {
27994
28337
  message: 'fee amount must be non-negative (>= 0)'
27995
28338
  });
28339
+ /**
28340
+ * Parses a display-only field leniently: a value `schema` rejects is replaced
28341
+ * with `fallback` instead of failing the response that carries it.
28342
+ * Implemented with `preprocess` rather than Zod's `.catch()` because static
28343
+ * analysis misreads `.catch` on the schema chain as an unhandled Promise
28344
+ * (S7785).
28345
+ */ const withFallback = (schema, fallback)=>z.preprocess((value)=>schema.safeParse(value).success ? value : fallback, schema);
28346
+ const swapRouteTokenSchema = z.object({
28347
+ symbol: z.string(),
28348
+ address: z.string(),
28349
+ decimals: z.number()
28350
+ });
28351
+ const swapRouteMetadataSchema = z.record(z.unknown()).optional();
28352
+ const swapRouteStepSchema = z.object({
28353
+ type: z.string(),
28354
+ tool: z.string(),
28355
+ toolName: z.string(),
28356
+ fromToken: swapRouteTokenSchema,
28357
+ toToken: swapRouteTokenSchema,
28358
+ estimate: z.object({
28359
+ fromAmount: z.string(),
28360
+ toAmount: z.string(),
28361
+ gasCostUSD: z.string().optional()
28362
+ }),
28363
+ metadata: swapRouteMetadataSchema
28364
+ });
28365
+ /**
28366
+ * Strict schema for a `SwapRoute`. Module-private: service responses
28367
+ * parse routes leniently through {@link responseSwapRouteSchema}.
28368
+ */ const swapRouteSchema = z.object({
28369
+ provider: z.string().min(1, 'provider must be a non-empty string'),
28370
+ steps: z.array(swapRouteStepSchema),
28371
+ metadata: swapRouteMetadataSchema
28372
+ });
28373
+ /**
28374
+ * Route details are display-only, so service responses parse them leniently:
28375
+ * steps or metadata in a shape the SDK does not know are dropped rather than
28376
+ * failing the quote or swap they describe. Steps are all-or-nothing: if any
28377
+ * step is malformed, `steps` is empty, so a route never silently loses a hop.
28378
+ */ const responseSwapRouteSchema = swapRouteSchema.extend({
28379
+ steps: withFallback(swapRouteSchema.shape.steps, []),
28380
+ metadata: withFallback(swapRouteMetadataSchema, undefined)
28381
+ });
28382
+ const createSwapFeeItemSchema = z.object({
28383
+ token: z.string({
28384
+ required_error: 'fee token is required',
28385
+ invalid_type_error: 'fee token must be a string'
28386
+ }).min(1, 'fee token must be a non-empty string'),
28387
+ amount: feeAmountSchema,
28388
+ decimals: z.number().int('fee token decimals must be an integer').nonnegative('fee token decimals must be a non-negative integer').optional(),
28389
+ symbol: z.string({
28390
+ invalid_type_error: 'fee token symbol must be a string'
28391
+ }).min(1, 'fee token symbol must be a non-empty string').optional(),
28392
+ // Descriptive metadata: a value the SDK cannot use is dropped rather than
28393
+ // failing the response that carries the fee.
28394
+ type: withFallback(z.string().min(1).optional(), undefined),
28395
+ chain: withFallback(z.string().min(1).optional(), undefined)
28396
+ });
27996
28397
  /**
27997
28398
  * Zod schema for validating API keys.
27998
28399
  *
@@ -28078,10 +28479,7 @@ const optionalChainIdentifierField = chainIdentifierField.optional();
28078
28479
  /** Quote information with estimated amounts and route */ quote: z.object({
28079
28480
  /** Estimated output amount (base units) */ estimatedAmount: z.string().min(1, 'estimatedAmount must be a non-empty string').regex(/^\d+$/, 'estimatedAmount must be a numeric string'),
28080
28481
  /** Guaranteed minimum output amount (base units) */ minAmount: z.string().min(1, 'minAmount must be a non-empty string').regex(/^\d+$/, 'minAmount must be a numeric string'),
28081
- /** Route information from DEX aggregator */ route: z.object({
28082
- provider: z.string().min(1, 'provider must be a non-empty string'),
28083
- steps: z.array(z.unknown())
28084
- })
28482
+ /** Route information from DEX aggregator */ route: responseSwapRouteSchema
28085
28483
  }),
28086
28484
  /** Fee context indicating where fees should be collected */ feeContext: quoteFeeContextSchema
28087
28485
  });
@@ -28297,19 +28695,6 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
28297
28695
  }).min(1, 'toChain must be a non-empty string if provided').optional(),
28298
28696
  apiKey: apiKeySchema.optional()
28299
28697
  });
28300
- /**
28301
- * Zod schema for validating CreateSwapResponse payloads.
28302
- */ const createSwapFeeItemSchema = z.object({
28303
- token: z.string({
28304
- required_error: 'fee token is required',
28305
- invalid_type_error: 'fee token must be a string'
28306
- }).min(1, 'fee token must be a non-empty string'),
28307
- amount: feeAmountSchema,
28308
- decimals: z.number().int('fee token decimals must be an integer').nonnegative('fee token decimals must be a non-negative integer').optional(),
28309
- symbol: z.string({
28310
- invalid_type_error: 'fee token symbol must be a string'
28311
- }).min(1, 'fee token symbol must be a non-empty string').optional()
28312
- });
28313
28698
  /**
28314
28699
  * Developer fee item schema with basis field.
28315
28700
  */ const createSwapDeveloperFeeItemSchema = createSwapFeeItemSchema.extend({
@@ -28447,6 +28832,8 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
28447
28832
  correlationId: z.preprocess((value)=>z.string().uuid().safeParse(value).success ? value : undefined, z.string().optional()),
28448
28833
  config: createSwapRequestBaseSchema.shape.config.optional(),
28449
28834
  fees: createSwapFeesSchema.optional(),
28835
+ // A route the SDK cannot parse is dropped instead of failing the swap.
28836
+ route: withFallback(responseSwapRouteSchema.optional(), undefined),
28450
28837
  transaction: createSwapTransactionSchema
28451
28838
  });
28452
28839
 
@@ -29574,6 +29961,16 @@ createTokenRegistry();
29574
29961
  name: 'USDC',
29575
29962
  version: '2'
29576
29963
  },
29964
+ Cronos: {
29965
+ chainId: Cronos.chainId,
29966
+ name: 'USDC',
29967
+ version: '2'
29968
+ },
29969
+ Morph: {
29970
+ chainId: Morph.chainId,
29971
+ name: 'USDC',
29972
+ version: '2'
29973
+ },
29577
29974
  Arc_Testnet: {
29578
29975
  chainId: ArcTestnet.chainId,
29579
29976
  name: 'USDC',
@@ -29662,6 +30059,7 @@ const optionalSwapChainIdentifierField = swapChainIdentifierField.optional();
29662
30059
  * - customFee: Optional fee configuration
29663
30060
  * - apiKey: Optional credential string
29664
30061
  * - kitKey: Optional credential string (deprecated alias for apiKey)
30062
+ * - batchTransactions: Optional boolean to opt out of atomic batching
29665
30063
  */ const swapConfigSchema = z.object({
29666
30064
  allowanceStrategy: allowanceStrategySchema.optional(),
29667
30065
  slippageBps: z.number().int().min(0).optional(),
@@ -29672,7 +30070,10 @@ const optionalSwapChainIdentifierField = swapChainIdentifierField.optional();
29672
30070
  })(z.string())).optional(),
29673
30071
  customFee: swapCustomFeeSchema.optional(),
29674
30072
  apiKey: z.string().optional(),
29675
- kitKey: z.string().optional()
30073
+ kitKey: z.string().optional(),
30074
+ batchTransactions: z.boolean({
30075
+ invalid_type_error: 'batchTransactions must be a boolean'
30076
+ }).optional()
29676
30077
  });
29677
30078
  const swapDestinationSchema = z.object({
29678
30079
  chain: optionalSwapChainIdentifierField,
@@ -29865,7 +30266,7 @@ new Set(Object.values(Blockchain));
29865
30266
  registerKit(`${pkg$2.name}/${pkg$2.version}`);
29866
30267
 
29867
30268
  var name$1 = "@circle-fin/earn-kit";
29868
- var version$1 = "1.8.1";
30269
+ var version$1 = "1.10.0";
29869
30270
  var pkg$1 = {
29870
30271
  name: name$1,
29871
30272
  version: version$1};
@@ -31369,7 +31770,7 @@ registerKit(`${pkg$1.name}/${pkg$1.version}`);
31369
31770
  };
31370
31771
 
31371
31772
  var name = "@circle-fin/borrow-kit";
31372
- var version = "1.0.0";
31773
+ var version = "1.1.1";
31373
31774
  var pkg = {
31374
31775
  name: name,
31375
31776
  version: version};