@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/earn.cjs CHANGED
@@ -749,6 +749,11 @@ class KitError extends Error {
749
749
  code: 5001,
750
750
  name: 'ONCHAIN_TRANSACTION_REVERTED',
751
751
  type: 'ONCHAIN'
752
+ },
753
+ /** Pre-flight transaction simulation failed */ SIMULATION_FAILED: {
754
+ code: 5002,
755
+ name: 'ONCHAIN_SIMULATION_FAILED',
756
+ type: 'ONCHAIN'
752
757
  }};
753
758
  /**
754
759
  * Standardized error definitions for LIQUIDITY type errors.
@@ -1171,6 +1176,49 @@ class KitError extends Error {
1171
1176
  return new KitError(errorDetails);
1172
1177
  }
1173
1178
 
1179
+ /**
1180
+ * Creates error for transaction simulation failures.
1181
+ *
1182
+ * This error is thrown when a pre-flight transaction simulation fails,
1183
+ * typically due to contract logic that would revert. The error is FATAL
1184
+ * as it indicates the transaction would fail if submitted.
1185
+ *
1186
+ * @param chain - The blockchain network where the simulation failed
1187
+ * @param reason - The reason for simulation failure (e.g., revert message)
1188
+ * @param trace - Optional trace context to include in error (can include rawError and additional debugging data)
1189
+ * @returns KitError with simulation failure details
1190
+ *
1191
+ * @example
1192
+ * ```typescript
1193
+ * import { createSimulationFailedError } from '@core/errors'
1194
+ *
1195
+ * throw createSimulationFailedError('Ethereum', 'ERC20: insufficient allowance')
1196
+ * // Message: "Simulation failed on Ethereum: ERC20: insufficient allowance"
1197
+ * ```
1198
+ *
1199
+ * @example
1200
+ * ```typescript
1201
+ * // With trace context for debugging
1202
+ * throw createSimulationFailedError('Ethereum', 'ERC20: insufficient allowance', {
1203
+ * rawError: error,
1204
+ * txHash: '0x1234...',
1205
+ * gasLimit: '21000',
1206
+ * })
1207
+ * ```
1208
+ */ function createSimulationFailedError(chain, reason, trace) {
1209
+ return new KitError({
1210
+ ...OnchainError.SIMULATION_FAILED,
1211
+ recoverability: 'FATAL',
1212
+ message: `Simulation failed on ${chain}: ${reason}`,
1213
+ cause: {
1214
+ trace: {
1215
+ ...trace,
1216
+ chain,
1217
+ reason
1218
+ }
1219
+ }
1220
+ });
1221
+ }
1174
1222
  /**
1175
1223
  * Creates error for transaction reverts.
1176
1224
  *
@@ -1225,6 +1273,82 @@ class KitError extends Error {
1225
1273
  });
1226
1274
  }
1227
1275
 
1276
+ /**
1277
+ * Matches revert data: a 4-byte selector followed by whole ABI-encoded bytes.
1278
+ */ const REVERT_DATA_PATTERN = /^0x[0-9a-f]{8}(?:[0-9a-f]{2})*$/i;
1279
+ /**
1280
+ * Returns the revert data held by one error in a `cause` chain.
1281
+ *
1282
+ * Browser wallets (EIP-1193) wrap the node's error, so the revert data sits
1283
+ * one level down as `data.data`. viem's `getContractError` reads it the same
1284
+ * way, then keeps it on `ContractFunctionRevertedError.raw` and drops the
1285
+ * node's error from the chain.
1286
+ *
1287
+ * @param link - One error in the `cause` chain
1288
+ * @returns The `raw` or `data` string, or undefined if there is none
1289
+ */ function getRevertData(link) {
1290
+ if ('raw' in link && typeof link.raw === 'string' && REVERT_DATA_PATTERN.test(link.raw)) {
1291
+ return link.raw;
1292
+ }
1293
+ if (!('data' in link)) {
1294
+ return undefined;
1295
+ }
1296
+ const { data } = link;
1297
+ if (typeof data === 'string') {
1298
+ return data;
1299
+ }
1300
+ if (data !== null && typeof data === 'object' && 'data' in data && typeof data.data === 'string') {
1301
+ return data.data;
1302
+ }
1303
+ return undefined;
1304
+ }
1305
+ /**
1306
+ * Finds the first revert data on the error or in its `cause` chain that
1307
+ * `accept` takes.
1308
+ *
1309
+ * viem keeps the node's revert data on an inner `RpcRequestError`, not on
1310
+ * the error it throws. The `seen` set stops a self-referencing `cause` chain.
1311
+ *
1312
+ * @param error - The error object
1313
+ * @param accept - Returns true for the data shape the caller wants
1314
+ * @returns The data as written on the error, or undefined if not found
1315
+ */ function findRevertData(error, accept) {
1316
+ const seen = new Set();
1317
+ let current = error;
1318
+ while(current !== null && typeof current === 'object' && !seen.has(current)){
1319
+ seen.add(current);
1320
+ const data = getRevertData(current);
1321
+ if (data !== undefined && accept(data)) {
1322
+ return data;
1323
+ }
1324
+ current = 'cause' in current ? current.cause : undefined;
1325
+ }
1326
+ return undefined;
1327
+ }
1328
+ /**
1329
+ * Finds the raw revert data on an error or anywhere in its `cause` chain.
1330
+ *
1331
+ * Reads `data`, the nested `data.data` from browser wallets, and viem's
1332
+ * `raw`. Only hex of at least 4 bytes counts, so decoded `data` objects and
1333
+ * empty `0x` data are skipped.
1334
+ *
1335
+ * Revert data is public: it is what the contract returned. Keep it when the
1336
+ * rest of a raw error is redacted, so callers can still decode the revert.
1337
+ *
1338
+ * @param error - The raw error from the blockchain library
1339
+ * @returns The revert data in lowercase, or undefined if there is none
1340
+ *
1341
+ * @example
1342
+ * ```typescript
1343
+ * import { extractRevertData } from '@core/errors'
1344
+ *
1345
+ * extractRevertData({ cause: { data: { data: '0xF64059DB' } } })
1346
+ * // Returns: '0xf64059db'
1347
+ * ```
1348
+ */ function extractRevertData(error) {
1349
+ return findRevertData(error, (data)=>REVERT_DATA_PATTERN.test(data))?.toLowerCase();
1350
+ }
1351
+
1228
1352
  /**
1229
1353
  * Type guard to check if an error is a KitError instance.
1230
1354
  *
@@ -2010,7 +2134,7 @@ class KitError extends Error {
2010
2134
  * between input errors (fix your request) and service errors (retry later).
2011
2135
  *
2012
2136
  * Error code ranges:
2013
- * - 1100-1106: INPUT errors — invalid, unsupported, or stale request state
2137
+ * - 1100-1107: INPUT errors — invalid, unsupported, or stale request state
2014
2138
  * - 8100-8105: SERVICE errors — retryable backend/provider failures
2015
2139
  *
2016
2140
  * @example
@@ -2071,6 +2195,15 @@ class KitError extends Error {
2071
2195
  name: 'EARN_BRIDGE_QUOTE_EXPIRED',
2072
2196
  type: 'INPUT'
2073
2197
  },
2198
+ /**
2199
+ * The wallet's vault shares are inside the vault's share lock period, which
2200
+ * starts at deposit. Surfaced as FATAL rather than RETRYABLE because the
2201
+ * lock can last hours, so an immediate retry fails the same way.
2202
+ */ SHARES_LOCKED: {
2203
+ code: 1107,
2204
+ name: 'EARN_SHARES_LOCKED',
2205
+ type: 'INPUT'
2206
+ },
2074
2207
  /** The proxy signing call failed — retryable. */ SIGNING_FAILED: {
2075
2208
  code: 8100,
2076
2209
  name: 'EARN_SIGNING_FAILED',
@@ -2312,6 +2445,15 @@ class KitError extends Error {
2312
2445
  recoverability: 'RETRYABLE'
2313
2446
  }
2314
2447
  ],
2448
+ // DEPOSIT_INTENT_INVALID: the Veda deposit intent is missing, expired, or
2449
+ // not signed by the depositing wallet.
2450
+ [
2451
+ 380418,
2452
+ {
2453
+ errorDef: EarnError.SIGNATURE_REJECTED,
2454
+ recoverability: 'FATAL'
2455
+ }
2456
+ ],
2315
2457
  // Bridge (380_5XX)
2316
2458
  [
2317
2459
  380500,
@@ -2543,6 +2685,8 @@ var SwapChain;
2543
2685
  SwapChain["HyperEVM"] = "HyperEVM";
2544
2686
  SwapChain["Monad"] = "Monad";
2545
2687
  SwapChain["Arc"] = "Arc";
2688
+ SwapChain["Cronos"] = "Cronos";
2689
+ SwapChain["Morph"] = "Morph";
2546
2690
  // Testnet chains with swap support
2547
2691
  SwapChain["Arc_Testnet"] = "Arc_Testnet";
2548
2692
  })(SwapChain || (SwapChain = {}));
@@ -3223,14 +3367,14 @@ var BorrowChain;
3223
3367
  symbol: 'USDC',
3224
3368
  // Arc uses native USDC with 18 decimals for gas payments (EVM standard).
3225
3369
  // Note: The ERC-20 USDC contract at usdcAddress uses 6 decimals.
3226
- // See: https://docs.arc.network/arc/references/contract-addresses
3370
+ // See: https://docs.arc.io/arc/references/contract-addresses
3227
3371
  decimals: 18
3228
3372
  },
3229
3373
  chainId: 5042002,
3230
3374
  isTestnet: true,
3231
3375
  explorerUrl: 'https://testnet.arcscan.app/tx/{hash}',
3232
3376
  rpcEndpoints: [
3233
- 'https://rpc.testnet.arc.network/'
3377
+ 'https://rpc.testnet.arc.io'
3234
3378
  ],
3235
3379
  eurcAddress: '0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a',
3236
3380
  usdcAddress: '0x3600000000000000000000000000000000000000',
@@ -3875,7 +4019,8 @@ var BorrowChain;
3875
4019
  }
3876
4020
  },
3877
4021
  kitContracts: {
3878
- bridge: BRIDGE_CONTRACT_EVM_MAINNET
4022
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
4023
+ adapter: ADAPTER_CONTRACT_EVM_MAINNET
3879
4024
  }
3880
4025
  });
3881
4026
 
@@ -4744,7 +4889,8 @@ var BorrowChain;
4744
4889
  }
4745
4890
  },
4746
4891
  kitContracts: {
4747
- bridge: BRIDGE_CONTRACT_EVM_MAINNET
4892
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
4893
+ adapter: ADAPTER_CONTRACT_EVM_MAINNET
4748
4894
  }
4749
4895
  });
4750
4896
 
@@ -6634,6 +6780,56 @@ var Chains = {
6634
6780
  Blockchain.World_Chain_Sepolia
6635
6781
  ]);
6636
6782
 
6783
+ /**
6784
+ * Check if a chain supports a specific type of custom smart contract logic.
6785
+ *
6786
+ * This function examines a chain definition to determine if it has a specific
6787
+ * contract type configured. When a chain has custom contract support, it can
6788
+ * use enhanced features and custom business logic in addition to the standard
6789
+ * CCTP approve/burn/mint flow.
6790
+ *
6791
+ * @param chain - The chain definition to examine for custom contract support.
6792
+ * @param contractType - The type of contract to check for (e.g., 'bridge').
6793
+ * @returns True if the chain supports the specified contract type with a valid address configured.
6794
+ *
6795
+ * @example
6796
+ * ```typescript
6797
+ * import { hasCustomContractSupport, Ethereum } from '@core/chains'
6798
+ *
6799
+ * // Check if a chain supports bridge contracts
6800
+ * if (hasCustomContractSupport(Ethereum, 'bridge')) {
6801
+ * // Runtime validation confirmed kitContracts.bridge exists
6802
+ * console.log('Bridge contract:', Ethereum.kitContracts?.bridge)
6803
+ * return executeCustomFlow(Ethereum.kitContracts?.bridge!, params)
6804
+ * } else {
6805
+ * console.log('Using standard CCTP flow')
6806
+ * return executeStandardFlow(params)
6807
+ * }
6808
+ * ```
6809
+ *
6810
+ * @example
6811
+ * ```typescript
6812
+ * // Usage in flow determination
6813
+ * function determineBridgingFlow(chain: ChainDefinition, params: BridgeParams) {
6814
+ * if (hasCustomContractSupport(chain, 'bridge')) {
6815
+ * return 'custom'
6816
+ * } else if (isCCTPV2Supported(chain)) {
6817
+ * return 'standard'
6818
+ * } else {
6819
+ * throw new Error(`Chain ${chain.name} is not supported for bridging`)
6820
+ * }
6821
+ * }
6822
+ *
6823
+ * // Future usage with other contract types (when added to KitContractType)
6824
+ * // if (hasCustomContractSupport(someChain, 'customType')) {
6825
+ * // console.log('Custom contract:', someChain.kitContracts?.customType)
6826
+ * // }
6827
+ * ```
6828
+ */ function hasCustomContractSupport(chain, contractType) {
6829
+ const contractAddress = chain.kitContracts?.[contractType];
6830
+ return typeof contractAddress === 'string' && contractAddress.trim().length > 0;
6831
+ }
6832
+
6637
6833
  /**
6638
6834
  * Temporary allowlist of chains permitted to initiate Gateway fast deposits.
6639
6835
  * Only chains keyed here are eligible; all others are rejected. Using the
@@ -6887,8 +7083,8 @@ var Chains = {
6887
7083
  * Zod schema for validating swap-specific chain identifiers.
6888
7084
  *
6889
7085
  * Validates chains based on:
6890
- * - CCTPv2 support (adapter contract deployed)
6891
- * - Mainnet only (no testnets)
7086
+ * - Membership in the SwapChain enum
7087
+ * - Mainnet or explicitly allowlisted testnet
6892
7088
  * - At least one supported token available
6893
7089
  *
6894
7090
  */ const swapChainIdentifierSchema = zod.z.union([
@@ -7293,7 +7489,7 @@ const swapTokenEnumSchema = zod.z.enum([
7293
7489
  *
7294
7490
  * A chain supports swaps if ALL conditions are met:
7295
7491
  * 1. Is a member of the {@link SwapChain} enum
7296
- * 2. Has CCTPv2 support (adapter contract deployed)
7492
+ * 2. Has an Adapter contract configured directly or through CCTP v2
7297
7493
  *
7298
7494
  * Testnets are allowed only when explicitly listed in the
7299
7495
  * {@link SwapChain} enum (e.g., Arc_Testnet).
@@ -7314,14 +7510,14 @@ const swapTokenEnumSchema = zod.z.enum([
7314
7510
  * @remarks
7315
7511
  * Chains automatically become swap-enabled when:
7316
7512
  * - Added to the {@link SwapChain} enum
7317
- * - Adapter contract is deployed (CCTPv2)
7513
+ * - An Adapter contract is deployed and configured
7318
7514
  *
7319
7515
  * No code changes needed when new chains are added!
7320
7516
  */ function isSwapSupportedChain(chain) {
7321
7517
  if (!(chain.chain in SwapChain)) {
7322
7518
  return false;
7323
7519
  }
7324
- if (!isCCTPV2Supported(chain)) {
7520
+ if (!isCCTPV2Supported(chain) && !hasCustomContractSupport(chain, 'adapter')) {
7325
7521
  return false;
7326
7522
  }
7327
7523
  return true;
@@ -7332,7 +7528,7 @@ const swapTokenEnumSchema = zod.z.enum([
7332
7528
  *
7333
7529
  * Dynamically filter chain definitions to include only those that:
7334
7530
  * - Are members of the {@link SwapChain} enum
7335
- * - Have CCTPv2 support
7531
+ * - Have an Adapter contract configured directly or through CCTP v2
7336
7532
  *
7337
7533
  * This function is pure and accepts chains as parameter to avoid
7338
7534
  * circular dependencies.
@@ -8205,11 +8401,13 @@ const swapTokenEnumSchema = zod.z.enum([
8205
8401
  [Blockchain.Arbitrum]: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9',
8206
8402
  [Blockchain.Avalanche]: '0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7',
8207
8403
  [Blockchain.Celo]: '0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e',
8404
+ [Blockchain.Cronos]: '0x66e428c3f67a68878562e79A0234c1F83c208770',
8208
8405
  [Blockchain.Ethereum]: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
8209
8406
  [Blockchain.HyperEVM]: '0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb',
8210
8407
  [Blockchain.Ink]: '0x0200C29006150606B650577BBE7B6248F58470c1',
8211
8408
  [Blockchain.Linea]: '0xA219439258ca9da29E9Cc4cE5596924745e12B93',
8212
8409
  [Blockchain.Monad]: '0xe7cd86e13AC4309349F30B3435a9d337750fC82D',
8410
+ [Blockchain.Morph]: '0xe7cd86e13AC4309349F30B3435a9d337750fC82D',
8213
8411
  [Blockchain.NEAR]: 'usdt.tether-token.near',
8214
8412
  [Blockchain.Optimism]: '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58',
8215
8413
  [Blockchain.Polkadot_Asset_Hub]: '1984',
@@ -8268,6 +8466,7 @@ const swapTokenEnumSchema = zod.z.enum([
8268
8466
  [Blockchain.Arbitrum]: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1',
8269
8467
  [Blockchain.Avalanche]: '0xd586E7F844cEa2F87f50152665BCbc2C279D8d70',
8270
8468
  [Blockchain.Base]: '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb',
8469
+ [Blockchain.Cronos]: '0xF2001B145b43032AAF5Ee2884e456CCd805F677D',
8271
8470
  [Blockchain.Ethereum]: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
8272
8471
  [Blockchain.Linea]: '0x4AF15ec2A0BD43Db75dd04E62FAA3B8EF36b00d5',
8273
8472
  [Blockchain.Optimism]: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1',
@@ -8330,7 +8529,9 @@ const swapTokenEnumSchema = zod.z.enum([
8330
8529
  [Blockchain.Arbitrum]: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
8331
8530
  [Blockchain.Avalanche]: '0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB',
8332
8531
  [Blockchain.Base]: '0x4200000000000000000000000000000000000006',
8532
+ [Blockchain.Cronos]: '0xe44Fd7fCb2b1581822D0c862B68222998a0c299a',
8333
8533
  [Blockchain.Ethereum]: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
8534
+ [Blockchain.Morph]: '0x5300000000000000000000000000000000000011',
8334
8535
  [Blockchain.Optimism]: '0x4200000000000000000000000000000000000006',
8335
8536
  [Blockchain.Polygon]: '0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619',
8336
8537
  [Blockchain.Solana]: '7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs'
@@ -8349,6 +8550,7 @@ const swapTokenEnumSchema = zod.z.enum([
8349
8550
  locators: {
8350
8551
  [Blockchain.Ethereum]: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599',
8351
8552
  [Blockchain.Arbitrum]: '0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f',
8553
+ [Blockchain.Cronos]: '0x062E66477Faf219F25D27dCED647BF57C3107d52',
8352
8554
  [Blockchain.Avalanche]: '0x50b7545627a5162F82A992c33b87aDc75187B218'
8353
8555
  }
8354
8556
  };
@@ -9367,7 +9569,7 @@ const swapTokenEnumSchema = zod.z.enum([
9367
9569
  }
9368
9570
 
9369
9571
  var name$4 = "@circle-fin/bridge-kit";
9370
- var version$4 = "1.15.2";
9572
+ var version$4 = "1.16.0";
9371
9573
  var pkg$4 = {
9372
9574
  name: name$4,
9373
9575
  version: version$4};
@@ -11163,7 +11365,7 @@ const validateQuoteItemSchema = zod.z.object({
11163
11365
  registerKit(`${pkg$4.name}/${pkg$4.version}`);
11164
11366
 
11165
11367
  var name$3 = "@circle-fin/swap-kit";
11166
- var version$3 = "1.7.1";
11368
+ var version$3 = "1.8.1";
11167
11369
  var pkg$3 = {
11168
11370
  name: name$3,
11169
11371
  version: version$3};
@@ -11505,6 +11707,64 @@ const optionalChainIdentifierField = chainIdentifierField.optional();
11505
11707
  }, {
11506
11708
  message: 'fee amount must be non-negative (>= 0)'
11507
11709
  });
11710
+ /**
11711
+ * Parses a display-only field leniently: a value `schema` rejects is replaced
11712
+ * with `fallback` instead of failing the response that carries it.
11713
+ * Implemented with `preprocess` rather than Zod's `.catch()` because static
11714
+ * analysis misreads `.catch` on the schema chain as an unhandled Promise
11715
+ * (S7785).
11716
+ */ const withFallback = (schema, fallback)=>zod.z.preprocess((value)=>schema.safeParse(value).success ? value : fallback, schema);
11717
+ const swapRouteTokenSchema = zod.z.object({
11718
+ symbol: zod.z.string(),
11719
+ address: zod.z.string(),
11720
+ decimals: zod.z.number()
11721
+ });
11722
+ const swapRouteMetadataSchema = zod.z.record(zod.z.unknown()).optional();
11723
+ const swapRouteStepSchema = zod.z.object({
11724
+ type: zod.z.string(),
11725
+ tool: zod.z.string(),
11726
+ toolName: zod.z.string(),
11727
+ fromToken: swapRouteTokenSchema,
11728
+ toToken: swapRouteTokenSchema,
11729
+ estimate: zod.z.object({
11730
+ fromAmount: zod.z.string(),
11731
+ toAmount: zod.z.string(),
11732
+ gasCostUSD: zod.z.string().optional()
11733
+ }),
11734
+ metadata: swapRouteMetadataSchema
11735
+ });
11736
+ /**
11737
+ * Strict schema for a `SwapRoute`. Module-private: service responses
11738
+ * parse routes leniently through {@link responseSwapRouteSchema}.
11739
+ */ const swapRouteSchema = zod.z.object({
11740
+ provider: zod.z.string().min(1, 'provider must be a non-empty string'),
11741
+ steps: zod.z.array(swapRouteStepSchema),
11742
+ metadata: swapRouteMetadataSchema
11743
+ });
11744
+ /**
11745
+ * Route details are display-only, so service responses parse them leniently:
11746
+ * steps or metadata in a shape the SDK does not know are dropped rather than
11747
+ * failing the quote or swap they describe. Steps are all-or-nothing: if any
11748
+ * step is malformed, `steps` is empty, so a route never silently loses a hop.
11749
+ */ const responseSwapRouteSchema = swapRouteSchema.extend({
11750
+ steps: withFallback(swapRouteSchema.shape.steps, []),
11751
+ metadata: withFallback(swapRouteMetadataSchema, undefined)
11752
+ });
11753
+ const createSwapFeeItemSchema = zod.z.object({
11754
+ token: zod.z.string({
11755
+ required_error: 'fee token is required',
11756
+ invalid_type_error: 'fee token must be a string'
11757
+ }).min(1, 'fee token must be a non-empty string'),
11758
+ amount: feeAmountSchema,
11759
+ decimals: zod.z.number().int('fee token decimals must be an integer').nonnegative('fee token decimals must be a non-negative integer').optional(),
11760
+ symbol: zod.z.string({
11761
+ invalid_type_error: 'fee token symbol must be a string'
11762
+ }).min(1, 'fee token symbol must be a non-empty string').optional(),
11763
+ // Descriptive metadata: a value the SDK cannot use is dropped rather than
11764
+ // failing the response that carries the fee.
11765
+ type: withFallback(zod.z.string().min(1).optional(), undefined),
11766
+ chain: withFallback(zod.z.string().min(1).optional(), undefined)
11767
+ });
11508
11768
  /**
11509
11769
  * Zod schema for validating API keys.
11510
11770
  *
@@ -11590,10 +11850,7 @@ const optionalChainIdentifierField = chainIdentifierField.optional();
11590
11850
  /** Quote information with estimated amounts and route */ quote: zod.z.object({
11591
11851
  /** 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'),
11592
11852
  /** 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'),
11593
- /** Route information from DEX aggregator */ route: zod.z.object({
11594
- provider: zod.z.string().min(1, 'provider must be a non-empty string'),
11595
- steps: zod.z.array(zod.z.unknown())
11596
- })
11853
+ /** Route information from DEX aggregator */ route: responseSwapRouteSchema
11597
11854
  }),
11598
11855
  /** Fee context indicating where fees should be collected */ feeContext: quoteFeeContextSchema
11599
11856
  });
@@ -11809,19 +12066,6 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
11809
12066
  }).min(1, 'toChain must be a non-empty string if provided').optional(),
11810
12067
  apiKey: apiKeySchema.optional()
11811
12068
  });
11812
- /**
11813
- * Zod schema for validating CreateSwapResponse payloads.
11814
- */ const createSwapFeeItemSchema = zod.z.object({
11815
- token: zod.z.string({
11816
- required_error: 'fee token is required',
11817
- invalid_type_error: 'fee token must be a string'
11818
- }).min(1, 'fee token must be a non-empty string'),
11819
- amount: feeAmountSchema,
11820
- decimals: zod.z.number().int('fee token decimals must be an integer').nonnegative('fee token decimals must be a non-negative integer').optional(),
11821
- symbol: zod.z.string({
11822
- invalid_type_error: 'fee token symbol must be a string'
11823
- }).min(1, 'fee token symbol must be a non-empty string').optional()
11824
- });
11825
12069
  /**
11826
12070
  * Developer fee item schema with basis field.
11827
12071
  */ const createSwapDeveloperFeeItemSchema = createSwapFeeItemSchema.extend({
@@ -11959,6 +12203,8 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
11959
12203
  correlationId: zod.z.preprocess((value)=>zod.z.string().uuid().safeParse(value).success ? value : undefined, zod.z.string().optional()),
11960
12204
  config: createSwapRequestBaseSchema.shape.config.optional(),
11961
12205
  fees: createSwapFeesSchema.optional(),
12206
+ // A route the SDK cannot parse is dropped instead of failing the swap.
12207
+ route: withFallback(responseSwapRouteSchema.optional(), undefined),
11962
12208
  transaction: createSwapTransactionSchema
11963
12209
  });
11964
12210
 
@@ -14380,6 +14626,47 @@ const abiParameterSchema = zod.z.object({
14380
14626
  */ function hasSignTypedData(adapter) {
14381
14627
  return typeof adapter === 'object' && adapter !== null && 'signTypedData' in adapter && typeof adapter.signTypedData === 'function';
14382
14628
  }
14629
+ /**
14630
+ * Type guard to check if an adapter can actually produce an EIP-712
14631
+ * typed-data signature.
14632
+ *
14633
+ * @remarks
14634
+ * Strengthens {@link hasSignTypedData}: having a `signTypedData` method
14635
+ * does not guarantee it can succeed. Adapters whose signer is delegated
14636
+ * (e.g. through a signing strategy backed by a smart contract account)
14637
+ * expose the method but reject typed-data payloads at runtime. Such
14638
+ * adapters report their real capability through an optional
14639
+ * `supportsSignTypedData()` method, which this guard consults when
14640
+ * present. Adapters without the capability method are assumed able to
14641
+ * sign, preserving the previous duck-typing behavior.
14642
+ *
14643
+ * @param adapter - The adapter to check
14644
+ * @returns True if calling `signTypedData` can be expected to succeed
14645
+ *
14646
+ * @example
14647
+ * ```typescript
14648
+ * import { canSignTypedData } from '@core/adapter-evm'
14649
+ *
14650
+ * if (canSignTypedData(adapter)) {
14651
+ * const signature = await adapter.signTypedData(typedData, context)
14652
+ * } else {
14653
+ * // take an on-chain approval path instead of a permit signature
14654
+ * }
14655
+ * ```
14656
+ */ function canSignTypedData(adapter) {
14657
+ if (!hasSignTypedData(adapter)) {
14658
+ return false;
14659
+ }
14660
+ if (typeof adapter.supportsSignTypedData === 'function') {
14661
+ // The value is `boolean` per the interface, but a plain-JS adapter may
14662
+ // return anything; treat it as untrusted and coerce to a strict
14663
+ // boolean. Comparing an `unknown` (not a `boolean`) also keeps the
14664
+ // lint autofix from stripping this as a redundant `=== true`.
14665
+ const supported = adapter.supportsSignTypedData();
14666
+ return supported === true;
14667
+ }
14668
+ return true;
14669
+ }
14383
14670
 
14384
14671
  /** Inputs for {@link checkErc3009ValidityWindow}. */ /**
14385
14672
  * Check an ERC-3009 authorization validity window.
@@ -14921,6 +15208,16 @@ createTokenRegistry();
14921
15208
  name: 'USDC',
14922
15209
  version: '2'
14923
15210
  },
15211
+ Cronos: {
15212
+ chainId: Cronos.chainId,
15213
+ name: 'USDC',
15214
+ version: '2'
15215
+ },
15216
+ Morph: {
15217
+ chainId: Morph.chainId,
15218
+ name: 'USDC',
15219
+ version: '2'
15220
+ },
14924
15221
  Arc_Testnet: {
14925
15222
  chainId: ArcTestnet.chainId,
14926
15223
  name: 'USDC',
@@ -15009,6 +15306,7 @@ const optionalSwapChainIdentifierField = swapChainIdentifierField.optional();
15009
15306
  * - customFee: Optional fee configuration
15010
15307
  * - apiKey: Optional credential string
15011
15308
  * - kitKey: Optional credential string (deprecated alias for apiKey)
15309
+ * - batchTransactions: Optional boolean to opt out of atomic batching
15012
15310
  */ const swapConfigSchema = zod.z.object({
15013
15311
  allowanceStrategy: allowanceStrategySchema.optional(),
15014
15312
  slippageBps: zod.z.number().int().min(0).optional(),
@@ -15019,7 +15317,10 @@ const optionalSwapChainIdentifierField = swapChainIdentifierField.optional();
15019
15317
  })(zod.z.string())).optional(),
15020
15318
  customFee: swapCustomFeeSchema.optional(),
15021
15319
  apiKey: zod.z.string().optional(),
15022
- kitKey: zod.z.string().optional()
15320
+ kitKey: zod.z.string().optional(),
15321
+ batchTransactions: zod.z.boolean({
15322
+ invalid_type_error: 'batchTransactions must be a boolean'
15323
+ }).optional()
15023
15324
  });
15024
15325
  const swapDestinationSchema = zod.z.object({
15025
15326
  chain: optionalSwapChainIdentifierField,
@@ -15212,7 +15513,7 @@ new Set(Object.values(Blockchain));
15212
15513
  registerKit(`${pkg$3.name}/${pkg$3.version}`);
15213
15514
 
15214
15515
  var name$2 = "@circle-fin/earn-kit";
15215
- var version$2 = "1.8.1";
15516
+ var version$2 = "1.10.0";
15216
15517
  var pkg$2 = {
15217
15518
  name: name$2,
15218
15519
  version: version$2};
@@ -15319,6 +15620,61 @@ const EARN_BRIDGE_USDC_ERC3009_DOMAINS = {
15319
15620
  *
15320
15621
  * @internal
15321
15622
  */ const EARN_BRIDGE_MAX_VALIDITY_SECONDS = 86_400n;
15623
+ /**
15624
+ * Static fields of the EIP-712 domain for the `DepositIntent` typed-data
15625
+ * intent. `chainId` is excluded here — it must come from the deposit's
15626
+ * actual chain (see {@link buildDepositIntentTypedData}), since a mismatched
15627
+ * `chainId` in the domain separator would make every intent signature
15628
+ * invalid.
15629
+ *
15630
+ * `verifyingContract` is intentionally omitted — the intent is verified
15631
+ * off-chain by the Earn Service, not by a deployed contract.
15632
+ *
15633
+ * @internal
15634
+ */ const DEPOSIT_INTENT_DOMAIN = {
15635
+ name: 'Circle EarnKit',
15636
+ version: '1'
15637
+ };
15638
+ /**
15639
+ * EIP-712 type definition for the `DepositIntent` struct.
15640
+ *
15641
+ * @internal
15642
+ */ const DEPOSIT_INTENT_TYPES = {
15643
+ DepositIntent: [
15644
+ {
15645
+ name: 'wallet',
15646
+ type: 'address'
15647
+ },
15648
+ {
15649
+ name: 'vault',
15650
+ type: 'address'
15651
+ },
15652
+ {
15653
+ name: 'amount',
15654
+ type: 'uint256'
15655
+ },
15656
+ {
15657
+ name: 'deadline',
15658
+ type: 'uint256'
15659
+ }
15660
+ ]
15661
+ };
15662
+ /**
15663
+ * Lifetime of a `DepositIntent` signature, in seconds.
15664
+ *
15665
+ * The Earn Service rejects a deadline more than 300 seconds ahead of its own
15666
+ * clock. 240 seconds leaves room for clock skew between the client and the
15667
+ * service while still covering the deposit request that follows the
15668
+ * signature.
15669
+ *
15670
+ * @internal
15671
+ */ const DEPOSIT_INTENT_TTL_SECONDS = 240;
15672
+ /**
15673
+ * Protocol value the Earn Service reports for Veda vaults. A deposit into a
15674
+ * Veda vault needs a signed `DepositIntent`.
15675
+ *
15676
+ * @internal
15677
+ */ const VEDA_PROTOCOL = 'veda';
15322
15678
  /**
15323
15679
  * Display symbol for vault share tokens.
15324
15680
  *
@@ -16962,6 +17318,187 @@ function isCrossChainDepositStatusStep(step) {
16962
17318
  return step.name === 'status';
16963
17319
  }
16964
17320
 
17321
+ /**
17322
+ * Selector of `TellerWithMultiAssetSupport__SharesAreLocked()`.
17323
+ */ const SHARES_LOCKED_SELECTOR = '0xf64059db';
17324
+ /**
17325
+ * Readable reasons for Veda BoringVault Teller and Accountant reverts.
17326
+ */ const VEDA_REVERT_REASONS = {
17327
+ '0xe0f9e71d': 'The vault is paused',
17328
+ '0x3b3132f6': 'The vault exchange rate is paused',
17329
+ '0xc8bfa33e': 'The vault does not support this asset for this operation',
17330
+ '0x4b37cd2a': 'The asset amount must be greater than zero',
17331
+ '0x3a8c54c4': 'The share amount must be greater than zero',
17332
+ '0x4bd956a8': 'The deposit would mint fewer vault shares than the signed minimum',
17333
+ '0x9883d840': 'The withdrawal would pay out fewer assets than the signed minimum',
17334
+ '0xed32f3bb': 'The deposit would exceed the vault deposit cap',
17335
+ '0x1b8851f0': 'The permit failed and the token allowance is too low',
17336
+ '0x2821264f': 'The vault denied the share transfer'
17337
+ };
17338
+ /**
17339
+ * Matches a 4-byte selector in a message. The lookarounds stop a longer hex
17340
+ * value, such as an address or a hash, from matching.
17341
+ */ const SELECTOR_IN_TEXT = /(?<![0-9a-fx])0x[0-9a-f]{8}(?![0-9a-f])/gi;
17342
+ function toRecord(value) {
17343
+ return typeof value === 'object' && value !== null ? value : {};
17344
+ }
17345
+ function isVedaSelector(selector) {
17346
+ return selector === SHARES_LOCKED_SELECTOR || selector in VEDA_REVERT_REASONS;
17347
+ }
17348
+ /**
17349
+ * Reads the selector from the revert data on a trace. The `/next` adapters
17350
+ * keep it on `revertData`. The default adapters keep the whole raw error on
17351
+ * `rawError`.
17352
+ */ function selectorFromData(trace) {
17353
+ const data = typeof trace['revertData'] === 'string' ? trace['revertData'] : extractRevertData(trace['rawError']);
17354
+ const selector = data?.slice(0, 10).toLowerCase();
17355
+ return selector !== undefined && isVedaSelector(selector) ? selector : undefined;
17356
+ }
17357
+ /**
17358
+ * Reads the selector from error text, for wallets that return no revert
17359
+ * data and put the selector or the decoded error name in the message.
17360
+ */ function selectorFromText(texts) {
17361
+ for (const text of texts){
17362
+ if (typeof text !== 'string') {
17363
+ continue;
17364
+ }
17365
+ if (/SharesAreLocked/i.test(text)) {
17366
+ return SHARES_LOCKED_SELECTOR;
17367
+ }
17368
+ const selector = text.match(SELECTOR_IN_TEXT)?.map((match)=>match.toLowerCase()).find(isVedaSelector);
17369
+ if (selector !== undefined) {
17370
+ return selector;
17371
+ }
17372
+ }
17373
+ return undefined;
17374
+ }
17375
+ /**
17376
+ * Reads a Veda revert from one error, without looking at nested errors.
17377
+ */ function readVedaRevert(error) {
17378
+ const trace = toRecord(error.cause?.trace);
17379
+ const selector = selectorFromData(trace) ?? selectorFromText([
17380
+ error.message,
17381
+ trace['reason']
17382
+ ]);
17383
+ if (selector === undefined) {
17384
+ return undefined;
17385
+ }
17386
+ const chain = trace['chain'];
17387
+ return {
17388
+ selector,
17389
+ chain: typeof chain === 'string' ? chain : undefined
17390
+ };
17391
+ }
17392
+ /**
17393
+ * Returns the errors that a failed batch nests under `trace.cause` or
17394
+ * `trace.error`.
17395
+ */ function nestedErrors(error) {
17396
+ const trace = toRecord(error.cause?.trace);
17397
+ return [
17398
+ trace['cause'],
17399
+ trace['error']
17400
+ ].filter(isKitError);
17401
+ }
17402
+ /**
17403
+ * Finds a Veda revert on the error or on an error nested in its trace.
17404
+ */ function findVedaRevert(error) {
17405
+ const seen = new Set();
17406
+ const pending = [
17407
+ error
17408
+ ];
17409
+ let current = pending.pop();
17410
+ while(current !== undefined){
17411
+ if (!seen.has(current)) {
17412
+ seen.add(current);
17413
+ const revert = readVedaRevert(current);
17414
+ if (revert !== undefined) {
17415
+ return {
17416
+ ...revert,
17417
+ nested: current !== error
17418
+ };
17419
+ }
17420
+ pending.push(...nestedErrors(current));
17421
+ }
17422
+ current = pending.pop();
17423
+ }
17424
+ return undefined;
17425
+ }
17426
+ /**
17427
+ * Map a Veda vault revert to an Earn error.
17428
+ *
17429
+ * A Teller `SharesAreLocked()` revert becomes `EARN_SHARES_LOCKED`. Other
17430
+ * known Veda reverts keep their simulation failed or transaction reverted
17431
+ * code and get a readable reason. A failed batch that wraps a Veda revert
17432
+ * keeps its code, and the reason is added to its message. A timed out batch
17433
+ * is returned unchanged, because its outcome is unknown. Any other error is
17434
+ * returned unchanged.
17435
+ *
17436
+ * @param error - The error caught from an earn operation.
17437
+ * @returns The mapped error, or `error` when it is not a Veda revert.
17438
+ *
17439
+ * @example
17440
+ * ```typescript
17441
+ * import { mapVedaRevert } from './vedaRevert'
17442
+ *
17443
+ * const mapped = mapVedaRevert(error)
17444
+ * console.log(mapped.name) // 'EARN_SHARES_LOCKED' for a share lock revert
17445
+ * ```
17446
+ *
17447
+ * @internal
17448
+ */ function mapVedaRevert(error) {
17449
+ if (error.name === NetworkError.TIMEOUT.name) {
17450
+ return error;
17451
+ }
17452
+ const revert = findVedaRevert(error);
17453
+ if (revert === undefined) {
17454
+ return error;
17455
+ }
17456
+ const trace = toRecord(error.cause?.trace);
17457
+ const chain = typeof trace['chain'] === 'string' ? trace['chain'] : revert.chain ?? 'EVM';
17458
+ if (revert.selector === SHARES_LOCKED_SELECTOR) {
17459
+ return new KitError({
17460
+ ...EarnError.SHARES_LOCKED,
17461
+ // The lock can last hours, so an immediate retry fails the same way.
17462
+ recoverability: 'FATAL',
17463
+ 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.`,
17464
+ cause: {
17465
+ trace: {
17466
+ ...trace,
17467
+ selector: revert.selector,
17468
+ chain
17469
+ }
17470
+ }
17471
+ });
17472
+ }
17473
+ const readable = VEDA_REVERT_REASONS[revert.selector];
17474
+ if (readable === undefined) {
17475
+ return error;
17476
+ }
17477
+ const reason = `${readable} (${revert.selector})`;
17478
+ if (error.name === OnchainError.SIMULATION_FAILED.name) {
17479
+ return createSimulationFailedError(chain, reason, trace);
17480
+ }
17481
+ if (error.name === OnchainError.TRANSACTION_REVERTED.name) {
17482
+ return createTransactionRevertedError(chain, reason, trace);
17483
+ }
17484
+ if (!revert.nested) {
17485
+ return error;
17486
+ }
17487
+ return new KitError({
17488
+ code: error.code,
17489
+ name: error.name,
17490
+ type: error.type,
17491
+ recoverability: error.recoverability,
17492
+ message: `${error.message} Reason: ${reason}.`,
17493
+ cause: {
17494
+ trace: {
17495
+ ...trace,
17496
+ reason
17497
+ }
17498
+ }
17499
+ });
17500
+ }
17501
+
16965
17502
  const EARN_OPERATIONS = new Set([
16966
17503
  'deposit',
16967
17504
  'withdraw',
@@ -17007,6 +17544,10 @@ const EARN_OPERATIONS = new Set([
17007
17544
  * `RESUMABLE` so callers know `retry()` can resume from the failed phase. A
17008
17545
  * fatal underlying error stays fatal — it cannot be resumed.
17009
17546
  *
17547
+ * A Veda vault revert is mapped first (see {@link mapVedaRevert}): a share
17548
+ * lock becomes `EARN_SHARES_LOCKED`, and other known Veda reverts get a
17549
+ * readable reason.
17550
+ *
17010
17551
  * Non-{@link KitError} inputs (which should not occur on provider code paths)
17011
17552
  * are normalized to a retryable {@link ServiceError.INTERNAL_ERROR}.
17012
17553
  *
@@ -17028,7 +17569,7 @@ const EARN_OPERATIONS = new Set([
17028
17569
  * }
17029
17570
  * ```
17030
17571
  */ function augmentEarnError(error, context) {
17031
- const base = isKitError(error) ? error : new KitError({
17572
+ const base = isKitError(error) ? mapVedaRevert(error) : new KitError({
17032
17573
  ...ServiceError.INTERNAL_ERROR,
17033
17574
  recoverability: 'RETRYABLE',
17034
17575
  message: getErrorMessage(error)
@@ -17971,7 +18512,7 @@ const bridgeDepositPrepareReviewSchema = zod.z.object({
17971
18512
  }
17972
18513
 
17973
18514
  var name$1 = "@circle-fin/provider-earn-service";
17974
- var version$1 = "1.7.1";
18515
+ var version$1 = "1.8.0";
17975
18516
  var pkg$1 = {
17976
18517
  name: name$1,
17977
18518
  version: version$1};
@@ -18323,8 +18864,9 @@ function toPositionInfo(data) {
18323
18864
  * Build signed deposit instructions via the Earn Service API.
18324
18865
  *
18325
18866
  * Call `POST /v1/earnKit/deposit` with the vault address, amount,
18326
- * wallet address, and chain. Return EIP-712 signed execution
18327
- * parameters for on-chain submission.
18867
+ * wallet address, and chain, plus the signed `DepositIntent` when one is
18868
+ * given. Return EIP-712 signed execution parameters for on-chain
18869
+ * submission.
18328
18870
  *
18329
18871
  * @param params - Deposit parameters
18330
18872
  * @returns Signed execution parameters for the deposit
@@ -18338,7 +18880,8 @@ function toPositionInfo(data) {
18338
18880
  vaultAddress: params.vaultAddress,
18339
18881
  amount: params.amount,
18340
18882
  address: params.address,
18341
- chain: params.chain
18883
+ chain: params.chain,
18884
+ ...params.intent
18342
18885
  };
18343
18886
  try {
18344
18887
  const response = await pollApiPost(url.toString(), requestBody, isDepositResponse, pollingConfig);
@@ -18350,6 +18893,232 @@ function toPositionInfo(data) {
18350
18893
  }
18351
18894
  }
18352
18895
 
18896
+ /**
18897
+ * Build the version-1 EarnKit `DepositIntent` EIP-712 typed data.
18898
+ *
18899
+ * The signed struct binds the depositing `wallet`, the target `vault`, the
18900
+ * deposit `amount` in the asset's smallest unit, and a signature `deadline`.
18901
+ * The Earn Service rebuilds the same struct from the deposit request and
18902
+ * rejects the deposit unless `wallet` signed it. The domain intentionally
18903
+ * omits `verifyingContract` because the service verifies the signature
18904
+ * off-chain.
18905
+ *
18906
+ * `domain.chainId` comes from the deposit's chain rather than a constant: the
18907
+ * domain separator must match what the service rebuilds, and a wrong
18908
+ * `chainId` would make every intent signature invalid.
18909
+ *
18910
+ * This function returns the fully-typed {@link DepositIntentTypedData} with
18911
+ * no assertion. {@link resolveDepositIntent} casts it to the adapter's
18912
+ * generic `EIP712Domain` shape only where it calls `adapter.signTypedData`,
18913
+ * since that shared interface always requires `verifyingContract`. The
18914
+ * adapter's runtime `eip712DomainSchema` treats `verifyingContract` as
18915
+ * optional, so the cast is safe at runtime. BorrowKit's
18916
+ * `buildWebhookRegistrationTypedData` works around the same gap.
18917
+ *
18918
+ * @param params - Wallet, vault, base-unit amount, deadline, and chain id.
18919
+ * @returns EIP-712 typed data ready to be cast for `adapter.signTypedData`.
18920
+ *
18921
+ * @example
18922
+ * ```typescript
18923
+ * const typedData = buildDepositIntentTypedData({
18924
+ * wallet: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
18925
+ * vault: '0x00000000000000000000000000000000000000a1',
18926
+ * amount: 100_000_000n,
18927
+ * deadline: 1_700_000_000n,
18928
+ * chainId: 5_042_002,
18929
+ * })
18930
+ * ```
18931
+ *
18932
+ * @internal
18933
+ */ function buildDepositIntentTypedData(params) {
18934
+ return {
18935
+ domain: {
18936
+ ...DEPOSIT_INTENT_DOMAIN,
18937
+ chainId: params.chainId
18938
+ },
18939
+ types: DEPOSIT_INTENT_TYPES,
18940
+ primaryType: 'DepositIntent',
18941
+ message: {
18942
+ wallet: params.wallet,
18943
+ vault: params.vault,
18944
+ amount: params.amount,
18945
+ deadline: params.deadline
18946
+ }
18947
+ };
18948
+ }
18949
+ /**
18950
+ * Earn Service error code for a missing, expired, or wrongly signed
18951
+ * `DepositIntent` (`DEPOSIT_INTENT_INVALID`).
18952
+ */ const DEPOSIT_INTENT_INVALID_CODE = 380418;
18953
+ /**
18954
+ * Sign a `DepositIntent` when the target vault needs one.
18955
+ *
18956
+ * A Veda vault mints shares straight to the depositing wallet, so the Earn
18957
+ * Service only builds a Veda deposit for a wallet that signed a
18958
+ * `DepositIntent` for the same vault and amount. The vault lookup exists only
18959
+ * to find out whether the vault is a Veda vault (and its asset decimals, to
18960
+ * sign the amount). Other vaults ignore the intent, so this returns no intent
18961
+ * for them and the wallet sees no extra signature prompt. A vault the service
18962
+ * does not return is also treated as not needing an intent; the deposit call
18963
+ * then reports why.
18964
+ *
18965
+ * A failed vault lookup does not stop the deposit: this returns the lookup
18966
+ * error with no intent, so a non-Veda deposit works as before. A Veda deposit
18967
+ * then gets `DEPOSIT_INTENT_INVALID` from the service, and
18968
+ * {@link resolveDepositError} reports the lookup error instead. The lookup
18969
+ * fails either by throwing or by returning the vault in its `errors` list (for
18970
+ * example `VAULT_REFRESH_BUSY`); both are kept the same way.
18971
+ *
18972
+ * @param params - Adapter, resolved chain, wallet, vault, human-readable
18973
+ * amount, and service config.
18974
+ * @returns The signed intent when the vault needs one, and the lookup error
18975
+ * when the vault lookup failed.
18976
+ * @throws {@link KitError} `InputError.VALIDATION_FAILED` when the vault needs
18977
+ * an intent but the chain is not EVM, the adapter cannot sign typed data, or
18978
+ * the amount has more decimal places than the vault asset.
18979
+ *
18980
+ * @example
18981
+ * ```typescript
18982
+ * const { intent } = await resolveDepositIntent({
18983
+ * adapter,
18984
+ * chain: ArcTestnet,
18985
+ * address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
18986
+ * vaultAddress: '0x00000000000000000000000000000000000000a1',
18987
+ * amount: '100',
18988
+ * config,
18989
+ * })
18990
+ * // intent -> { intentSignature: '0x…', intentDeadline: 1700000240 }
18991
+ * ```
18992
+ *
18993
+ * @internal
18994
+ */ async function resolveDepositIntent(params) {
18995
+ let lookup;
18996
+ try {
18997
+ lookup = await fetchVaults({
18998
+ // `resolveAdapterContext` already rejected chains the Earn Service does
18999
+ // not support, so the chain is an Earn chain here.
19000
+ vaults: [
19001
+ {
19002
+ chain: params.chain,
19003
+ vaultAddress: params.vaultAddress
19004
+ }
19005
+ ],
19006
+ config: params.config
19007
+ });
19008
+ } catch (lookupError) {
19009
+ return {
19010
+ lookupError
19011
+ };
19012
+ }
19013
+ const vaultError = lookup.errors.find((candidate)=>isSameAddress(candidate.vaultAddress, params.vaultAddress));
19014
+ if (vaultError !== undefined) {
19015
+ // Same handling as a thrown lookup error, mapped with the Earn Service
19016
+ // error table so it keeps its recoverability (VAULT_REFRESH_BUSY retries).
19017
+ return {
19018
+ lookupError: parseEarnApiError({
19019
+ responseBody: {
19020
+ code: vaultError.code,
19021
+ message: vaultError.message
19022
+ }
19023
+ }, {
19024
+ operation: 'getVaults'
19025
+ })
19026
+ };
19027
+ }
19028
+ const vault = lookup.vaults.find((candidate)=>isSameAddress(candidate.address, params.vaultAddress));
19029
+ if (vault?.protocol.toLowerCase() !== VEDA_PROTOCOL) {
19030
+ return {};
19031
+ }
19032
+ if (params.chain.type !== 'evm') {
19033
+ throw createValidationFailedError('from.chain', params.chain.chain, 'Veda vault deposits need an EVM chain to sign the deposit intent');
19034
+ }
19035
+ if (!canSignTypedData(params.adapter)) {
19036
+ throw createValidationFailedError('from.adapter', params.adapter, 'adapter must support signTypedData for Veda vault deposits');
19037
+ }
19038
+ const wallet = assertHexAddress('from.address', params.address, 'Depositor address must be a 0x-prefixed 20-byte hex address.');
19039
+ const assetDecimals = vault.liquidityProfile.totalDeposits.decimals;
19040
+ // Amount.parse truncates extra digits, but the service rejects them. Fail
19041
+ // before the wallet prompt instead of asking to sign a deposit that cannot
19042
+ // go through.
19043
+ assertDecimalPlaces('amount', params.amount, assetDecimals, `amount must have at most ${String(assetDecimals)} decimal places for the vault asset`);
19044
+ const deadline = Math.floor(Date.now() / 1000) + DEPOSIT_INTENT_TTL_SECONDS;
19045
+ const typedData = buildDepositIntentTypedData({
19046
+ wallet,
19047
+ vault: params.vaultAddress,
19048
+ amount: Amount.parse(params.amount, {
19049
+ decimals: assetDecimals
19050
+ }).raw,
19051
+ deadline: BigInt(deadline),
19052
+ chainId: params.chain.chainId
19053
+ });
19054
+ // `typedData.domain` intentionally omits `verifyingContract` (the Earn
19055
+ // Service verifies off-chain), but the shared `signTypedData` signature
19056
+ // requires the standard `EIP712Domain` shape. The adapter's runtime
19057
+ // `eip712DomainSchema` treats `verifyingContract` as optional, so this cast
19058
+ // is safe at runtime.
19059
+ const intentSignature = await params.adapter.signTypedData(typedData, {
19060
+ chain: params.chain,
19061
+ address: wallet
19062
+ });
19063
+ return {
19064
+ intent: {
19065
+ intentSignature,
19066
+ intentDeadline: deadline
19067
+ }
19068
+ };
19069
+ }
19070
+ /**
19071
+ * Pick the error to report for a failed deposit request.
19072
+ *
19073
+ * When the vault lookup failed, the SDK sent the deposit without an intent.
19074
+ * If the service then answers `DEPOSIT_INTENT_INVALID`, the vault is a Veda
19075
+ * vault and the real cause is the failed lookup, not a bad signature. Report
19076
+ * the lookup error, which keeps its own recoverability (often retryable),
19077
+ * instead of a fatal signature rejection.
19078
+ *
19079
+ * When the SDK did send an intent and the service still answers
19080
+ * `DEPOSIT_INTENT_INVALID`, the error stays a fatal signature rejection, but
19081
+ * its message adds that the intent may have expired: the deadline starts
19082
+ * before the wallet prompt, so a slow signer can submit an expired intent.
19083
+ * Calling `deposit()` again signs a new one. Every other error is reported as
19084
+ * is.
19085
+ *
19086
+ * @param resolution - The result of {@link resolveDepositIntent}.
19087
+ * @param error - The error from the deposit request.
19088
+ * @returns The error to throw.
19089
+ *
19090
+ * @example
19091
+ * ```typescript
19092
+ * try {
19093
+ * return await fetchDeposit({ ...params, intent: resolution.intent })
19094
+ * } catch (error) {
19095
+ * throw resolveDepositError(resolution, error)
19096
+ * }
19097
+ * ```
19098
+ *
19099
+ * @internal
19100
+ */ function resolveDepositError(resolution, error) {
19101
+ if (resolution.lookupError !== undefined && isKitError(error) && isEarnServiceCode(error.cause?.trace, DEPOSIT_INTENT_INVALID_CODE)) {
19102
+ return resolution.lookupError;
19103
+ }
19104
+ if (resolution.intent !== undefined && isKitError(error) && isEarnServiceCode(error.cause?.trace, DEPOSIT_INTENT_INVALID_CODE)) {
19105
+ return new KitError({
19106
+ code: error.code,
19107
+ name: error.name,
19108
+ type: error.type,
19109
+ recoverability: error.recoverability,
19110
+ message: `${error.message} The deposit intent may have expired before the deposit was submitted (it is valid for ${String(DEPOSIT_INTENT_TTL_SECONDS)} seconds from when the SDK asks the wallet to sign). Call deposit() again to sign a new intent.`,
19111
+ ...error.cause !== undefined && {
19112
+ cause: error.cause
19113
+ }
19114
+ });
19115
+ }
19116
+ return error;
19117
+ }
19118
+ function isEarnServiceCode(trace, code) {
19119
+ return typeof trace === 'object' && trace !== null && 'earnServiceCode' in trace && trace.earnServiceCode === code;
19120
+ }
19121
+
18353
19122
  /**
18354
19123
  * Build cross-chain deposit instructions via the bridge API.
18355
19124
  *
@@ -19232,7 +20001,7 @@ function reportTransactionFailure(reportContext, action, error) {
19232
20001
  }
19233
20002
  function transactionReportErrorCode(error) {
19234
20003
  if (isKitError(error)) {
19235
- return error.name;
20004
+ return mapVedaRevert(error).name;
19236
20005
  }
19237
20006
  const message = getErrorMessage(error);
19238
20007
  if (/user (rejected|denied)|rejected by user/i.test(message)) {
@@ -19578,13 +20347,31 @@ function finishElapsedWait(lastStatus, lastError) {
19578
20347
  const adapterContractAddress = requireAdapterContract(chain);
19579
20348
  const { adapter } = params.from;
19580
20349
  const vaultAddress = assertHexAddress('vaultAddress', params.vaultAddress, 'Vault address must be a 0x-prefixed 20-byte hex address.');
19581
- const { execId, executionParams, signature } = await this.runPhase(ctx, 'deposit', 'fetchParams', async ()=>fetchDeposit({
20350
+ const { execId, executionParams, signature } = await this.runPhase(ctx, 'deposit', 'fetchParams', async ()=>{
20351
+ // Veda vaults mint shares straight to `address`, so the service
20352
+ // needs proof that `address` asked for this deposit. Sign it here,
20353
+ // inside fetchParams, so a rejected prompt fails this phase.
20354
+ const resolution = await resolveDepositIntent({
20355
+ adapter,
20356
+ chain,
20357
+ address,
19582
20358
  vaultAddress,
19583
20359
  amount: params.amount,
19584
- address,
19585
- chain: apiChain,
19586
20360
  config
19587
- }), ()=>undefined);
20361
+ });
20362
+ try {
20363
+ return await fetchDeposit({
20364
+ vaultAddress,
20365
+ amount: params.amount,
20366
+ address,
20367
+ chain: apiChain,
20368
+ intent: resolution.intent,
20369
+ config
20370
+ });
20371
+ } catch (error) {
20372
+ throw resolveDepositError(resolution, error);
20373
+ }
20374
+ }, ()=>undefined);
19588
20375
  validateExecutionDeadline(executionParams);
19589
20376
  const transactionReportContext = {
19590
20377
  execId,
@@ -22591,7 +23378,7 @@ registerKit(`${pkg$2.name}/${pkg$2.version}`);
22591
23378
  };
22592
23379
 
22593
23380
  var name = "@circle-fin/borrow-kit";
22594
- var version = "1.0.0";
23381
+ var version = "1.1.1";
22595
23382
  var pkg = {
22596
23383
  name: name,
22597
23384
  version: version};