@circle-fin/app-kit 1.16.0 → 1.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.cjs CHANGED
@@ -2811,22 +2811,96 @@ let activeRedactor = defaultMessageRedactor;
2811
2811
  '0x9147d463': 'Final balance is less than the initial balance',
2812
2812
  '0xf4b3b1bc': 'Native token transfer to the beneficiary failed'
2813
2813
  };
2814
+ /**
2815
+ * Matches revert data: a 4-byte selector followed by whole ABI-encoded bytes.
2816
+ */ const REVERT_DATA_PATTERN = /^0x[0-9a-f]{8}(?:[0-9a-f]{2})*$/i;
2817
+ /**
2818
+ * Returns the revert data held by one error in a `cause` chain.
2819
+ *
2820
+ * Browser wallets (EIP-1193) wrap the node's error, so the revert data sits
2821
+ * one level down as `data.data`. viem's `getContractError` reads it the same
2822
+ * way, then keeps it on `ContractFunctionRevertedError.raw` and drops the
2823
+ * node's error from the chain.
2824
+ *
2825
+ * @param link - One error in the `cause` chain
2826
+ * @returns The `raw` or `data` string, or undefined if there is none
2827
+ */ function getRevertData(link) {
2828
+ if ('raw' in link && typeof link.raw === 'string' && REVERT_DATA_PATTERN.test(link.raw)) {
2829
+ return link.raw;
2830
+ }
2831
+ if (!('data' in link)) {
2832
+ return undefined;
2833
+ }
2834
+ const { data } = link;
2835
+ if (typeof data === 'string') {
2836
+ return data;
2837
+ }
2838
+ if (data !== null && typeof data === 'object' && 'data' in data && typeof data.data === 'string') {
2839
+ return data.data;
2840
+ }
2841
+ return undefined;
2842
+ }
2843
+ /**
2844
+ * Finds the first revert data on the error or in its `cause` chain that
2845
+ * `accept` takes.
2846
+ *
2847
+ * viem keeps the node's revert data on an inner `RpcRequestError`, not on
2848
+ * the error it throws. The `seen` set stops a self-referencing `cause` chain.
2849
+ *
2850
+ * @param error - The error object
2851
+ * @param accept - Returns true for the data shape the caller wants
2852
+ * @returns The data as written on the error, or undefined if not found
2853
+ */ function findRevertData(error, accept) {
2854
+ const seen = new Set();
2855
+ let current = error;
2856
+ while(current !== null && typeof current === 'object' && !seen.has(current)){
2857
+ seen.add(current);
2858
+ const data = getRevertData(current);
2859
+ if (data !== undefined && accept(data)) {
2860
+ return data;
2861
+ }
2862
+ current = 'cause' in current ? current.cause : undefined;
2863
+ }
2864
+ return undefined;
2865
+ }
2866
+ /**
2867
+ * Finds the raw revert data on an error or anywhere in its `cause` chain.
2868
+ *
2869
+ * Reads `data`, the nested `data.data` from browser wallets, and viem's
2870
+ * `raw`. Only hex of at least 4 bytes counts, so decoded `data` objects and
2871
+ * empty `0x` data are skipped.
2872
+ *
2873
+ * Revert data is public: it is what the contract returned. Keep it when the
2874
+ * rest of a raw error is redacted, so callers can still decode the revert.
2875
+ *
2876
+ * @param error - The raw error from the blockchain library
2877
+ * @returns The revert data in lowercase, or undefined if there is none
2878
+ *
2879
+ * @example
2880
+ * ```typescript
2881
+ * import { extractRevertData } from '@core/errors'
2882
+ *
2883
+ * extractRevertData({ cause: { data: { data: '0xF64059DB' } } })
2884
+ * // Returns: '0xf64059db'
2885
+ * ```
2886
+ */ function extractRevertData(error) {
2887
+ return findRevertData(error, (data)=>REVERT_DATA_PATTERN.test(data))?.toLowerCase();
2888
+ }
2814
2889
  /**
2815
2890
  * Attempts to extract an error selector from an error object or message.
2816
2891
  *
2817
2892
  * Checks multiple sources in priority order:
2818
- * 1. `error.data` field
2893
+ * 1. `raw` or `data` field (or nested `data.data`) on the error or anywhere
2894
+ * in its `cause` chain
2819
2895
  * 2. Message pattern: "custom error 0x8baa579f"
2820
2896
  *
2821
2897
  * @param msg - The error message
2822
2898
  * @param error - The error object (optional)
2823
2899
  * @returns The 4 bytes selector , or undefined if not found
2824
2900
  */ function extractErrorSelector(msg, error) {
2825
- // Check error.data field
2826
- if (error !== null && typeof error === 'object' && 'data' in error) {
2827
- if (typeof error.data === 'string' && /^0x[0-9a-f]{8}$/i.test(error.data)) {
2828
- return error.data.toLowerCase();
2829
- }
2901
+ const data = findRevertData(error, (value)=>/^0x[0-9a-f]{8}$/i.test(value));
2902
+ if (data !== undefined) {
2903
+ return data.toLowerCase();
2830
2904
  }
2831
2905
  // Check for "custom error 0x8baa579f" in the message
2832
2906
  const match = /custom error (0x[0-9a-f]{8})\b/i.exec(msg);
@@ -4363,7 +4437,7 @@ let activeRedactor = defaultMessageRedactor;
4363
4437
  * between input errors (fix your request) and service errors (retry later).
4364
4438
  *
4365
4439
  * Error code ranges:
4366
- * - 1100-1106: INPUT errors — invalid, unsupported, or stale request state
4440
+ * - 1100-1107: INPUT errors — invalid, unsupported, or stale request state
4367
4441
  * - 8100-8105: SERVICE errors — retryable backend/provider failures
4368
4442
  *
4369
4443
  * @example
@@ -4424,6 +4498,15 @@ let activeRedactor = defaultMessageRedactor;
4424
4498
  name: 'EARN_BRIDGE_QUOTE_EXPIRED',
4425
4499
  type: 'INPUT'
4426
4500
  },
4501
+ /**
4502
+ * The wallet's vault shares are inside the vault's share lock period, which
4503
+ * starts at deposit. Surfaced as FATAL rather than RETRYABLE because the
4504
+ * lock can last hours, so an immediate retry fails the same way.
4505
+ */ SHARES_LOCKED: {
4506
+ code: 1107,
4507
+ name: 'EARN_SHARES_LOCKED',
4508
+ type: 'INPUT'
4509
+ },
4427
4510
  /** The proxy signing call failed — retryable. */ SIGNING_FAILED: {
4428
4511
  code: 8100,
4429
4512
  name: 'EARN_SIGNING_FAILED',
@@ -5570,6 +5653,8 @@ exports.SwapChain = void 0;
5570
5653
  SwapChain["HyperEVM"] = "HyperEVM";
5571
5654
  SwapChain["Monad"] = "Monad";
5572
5655
  SwapChain["Arc"] = "Arc";
5656
+ SwapChain["Cronos"] = "Cronos";
5657
+ SwapChain["Morph"] = "Morph";
5573
5658
  // Testnet chains with swap support
5574
5659
  SwapChain["Arc_Testnet"] = "Arc_Testnet";
5575
5660
  })(exports.SwapChain || (exports.SwapChain = {}));
@@ -6923,7 +7008,8 @@ exports.BorrowChain = void 0;
6923
7008
  }
6924
7009
  },
6925
7010
  kitContracts: {
6926
- bridge: BRIDGE_CONTRACT_EVM_MAINNET
7011
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
7012
+ adapter: ADAPTER_CONTRACT_EVM_MAINNET
6927
7013
  }
6928
7014
  });
6929
7015
 
@@ -7792,7 +7878,8 @@ exports.BorrowChain = void 0;
7792
7878
  }
7793
7879
  },
7794
7880
  kitContracts: {
7795
- bridge: BRIDGE_CONTRACT_EVM_MAINNET
7881
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
7882
+ adapter: ADAPTER_CONTRACT_EVM_MAINNET
7796
7883
  }
7797
7884
  });
7798
7885
 
@@ -10163,8 +10250,8 @@ var Chains = {
10163
10250
  * Zod schema for validating swap-specific chain identifiers.
10164
10251
  *
10165
10252
  * Validates chains based on:
10166
- * - CCTPv2 support (adapter contract deployed)
10167
- * - Mainnet only (no testnets)
10253
+ * - Membership in the SwapChain enum
10254
+ * - Mainnet or explicitly allowlisted testnet
10168
10255
  * - At least one supported token available
10169
10256
  *
10170
10257
  */ const swapChainIdentifierSchema = zod.z.union([
@@ -10806,7 +10893,7 @@ const swapTokenEnumSchema = zod.z.enum([
10806
10893
  *
10807
10894
  * A chain supports swaps if ALL conditions are met:
10808
10895
  * 1. Is a member of the {@link SwapChain} enum
10809
- * 2. Has CCTPv2 support (adapter contract deployed)
10896
+ * 2. Has an Adapter contract configured directly or through CCTP v2
10810
10897
  *
10811
10898
  * Testnets are allowed only when explicitly listed in the
10812
10899
  * {@link SwapChain} enum (e.g., Arc_Testnet).
@@ -10827,14 +10914,14 @@ const swapTokenEnumSchema = zod.z.enum([
10827
10914
  * @remarks
10828
10915
  * Chains automatically become swap-enabled when:
10829
10916
  * - Added to the {@link SwapChain} enum
10830
- * - Adapter contract is deployed (CCTPv2)
10917
+ * - An Adapter contract is deployed and configured
10831
10918
  *
10832
10919
  * No code changes needed when new chains are added!
10833
10920
  */ function isSwapSupportedChain(chain) {
10834
10921
  if (!(chain.chain in exports.SwapChain)) {
10835
10922
  return false;
10836
10923
  }
10837
- if (!isCCTPV2Supported(chain)) {
10924
+ if (!isCCTPV2Supported(chain) && !hasCustomContractSupport(chain, 'adapter')) {
10838
10925
  return false;
10839
10926
  }
10840
10927
  return true;
@@ -10845,7 +10932,7 @@ const swapTokenEnumSchema = zod.z.enum([
10845
10932
  *
10846
10933
  * Dynamically filter chain definitions to include only those that:
10847
10934
  * - Are members of the {@link SwapChain} enum
10848
- * - Have CCTPv2 support
10935
+ * - Have an Adapter contract configured directly or through CCTP v2
10849
10936
  *
10850
10937
  * This function is pure and accepts chains as parameter to avoid
10851
10938
  * circular dependencies.
@@ -12370,11 +12457,13 @@ function parseOrThrow(value, schema, context) {
12370
12457
  [exports.Blockchain.Arbitrum]: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9',
12371
12458
  [exports.Blockchain.Avalanche]: '0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7',
12372
12459
  [exports.Blockchain.Celo]: '0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e',
12460
+ [exports.Blockchain.Cronos]: '0x66e428c3f67a68878562e79A0234c1F83c208770',
12373
12461
  [exports.Blockchain.Ethereum]: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
12374
12462
  [exports.Blockchain.HyperEVM]: '0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb',
12375
12463
  [exports.Blockchain.Ink]: '0x0200C29006150606B650577BBE7B6248F58470c1',
12376
12464
  [exports.Blockchain.Linea]: '0xA219439258ca9da29E9Cc4cE5596924745e12B93',
12377
12465
  [exports.Blockchain.Monad]: '0xe7cd86e13AC4309349F30B3435a9d337750fC82D',
12466
+ [exports.Blockchain.Morph]: '0xe7cd86e13AC4309349F30B3435a9d337750fC82D',
12378
12467
  [exports.Blockchain.NEAR]: 'usdt.tether-token.near',
12379
12468
  [exports.Blockchain.Optimism]: '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58',
12380
12469
  [exports.Blockchain.Polkadot_Asset_Hub]: '1984',
@@ -12433,6 +12522,7 @@ function parseOrThrow(value, schema, context) {
12433
12522
  [exports.Blockchain.Arbitrum]: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1',
12434
12523
  [exports.Blockchain.Avalanche]: '0xd586E7F844cEa2F87f50152665BCbc2C279D8d70',
12435
12524
  [exports.Blockchain.Base]: '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb',
12525
+ [exports.Blockchain.Cronos]: '0xF2001B145b43032AAF5Ee2884e456CCd805F677D',
12436
12526
  [exports.Blockchain.Ethereum]: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
12437
12527
  [exports.Blockchain.Linea]: '0x4AF15ec2A0BD43Db75dd04E62FAA3B8EF36b00d5',
12438
12528
  [exports.Blockchain.Optimism]: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1',
@@ -12495,7 +12585,9 @@ function parseOrThrow(value, schema, context) {
12495
12585
  [exports.Blockchain.Arbitrum]: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
12496
12586
  [exports.Blockchain.Avalanche]: '0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB',
12497
12587
  [exports.Blockchain.Base]: '0x4200000000000000000000000000000000000006',
12588
+ [exports.Blockchain.Cronos]: '0xe44Fd7fCb2b1581822D0c862B68222998a0c299a',
12498
12589
  [exports.Blockchain.Ethereum]: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
12590
+ [exports.Blockchain.Morph]: '0x5300000000000000000000000000000000000011',
12499
12591
  [exports.Blockchain.Optimism]: '0x4200000000000000000000000000000000000006',
12500
12592
  [exports.Blockchain.Polygon]: '0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619',
12501
12593
  [exports.Blockchain.Solana]: '7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs'
@@ -12514,6 +12606,7 @@ function parseOrThrow(value, schema, context) {
12514
12606
  locators: {
12515
12607
  [exports.Blockchain.Ethereum]: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599',
12516
12608
  [exports.Blockchain.Arbitrum]: '0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f',
12609
+ [exports.Blockchain.Cronos]: '0x062E66477Faf219F25D27dCED647BF57C3107d52',
12517
12610
  [exports.Blockchain.Avalanche]: '0x50b7545627a5162F82A992c33b87aDc75187B218'
12518
12611
  }
12519
12612
  };
@@ -14588,7 +14681,7 @@ function resolveOptions(options) {
14588
14681
  }
14589
14682
 
14590
14683
  var name$5 = "@circle-fin/bridge-kit";
14591
- var version$6 = "1.15.2";
14684
+ var version$6 = "1.15.3";
14592
14685
  var pkg$6 = {
14593
14686
  name: name$5,
14594
14687
  version: version$6};
@@ -17653,7 +17746,11 @@ function createNoopLogger() {
17653
17746
  tokenId: bytes32Hex,
17654
17747
  domainId: nonNegativeInteger,
17655
17748
  tokenAddress: zod.z.string(),
17656
- tokenManagerAddress: zod.z.string()
17749
+ tokenManagerAddress: zod.z.string(),
17750
+ // Operator-controlled per-chain kill switch (CCTPE-3979). Optional for
17751
+ // back-compat with older IRIS versions that do not surface it. Downstream
17752
+ // consumers in `findCCTPXDeployment` treat a missing value as `true`.
17753
+ enabled: zod.z.boolean().optional()
17657
17754
  });
17658
17755
  /**
17659
17756
  * Schema for one token entry in the token registry response.
@@ -18137,17 +18234,23 @@ function createNoopLogger() {
18137
18234
  /**
18138
18235
  * Find a token's deployment on the given CCTP domain.
18139
18236
  *
18237
+ * A deployment row with `enabled === false` is treated as missing: an IRIS
18238
+ * operator has flipped the per-chain kill switch (CCTPE-3979), so the SDK must
18239
+ * not route through it even though the row still exists in the registry. A
18240
+ * missing or `true` `enabled` value remains routable — the field is optional on
18241
+ * older IRIS versions and absence is read as "allowed".
18242
+ *
18140
18243
  * @param token - The registry token whose deployments are searched.
18141
18244
  * @param domain - The CCTP domain id to match.
18142
18245
  * @returns The matching {@link CCTPXTokenDeployment}, or `undefined` when the
18143
- * token has no deployment on that domain.
18246
+ * token has no deployment on that domain or the deployment is disabled.
18144
18247
  *
18145
18248
  * @example
18146
18249
  * ```typescript
18147
18250
  * const deployment = findCCTPXDeployment(token, 0)
18148
18251
  * ```
18149
18252
  */ function findCCTPXDeployment(token, domain) {
18150
- return token.deployments.find((deployment)=>deployment.domainId === domain);
18253
+ return token.deployments.find((deployment)=>deployment.domainId === domain && deployment.enabled !== false);
18151
18254
  }
18152
18255
 
18153
18256
  /**
@@ -18249,6 +18352,24 @@ function createNoopLogger() {
18249
18352
  }
18250
18353
  }
18251
18354
  })
18355
+ ],
18356
+ // The token is not deployed on one of the route chains, or an operator has
18357
+ // flipped the per-chain `enabled` kill switch on the IRIS deployment row.
18358
+ // Either way the route is permanently unavailable until the operator intervenes;
18359
+ // a retry at the same source/destination will keep returning this error.
18360
+ [
18361
+ 'TOKEN_NOT_SUPPORTED_ON_ROUTE',
18362
+ ({ sourceDomain, destinationDomain })=>new KitError({
18363
+ ...InputError.UNSUPPORTED_ROUTE,
18364
+ recoverability: 'FATAL',
18365
+ message: 'CCTPx fee quote rejected the route: the token is not supported on ' + 'the source or destination chain (missing deployment or operator-disabled).',
18366
+ cause: {
18367
+ trace: {
18368
+ sourceDomain,
18369
+ destinationDomain
18370
+ }
18371
+ }
18372
+ })
18252
18373
  ]
18253
18374
  ]);
18254
18375
  /**
@@ -26259,7 +26380,7 @@ const mockAttestationMessage = {
26259
26380
  return step;
26260
26381
  }
26261
26382
 
26262
- var version$5 = "1.14.1";
26383
+ var version$5 = "1.14.2";
26263
26384
  var pkg$5 = {
26264
26385
  version: version$5};
26265
26386
 
@@ -30479,7 +30600,7 @@ async function resolveExecutionQuote(binding, suppliedQuote, refundAddress) {
30479
30600
  registerKit(`${pkg$6.name}/${pkg$6.version}`);
30480
30601
 
30481
30602
  var name$4 = "@circle-fin/swap-kit";
30482
- var version$4 = "1.7.1";
30603
+ var version$4 = "1.8.0";
30483
30604
  var pkg$4 = {
30484
30605
  name: name$4,
30485
30606
  version: version$4};
@@ -30939,6 +31060,64 @@ const optionalChainIdentifierField = chainIdentifierField.optional();
30939
31060
  }, {
30940
31061
  message: 'fee amount must be non-negative (>= 0)'
30941
31062
  });
31063
+ /**
31064
+ * Parses a display-only field leniently: a value `schema` rejects is replaced
31065
+ * with `fallback` instead of failing the response that carries it.
31066
+ * Implemented with `preprocess` rather than Zod's `.catch()` because static
31067
+ * analysis misreads `.catch` on the schema chain as an unhandled Promise
31068
+ * (S7785).
31069
+ */ const withFallback = (schema, fallback)=>zod.z.preprocess((value)=>schema.safeParse(value).success ? value : fallback, schema);
31070
+ const swapRouteTokenSchema = zod.z.object({
31071
+ symbol: zod.z.string(),
31072
+ address: zod.z.string(),
31073
+ decimals: zod.z.number()
31074
+ });
31075
+ const swapRouteMetadataSchema = zod.z.record(zod.z.unknown()).optional();
31076
+ const swapRouteStepSchema = zod.z.object({
31077
+ type: zod.z.string(),
31078
+ tool: zod.z.string(),
31079
+ toolName: zod.z.string(),
31080
+ fromToken: swapRouteTokenSchema,
31081
+ toToken: swapRouteTokenSchema,
31082
+ estimate: zod.z.object({
31083
+ fromAmount: zod.z.string(),
31084
+ toAmount: zod.z.string(),
31085
+ gasCostUSD: zod.z.string().optional()
31086
+ }),
31087
+ metadata: swapRouteMetadataSchema
31088
+ });
31089
+ /**
31090
+ * Strict schema for a `SwapRoute`. Module-private: service responses
31091
+ * parse routes leniently through {@link responseSwapRouteSchema}.
31092
+ */ const swapRouteSchema = zod.z.object({
31093
+ provider: zod.z.string().min(1, 'provider must be a non-empty string'),
31094
+ steps: zod.z.array(swapRouteStepSchema),
31095
+ metadata: swapRouteMetadataSchema
31096
+ });
31097
+ /**
31098
+ * Route details are display-only, so service responses parse them leniently:
31099
+ * steps or metadata in a shape the SDK does not know are dropped rather than
31100
+ * failing the quote or swap they describe. Steps are all-or-nothing: if any
31101
+ * step is malformed, `steps` is empty, so a route never silently loses a hop.
31102
+ */ const responseSwapRouteSchema = swapRouteSchema.extend({
31103
+ steps: withFallback(swapRouteSchema.shape.steps, []),
31104
+ metadata: withFallback(swapRouteMetadataSchema, undefined)
31105
+ });
31106
+ const createSwapFeeItemSchema = zod.z.object({
31107
+ token: zod.z.string({
31108
+ required_error: 'fee token is required',
31109
+ invalid_type_error: 'fee token must be a string'
31110
+ }).min(1, 'fee token must be a non-empty string'),
31111
+ amount: feeAmountSchema,
31112
+ decimals: zod.z.number().int('fee token decimals must be an integer').nonnegative('fee token decimals must be a non-negative integer').optional(),
31113
+ symbol: zod.z.string({
31114
+ invalid_type_error: 'fee token symbol must be a string'
31115
+ }).min(1, 'fee token symbol must be a non-empty string').optional(),
31116
+ // Descriptive metadata: a value the SDK cannot use is dropped rather than
31117
+ // failing the response that carries the fee.
31118
+ type: withFallback(zod.z.string().min(1).optional(), undefined),
31119
+ chain: withFallback(zod.z.string().min(1).optional(), undefined)
31120
+ });
30942
31121
  /**
30943
31122
  * Zod schema for validating API keys.
30944
31123
  *
@@ -31024,10 +31203,7 @@ const optionalChainIdentifierField = chainIdentifierField.optional();
31024
31203
  /** Quote information with estimated amounts and route */ quote: zod.z.object({
31025
31204
  /** Estimated output amount (base units) */ estimatedAmount: zod.z.string().min(1, 'estimatedAmount must be a non-empty string').regex(/^\d+$/, 'estimatedAmount must be a numeric string'),
31026
31205
  /** Guaranteed minimum output amount (base units) */ minAmount: zod.z.string().min(1, 'minAmount must be a non-empty string').regex(/^\d+$/, 'minAmount must be a numeric string'),
31027
- /** Route information from DEX aggregator */ route: zod.z.object({
31028
- provider: zod.z.string().min(1, 'provider must be a non-empty string'),
31029
- steps: zod.z.array(zod.z.unknown())
31030
- })
31206
+ /** Route information from DEX aggregator */ route: responseSwapRouteSchema
31031
31207
  }),
31032
31208
  /** Fee context indicating where fees should be collected */ feeContext: quoteFeeContextSchema
31033
31209
  });
@@ -31243,19 +31419,6 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
31243
31419
  }).min(1, 'toChain must be a non-empty string if provided').optional(),
31244
31420
  apiKey: apiKeySchema.optional()
31245
31421
  });
31246
- /**
31247
- * Zod schema for validating CreateSwapResponse payloads.
31248
- */ const createSwapFeeItemSchema = zod.z.object({
31249
- token: zod.z.string({
31250
- required_error: 'fee token is required',
31251
- invalid_type_error: 'fee token must be a string'
31252
- }).min(1, 'fee token must be a non-empty string'),
31253
- amount: feeAmountSchema,
31254
- decimals: zod.z.number().int('fee token decimals must be an integer').nonnegative('fee token decimals must be a non-negative integer').optional(),
31255
- symbol: zod.z.string({
31256
- invalid_type_error: 'fee token symbol must be a string'
31257
- }).min(1, 'fee token symbol must be a non-empty string').optional()
31258
- });
31259
31422
  /**
31260
31423
  * Developer fee item schema with basis field.
31261
31424
  */ const createSwapDeveloperFeeItemSchema = createSwapFeeItemSchema.extend({
@@ -31393,6 +31556,8 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
31393
31556
  correlationId: zod.z.preprocess((value)=>zod.z.string().uuid().safeParse(value).success ? value : undefined, zod.z.string().optional()),
31394
31557
  config: createSwapRequestBaseSchema.shape.config.optional(),
31395
31558
  fees: createSwapFeesSchema.optional(),
31559
+ // A route the SDK cannot parse is dropped instead of failing the swap.
31560
+ route: withFallback(responseSwapRouteSchema.optional(), undefined),
31396
31561
  transaction: createSwapTransactionSchema
31397
31562
  });
31398
31563
 
@@ -31420,6 +31585,25 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
31420
31585
  * }
31421
31586
  * ```
31422
31587
  */ const isGetQuoteResponse = (obj)=>getQuoteResponseSchema.safeParse(obj).success;
31588
+ /**
31589
+ * Parse and transform a GetQuoteResponse object.
31590
+ *
31591
+ * Applies the schema's lenient route and fee parsing: entries the SDK does
31592
+ * not recognise are dropped and undeclared fields are stripped, rather than
31593
+ * passed through to callers as the type guard alone would.
31594
+ *
31595
+ * @param obj - The raw API response object to parse.
31596
+ * @returns The validated and transformed GetQuoteResponse.
31597
+ * @throws If validation fails.
31598
+ *
31599
+ * @example
31600
+ * ```typescript
31601
+ * import { parseGetQuoteResponse } from '@core/service-client'
31602
+ *
31603
+ * const quote = parseGetQuoteResponse(await response.json())
31604
+ * console.log(quote.quote.route.steps.length)
31605
+ * ```
31606
+ */ const parseGetQuoteResponse = (obj)=>getQuoteResponseSchema.parse(obj);
31423
31607
  /**
31424
31608
  * Type guard to validate CreateSwapResponse objects.
31425
31609
  *
@@ -31825,7 +32009,9 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
31825
32009
  }
31826
32010
  }
31827
32011
  };
31828
- return pollApiGet(url, isGetQuoteResponse, effectiveConfig);
32012
+ // Use type guard for validation in polling, then parse for transformation
32013
+ const rawResponse = await pollApiGet(url, isGetQuoteResponse, effectiveConfig);
32014
+ return parseGetQuoteResponse(rawResponse);
31829
32015
  };
31830
32016
 
31831
32017
  /**
@@ -40749,6 +40935,16 @@ function resolveFeePayoutChain(tokenIn, tokenOut, sourceChain, destinationChain)
40749
40935
  name: 'USDC',
40750
40936
  version: '2'
40751
40937
  },
40938
+ Cronos: {
40939
+ chainId: Cronos.chainId,
40940
+ name: 'USDC',
40941
+ version: '2'
40942
+ },
40943
+ Morph: {
40944
+ chainId: Morph.chainId,
40945
+ name: 'USDC',
40946
+ version: '2'
40947
+ },
40752
40948
  Arc_Testnet: {
40753
40949
  chainId: ArcTestnet.chainId,
40754
40950
  name: 'USDC',
@@ -42317,6 +42513,12 @@ const TOKEN_REGISTRY$2 = createTokenRegistry();
42317
42513
  * i.e. 1 + 0.2 + 0.1 = 1.3. Use the greater of (local estimate × this multiplier)
42318
42514
  * or the proxy's gasLimit.
42319
42515
  */ const GAS_SAFETY_MULTIPLIER = 1.3;
42516
+ /**
42517
+ * Gas units used to estimate the displayed gas fee when the service returns an
42518
+ * EVM route without a gasLimit, as direct Circle CCTP plans do. Sized from
42519
+ * observed Adapter executions of those plans. Display-only: it never becomes an
42520
+ * execution gas limit.
42521
+ */ const DEFAULT_EVM_SWAP_GAS_LIMIT = 500_000n;
42320
42522
  const TOKEN_REGISTRY$1 = createTokenRegistry();
42321
42523
  /**
42322
42524
  * Enhances a KitError with transaction details (txHash and explorerUrl).
@@ -42833,9 +43035,9 @@ async function fetchSameChainStatusSnapshot({ isCrossChainSwap, txHash, chain, a
42833
43035
  // Calculate estimated gas/transaction fee based on chain type
42834
43036
  let estimatedGasFee = '0';
42835
43037
  const evmGasLimit = 'gasLimit' in serviceResponse.transaction ? serviceResponse.transaction.gasLimit : undefined;
42836
- if (chain.type === 'evm' && evmGasLimit !== undefined) {
43038
+ if (chain.type === 'evm') {
42837
43039
  // EVM: gasLimit (units) × gasPrice (wei/unit) = estimated gas fee (wei)
42838
- const gasLimitUnits = BigInt(evmGasLimit);
43040
+ const gasLimitUnits = evmGasLimit === undefined ? DEFAULT_EVM_SWAP_GAS_LIMIT : BigInt(evmGasLimit);
42839
43041
  // Use calculateTransactionFee which fetches current gas price and applies buffer
42840
43042
  const gasEstimate = await params.from.adapter.calculateTransactionFee(gasLimitUnits, undefined, chain);
42841
43043
  // Format the fee from wei to human-readable native currency amount
@@ -42879,7 +43081,10 @@ async function fetchSameChainStatusSnapshot({ isCrossChainSwap, txHash, chain, a
42879
43081
  const estimateResult = {
42880
43082
  stopLimit: formattedStopLimit,
42881
43083
  estimatedOutput: formattedEstimatedOutput,
42882
- fees
43084
+ fees,
43085
+ ...serviceResponse.route !== undefined && {
43086
+ route: serviceResponse.route
43087
+ }
42883
43088
  };
42884
43089
  return estimateResult;
42885
43090
  }
@@ -43264,7 +43469,7 @@ async function fetchSameChainStatusSnapshot({ isCrossChainSwap, txHash, chain, a
43264
43469
  * }
43265
43470
  * ```
43266
43471
  */ async swap(params) {
43267
- // Extract wallet context & validate
43472
+ validateServiceSwapParams(params);
43268
43473
  const { from, tokenIn, tokenOut, config } = params;
43269
43474
  const { adapter, chain } = from;
43270
43475
  const destinationChain = params.toChain ?? chain;
@@ -43401,6 +43606,9 @@ async function fetchSameChainStatusSnapshot({ isCrossChainSwap, txHash, chain, a
43401
43606
  ...swapResultFees !== undefined && {
43402
43607
  fees: swapResultFees
43403
43608
  },
43609
+ ...serviceResponse.route !== undefined && {
43610
+ route: serviceResponse.route
43611
+ },
43404
43612
  progress: statusSnapshot.progress,
43405
43613
  ...statusSnapshot.amountOut !== undefined && {
43406
43614
  amountOut: statusSnapshot.amountOut
@@ -43601,7 +43809,13 @@ async function fetchSameChainStatusSnapshot({ isCrossChainSwap, txHash, chain, a
43601
43809
  return {
43602
43810
  token: formatted.token,
43603
43811
  amount: formatted.amount,
43604
- type
43812
+ type,
43813
+ ...fee.type !== undefined && {
43814
+ component: fee.type
43815
+ },
43816
+ ...fee.chain !== undefined && {
43817
+ chain: fee.chain
43818
+ }
43605
43819
  };
43606
43820
  }));
43607
43821
  }
@@ -43622,7 +43836,13 @@ async function fetchSameChainStatusSnapshot({ isCrossChainSwap, txHash, chain, a
43622
43836
  token: formatted.token,
43623
43837
  amount: formatted.amount,
43624
43838
  type: 'developer',
43625
- recipientAddress
43839
+ recipientAddress,
43840
+ ...fee.type !== undefined && {
43841
+ component: fee.type
43842
+ },
43843
+ ...fee.chain !== undefined && {
43844
+ chain: fee.chain
43845
+ }
43626
43846
  };
43627
43847
  }));
43628
43848
  }
@@ -43679,6 +43899,7 @@ const optionalSwapChainIdentifierField = swapChainIdentifierField.optional();
43679
43899
  * - customFee: Optional fee configuration
43680
43900
  * - apiKey: Optional credential string
43681
43901
  * - kitKey: Optional credential string (deprecated alias for apiKey)
43902
+ * - batchTransactions: Optional boolean to opt out of atomic batching
43682
43903
  */ const swapConfigSchema = zod.z.object({
43683
43904
  allowanceStrategy: allowanceStrategySchema$1.optional(),
43684
43905
  slippageBps: zod.z.number().int().min(0).optional(),
@@ -43689,7 +43910,10 @@ const optionalSwapChainIdentifierField = swapChainIdentifierField.optional();
43689
43910
  })(zod.z.string())).optional(),
43690
43911
  customFee: swapCustomFeeSchema.optional(),
43691
43912
  apiKey: zod.z.string().optional(),
43692
- kitKey: zod.z.string().optional()
43913
+ kitKey: zod.z.string().optional(),
43914
+ batchTransactions: zod.z.boolean({
43915
+ invalid_type_error: 'batchTransactions must be a boolean'
43916
+ }).optional()
43693
43917
  });
43694
43918
  const swapDestinationSchema = zod.z.object({
43695
43919
  chain: optionalSwapChainIdentifierField,
@@ -44664,6 +44888,9 @@ const transformAmount = (value, direction, decimals)=>formatUnits(value, decimal
44664
44888
  ...formattedFees && {
44665
44889
  fees: formattedFees
44666
44890
  },
44891
+ ...result.route !== undefined && {
44892
+ route: result.route
44893
+ },
44667
44894
  ...formattedConfig && {
44668
44895
  config: formattedConfig
44669
44896
  },
@@ -44925,6 +45152,9 @@ const transformAmount = (value, direction, decimals)=>formatUnits(value, decimal
44925
45152
  estimatedOutput: providerEstimate.estimatedOutput,
44926
45153
  ...providerEstimate.fees !== undefined && {
44927
45154
  fees: providerEstimate.fees
45155
+ },
45156
+ ...providerEstimate.route !== undefined && {
45157
+ route: providerEstimate.route
44928
45158
  }
44929
45159
  };
44930
45160
  }
@@ -46724,7 +46954,7 @@ registerKit(`${pkg$4.name}/${pkg$4.version}`);
46724
46954
  };
46725
46955
 
46726
46956
  var name$3 = "@circle-fin/earn-kit";
46727
- var version$3 = "1.8.1";
46957
+ var version$3 = "1.9.0";
46728
46958
  var pkg$3 = {
46729
46959
  name: name$3,
46730
46960
  version: version$3};
@@ -48512,6 +48742,187 @@ function isCrossChainDepositStatusStep(step) {
48512
48742
  return step.name === 'status';
48513
48743
  }
48514
48744
 
48745
+ /**
48746
+ * Selector of `TellerWithMultiAssetSupport__SharesAreLocked()`.
48747
+ */ const SHARES_LOCKED_SELECTOR = '0xf64059db';
48748
+ /**
48749
+ * Readable reasons for Veda BoringVault Teller and Accountant reverts.
48750
+ */ const VEDA_REVERT_REASONS = {
48751
+ '0xe0f9e71d': 'The vault is paused',
48752
+ '0x3b3132f6': 'The vault exchange rate is paused',
48753
+ '0xc8bfa33e': 'The vault does not support this asset for this operation',
48754
+ '0x4b37cd2a': 'The asset amount must be greater than zero',
48755
+ '0x3a8c54c4': 'The share amount must be greater than zero',
48756
+ '0x4bd956a8': 'The deposit would mint fewer vault shares than the signed minimum',
48757
+ '0x9883d840': 'The withdrawal would pay out fewer assets than the signed minimum',
48758
+ '0xed32f3bb': 'The deposit would exceed the vault deposit cap',
48759
+ '0x1b8851f0': 'The permit failed and the token allowance is too low',
48760
+ '0x2821264f': 'The vault denied the share transfer'
48761
+ };
48762
+ /**
48763
+ * Matches a 4-byte selector in a message. The lookarounds stop a longer hex
48764
+ * value, such as an address or a hash, from matching.
48765
+ */ const SELECTOR_IN_TEXT = /(?<![0-9a-fx])0x[0-9a-f]{8}(?![0-9a-f])/gi;
48766
+ function toRecord(value) {
48767
+ return typeof value === 'object' && value !== null ? value : {};
48768
+ }
48769
+ function isVedaSelector(selector) {
48770
+ return selector === SHARES_LOCKED_SELECTOR || selector in VEDA_REVERT_REASONS;
48771
+ }
48772
+ /**
48773
+ * Reads the selector from the revert data on a trace. The `/next` adapters
48774
+ * keep it on `revertData`. The default adapters keep the whole raw error on
48775
+ * `rawError`.
48776
+ */ function selectorFromData(trace) {
48777
+ const data = typeof trace['revertData'] === 'string' ? trace['revertData'] : extractRevertData(trace['rawError']);
48778
+ const selector = data?.slice(0, 10).toLowerCase();
48779
+ return selector !== undefined && isVedaSelector(selector) ? selector : undefined;
48780
+ }
48781
+ /**
48782
+ * Reads the selector from error text, for wallets that return no revert
48783
+ * data and put the selector or the decoded error name in the message.
48784
+ */ function selectorFromText(texts) {
48785
+ for (const text of texts){
48786
+ if (typeof text !== 'string') {
48787
+ continue;
48788
+ }
48789
+ if (/SharesAreLocked/i.test(text)) {
48790
+ return SHARES_LOCKED_SELECTOR;
48791
+ }
48792
+ const selector = text.match(SELECTOR_IN_TEXT)?.map((match)=>match.toLowerCase()).find(isVedaSelector);
48793
+ if (selector !== undefined) {
48794
+ return selector;
48795
+ }
48796
+ }
48797
+ return undefined;
48798
+ }
48799
+ /**
48800
+ * Reads a Veda revert from one error, without looking at nested errors.
48801
+ */ function readVedaRevert(error) {
48802
+ const trace = toRecord(error.cause?.trace);
48803
+ const selector = selectorFromData(trace) ?? selectorFromText([
48804
+ error.message,
48805
+ trace['reason']
48806
+ ]);
48807
+ if (selector === undefined) {
48808
+ return undefined;
48809
+ }
48810
+ const chain = trace['chain'];
48811
+ return {
48812
+ selector,
48813
+ chain: typeof chain === 'string' ? chain : undefined
48814
+ };
48815
+ }
48816
+ /**
48817
+ * Returns the errors that a failed batch nests under `trace.cause` or
48818
+ * `trace.error`.
48819
+ */ function nestedErrors(error) {
48820
+ const trace = toRecord(error.cause?.trace);
48821
+ return [
48822
+ trace['cause'],
48823
+ trace['error']
48824
+ ].filter(isKitError);
48825
+ }
48826
+ /**
48827
+ * Finds a Veda revert on the error or on an error nested in its trace.
48828
+ */ function findVedaRevert(error) {
48829
+ const seen = new Set();
48830
+ const pending = [
48831
+ error
48832
+ ];
48833
+ let current = pending.pop();
48834
+ while(current !== undefined){
48835
+ if (!seen.has(current)) {
48836
+ seen.add(current);
48837
+ const revert = readVedaRevert(current);
48838
+ if (revert !== undefined) {
48839
+ return {
48840
+ ...revert,
48841
+ nested: current !== error
48842
+ };
48843
+ }
48844
+ pending.push(...nestedErrors(current));
48845
+ }
48846
+ current = pending.pop();
48847
+ }
48848
+ return undefined;
48849
+ }
48850
+ /**
48851
+ * Map a Veda vault revert to an Earn error.
48852
+ *
48853
+ * A Teller `SharesAreLocked()` revert becomes `EARN_SHARES_LOCKED`. Other
48854
+ * known Veda reverts keep their simulation failed or transaction reverted
48855
+ * code and get a readable reason. A failed batch that wraps a Veda revert
48856
+ * keeps its code, and the reason is added to its message. A timed out batch
48857
+ * is returned unchanged, because its outcome is unknown. Any other error is
48858
+ * returned unchanged.
48859
+ *
48860
+ * @param error - The error caught from an earn operation.
48861
+ * @returns The mapped error, or `error` when it is not a Veda revert.
48862
+ *
48863
+ * @example
48864
+ * ```typescript
48865
+ * import { mapVedaRevert } from './vedaRevert'
48866
+ *
48867
+ * const mapped = mapVedaRevert(error)
48868
+ * console.log(mapped.name) // 'EARN_SHARES_LOCKED' for a share lock revert
48869
+ * ```
48870
+ *
48871
+ * @internal
48872
+ */ function mapVedaRevert(error) {
48873
+ if (error.name === NetworkError.TIMEOUT.name) {
48874
+ return error;
48875
+ }
48876
+ const revert = findVedaRevert(error);
48877
+ if (revert === undefined) {
48878
+ return error;
48879
+ }
48880
+ const trace = toRecord(error.cause?.trace);
48881
+ const chain = typeof trace['chain'] === 'string' ? trace['chain'] : revert.chain ?? 'EVM';
48882
+ if (revert.selector === SHARES_LOCKED_SELECTOR) {
48883
+ return new KitError({
48884
+ ...EarnError.SHARES_LOCKED,
48885
+ // The lock can last hours, so an immediate retry fails the same way.
48886
+ recoverability: 'FATAL',
48887
+ message: `Vault shares on ${chain} are still locked after a recent deposit. Wait for the vault's share lock period to end, then try again.`,
48888
+ cause: {
48889
+ trace: {
48890
+ ...trace,
48891
+ selector: revert.selector,
48892
+ chain
48893
+ }
48894
+ }
48895
+ });
48896
+ }
48897
+ const readable = VEDA_REVERT_REASONS[revert.selector];
48898
+ if (readable === undefined) {
48899
+ return error;
48900
+ }
48901
+ const reason = `${readable} (${revert.selector})`;
48902
+ if (error.name === OnchainError.SIMULATION_FAILED.name) {
48903
+ return createSimulationFailedError(chain, reason, trace);
48904
+ }
48905
+ if (error.name === OnchainError.TRANSACTION_REVERTED.name) {
48906
+ return createTransactionRevertedError(chain, reason, trace);
48907
+ }
48908
+ if (!revert.nested) {
48909
+ return error;
48910
+ }
48911
+ return new KitError({
48912
+ code: error.code,
48913
+ name: error.name,
48914
+ type: error.type,
48915
+ recoverability: error.recoverability,
48916
+ message: `${error.message} Reason: ${reason}.`,
48917
+ cause: {
48918
+ trace: {
48919
+ ...trace,
48920
+ reason
48921
+ }
48922
+ }
48923
+ });
48924
+ }
48925
+
48515
48926
  const EARN_OPERATIONS = new Set([
48516
48927
  'deposit',
48517
48928
  'withdraw',
@@ -48557,6 +48968,10 @@ const EARN_OPERATIONS = new Set([
48557
48968
  * `RESUMABLE` so callers know `retry()` can resume from the failed phase. A
48558
48969
  * fatal underlying error stays fatal — it cannot be resumed.
48559
48970
  *
48971
+ * A Veda vault revert is mapped first (see {@link mapVedaRevert}): a share
48972
+ * lock becomes `EARN_SHARES_LOCKED`, and other known Veda reverts get a
48973
+ * readable reason.
48974
+ *
48560
48975
  * Non-{@link KitError} inputs (which should not occur on provider code paths)
48561
48976
  * are normalized to a retryable {@link ServiceError.INTERNAL_ERROR}.
48562
48977
  *
@@ -48578,7 +48993,7 @@ const EARN_OPERATIONS = new Set([
48578
48993
  * }
48579
48994
  * ```
48580
48995
  */ function augmentEarnError(error, context) {
48581
- const base = isKitError(error) ? error : new KitError({
48996
+ const base = isKitError(error) ? mapVedaRevert(error) : new KitError({
48582
48997
  ...ServiceError.INTERNAL_ERROR,
48583
48998
  recoverability: 'RETRYABLE',
48584
48999
  message: getErrorMessage(error)
@@ -49521,7 +49936,7 @@ const bridgeDepositPrepareReviewSchema = zod.z.object({
49521
49936
  }
49522
49937
 
49523
49938
  var name$2 = "@circle-fin/provider-earn-service";
49524
- var version$2 = "1.7.1";
49939
+ var version$2 = "1.7.2";
49525
49940
  var pkg$2 = {
49526
49941
  name: name$2,
49527
49942
  version: version$2};
@@ -50782,7 +51197,7 @@ function reportTransactionFailure(reportContext, action, error) {
50782
51197
  }
50783
51198
  function transactionReportErrorCode(error) {
50784
51199
  if (isKitError(error)) {
50785
- return error.name;
51200
+ return mapVedaRevert(error).name;
50786
51201
  }
50787
51202
  const message = getErrorMessage(error);
50788
51203
  if (/user (rejected|denied)|rejected by user/i.test(message)) {
@@ -54151,7 +54566,7 @@ registerKit(`${pkg$3.name}/${pkg$3.version}`);
54151
54566
  };
54152
54567
 
54153
54568
  var name$1 = "@circle-fin/borrow-kit";
54154
- var version$1 = "1.0.0";
54569
+ var version$1 = "1.1.0";
54155
54570
  var pkg$1 = {
54156
54571
  name: name$1,
54157
54572
  version: version$1};
@@ -64875,7 +65290,7 @@ function normaliseLoadTimeout(value) {
64875
65290
  currency: zod.z.string().min(1).optional(),
64876
65291
  email: zod.z.string().email().optional(),
64877
65292
  assets: onrampAssetSelectionSchema.optional(),
64878
- metadata: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
65293
+ metadata: zod.z.record(zod.z.string(), zod.z.string()).optional()
64879
65294
  });
64880
65295
 
64881
65296
  /**
@@ -67060,7 +67475,7 @@ async function deposit$2(context, params) {
67060
67475
  }
67061
67476
 
67062
67477
  var name = "@circle-fin/unified-balance-kit";
67063
- var version = "1.8.0";
67478
+ var version = "1.8.1";
67064
67479
  var pkg = {
67065
67480
  name: name,
67066
67481
  version: version};