@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.mjs CHANGED
@@ -743,6 +743,11 @@ class KitError extends Error {
743
743
  code: 5001,
744
744
  name: 'ONCHAIN_TRANSACTION_REVERTED',
745
745
  type: 'ONCHAIN'
746
+ },
747
+ /** Pre-flight transaction simulation failed */ SIMULATION_FAILED: {
748
+ code: 5002,
749
+ name: 'ONCHAIN_SIMULATION_FAILED',
750
+ type: 'ONCHAIN'
746
751
  }};
747
752
  /**
748
753
  * Standardized error definitions for LIQUIDITY type errors.
@@ -1165,6 +1170,49 @@ class KitError extends Error {
1165
1170
  return new KitError(errorDetails);
1166
1171
  }
1167
1172
 
1173
+ /**
1174
+ * Creates error for transaction simulation failures.
1175
+ *
1176
+ * This error is thrown when a pre-flight transaction simulation fails,
1177
+ * typically due to contract logic that would revert. The error is FATAL
1178
+ * as it indicates the transaction would fail if submitted.
1179
+ *
1180
+ * @param chain - The blockchain network where the simulation failed
1181
+ * @param reason - The reason for simulation failure (e.g., revert message)
1182
+ * @param trace - Optional trace context to include in error (can include rawError and additional debugging data)
1183
+ * @returns KitError with simulation failure details
1184
+ *
1185
+ * @example
1186
+ * ```typescript
1187
+ * import { createSimulationFailedError } from '@core/errors'
1188
+ *
1189
+ * throw createSimulationFailedError('Ethereum', 'ERC20: insufficient allowance')
1190
+ * // Message: "Simulation failed on Ethereum: ERC20: insufficient allowance"
1191
+ * ```
1192
+ *
1193
+ * @example
1194
+ * ```typescript
1195
+ * // With trace context for debugging
1196
+ * throw createSimulationFailedError('Ethereum', 'ERC20: insufficient allowance', {
1197
+ * rawError: error,
1198
+ * txHash: '0x1234...',
1199
+ * gasLimit: '21000',
1200
+ * })
1201
+ * ```
1202
+ */ function createSimulationFailedError(chain, reason, trace) {
1203
+ return new KitError({
1204
+ ...OnchainError.SIMULATION_FAILED,
1205
+ recoverability: 'FATAL',
1206
+ message: `Simulation failed on ${chain}: ${reason}`,
1207
+ cause: {
1208
+ trace: {
1209
+ ...trace,
1210
+ chain,
1211
+ reason
1212
+ }
1213
+ }
1214
+ });
1215
+ }
1168
1216
  /**
1169
1217
  * Creates error for transaction reverts.
1170
1218
  *
@@ -1219,6 +1267,82 @@ class KitError extends Error {
1219
1267
  });
1220
1268
  }
1221
1269
 
1270
+ /**
1271
+ * Matches revert data: a 4-byte selector followed by whole ABI-encoded bytes.
1272
+ */ const REVERT_DATA_PATTERN = /^0x[0-9a-f]{8}(?:[0-9a-f]{2})*$/i;
1273
+ /**
1274
+ * Returns the revert data held by one error in a `cause` chain.
1275
+ *
1276
+ * Browser wallets (EIP-1193) wrap the node's error, so the revert data sits
1277
+ * one level down as `data.data`. viem's `getContractError` reads it the same
1278
+ * way, then keeps it on `ContractFunctionRevertedError.raw` and drops the
1279
+ * node's error from the chain.
1280
+ *
1281
+ * @param link - One error in the `cause` chain
1282
+ * @returns The `raw` or `data` string, or undefined if there is none
1283
+ */ function getRevertData(link) {
1284
+ if ('raw' in link && typeof link.raw === 'string' && REVERT_DATA_PATTERN.test(link.raw)) {
1285
+ return link.raw;
1286
+ }
1287
+ if (!('data' in link)) {
1288
+ return undefined;
1289
+ }
1290
+ const { data } = link;
1291
+ if (typeof data === 'string') {
1292
+ return data;
1293
+ }
1294
+ if (data !== null && typeof data === 'object' && 'data' in data && typeof data.data === 'string') {
1295
+ return data.data;
1296
+ }
1297
+ return undefined;
1298
+ }
1299
+ /**
1300
+ * Finds the first revert data on the error or in its `cause` chain that
1301
+ * `accept` takes.
1302
+ *
1303
+ * viem keeps the node's revert data on an inner `RpcRequestError`, not on
1304
+ * the error it throws. The `seen` set stops a self-referencing `cause` chain.
1305
+ *
1306
+ * @param error - The error object
1307
+ * @param accept - Returns true for the data shape the caller wants
1308
+ * @returns The data as written on the error, or undefined if not found
1309
+ */ function findRevertData(error, accept) {
1310
+ const seen = new Set();
1311
+ let current = error;
1312
+ while(current !== null && typeof current === 'object' && !seen.has(current)){
1313
+ seen.add(current);
1314
+ const data = getRevertData(current);
1315
+ if (data !== undefined && accept(data)) {
1316
+ return data;
1317
+ }
1318
+ current = 'cause' in current ? current.cause : undefined;
1319
+ }
1320
+ return undefined;
1321
+ }
1322
+ /**
1323
+ * Finds the raw revert data on an error or anywhere in its `cause` chain.
1324
+ *
1325
+ * Reads `data`, the nested `data.data` from browser wallets, and viem's
1326
+ * `raw`. Only hex of at least 4 bytes counts, so decoded `data` objects and
1327
+ * empty `0x` data are skipped.
1328
+ *
1329
+ * Revert data is public: it is what the contract returned. Keep it when the
1330
+ * rest of a raw error is redacted, so callers can still decode the revert.
1331
+ *
1332
+ * @param error - The raw error from the blockchain library
1333
+ * @returns The revert data in lowercase, or undefined if there is none
1334
+ *
1335
+ * @example
1336
+ * ```typescript
1337
+ * import { extractRevertData } from '@core/errors'
1338
+ *
1339
+ * extractRevertData({ cause: { data: { data: '0xF64059DB' } } })
1340
+ * // Returns: '0xf64059db'
1341
+ * ```
1342
+ */ function extractRevertData(error) {
1343
+ return findRevertData(error, (data)=>REVERT_DATA_PATTERN.test(data))?.toLowerCase();
1344
+ }
1345
+
1222
1346
  /**
1223
1347
  * Type guard to check if an error is a KitError instance.
1224
1348
  *
@@ -2004,7 +2128,7 @@ class KitError extends Error {
2004
2128
  * between input errors (fix your request) and service errors (retry later).
2005
2129
  *
2006
2130
  * Error code ranges:
2007
- * - 1100-1106: INPUT errors — invalid, unsupported, or stale request state
2131
+ * - 1100-1107: INPUT errors — invalid, unsupported, or stale request state
2008
2132
  * - 8100-8105: SERVICE errors — retryable backend/provider failures
2009
2133
  *
2010
2134
  * @example
@@ -2065,6 +2189,15 @@ class KitError extends Error {
2065
2189
  name: 'EARN_BRIDGE_QUOTE_EXPIRED',
2066
2190
  type: 'INPUT'
2067
2191
  },
2192
+ /**
2193
+ * The wallet's vault shares are inside the vault's share lock period, which
2194
+ * starts at deposit. Surfaced as FATAL rather than RETRYABLE because the
2195
+ * lock can last hours, so an immediate retry fails the same way.
2196
+ */ SHARES_LOCKED: {
2197
+ code: 1107,
2198
+ name: 'EARN_SHARES_LOCKED',
2199
+ type: 'INPUT'
2200
+ },
2068
2201
  /** The proxy signing call failed — retryable. */ SIGNING_FAILED: {
2069
2202
  code: 8100,
2070
2203
  name: 'EARN_SIGNING_FAILED',
@@ -2306,6 +2439,15 @@ class KitError extends Error {
2306
2439
  recoverability: 'RETRYABLE'
2307
2440
  }
2308
2441
  ],
2442
+ // DEPOSIT_INTENT_INVALID: the Veda deposit intent is missing, expired, or
2443
+ // not signed by the depositing wallet.
2444
+ [
2445
+ 380418,
2446
+ {
2447
+ errorDef: EarnError.SIGNATURE_REJECTED,
2448
+ recoverability: 'FATAL'
2449
+ }
2450
+ ],
2309
2451
  // Bridge (380_5XX)
2310
2452
  [
2311
2453
  380500,
@@ -2537,6 +2679,8 @@ var SwapChain;
2537
2679
  SwapChain["HyperEVM"] = "HyperEVM";
2538
2680
  SwapChain["Monad"] = "Monad";
2539
2681
  SwapChain["Arc"] = "Arc";
2682
+ SwapChain["Cronos"] = "Cronos";
2683
+ SwapChain["Morph"] = "Morph";
2540
2684
  // Testnet chains with swap support
2541
2685
  SwapChain["Arc_Testnet"] = "Arc_Testnet";
2542
2686
  })(SwapChain || (SwapChain = {}));
@@ -3217,14 +3361,14 @@ var BorrowChain;
3217
3361
  symbol: 'USDC',
3218
3362
  // Arc uses native USDC with 18 decimals for gas payments (EVM standard).
3219
3363
  // Note: The ERC-20 USDC contract at usdcAddress uses 6 decimals.
3220
- // See: https://docs.arc.network/arc/references/contract-addresses
3364
+ // See: https://docs.arc.io/arc/references/contract-addresses
3221
3365
  decimals: 18
3222
3366
  },
3223
3367
  chainId: 5042002,
3224
3368
  isTestnet: true,
3225
3369
  explorerUrl: 'https://testnet.arcscan.app/tx/{hash}',
3226
3370
  rpcEndpoints: [
3227
- 'https://rpc.testnet.arc.network/'
3371
+ 'https://rpc.testnet.arc.io'
3228
3372
  ],
3229
3373
  eurcAddress: '0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a',
3230
3374
  usdcAddress: '0x3600000000000000000000000000000000000000',
@@ -3869,7 +4013,8 @@ var BorrowChain;
3869
4013
  }
3870
4014
  },
3871
4015
  kitContracts: {
3872
- bridge: BRIDGE_CONTRACT_EVM_MAINNET
4016
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
4017
+ adapter: ADAPTER_CONTRACT_EVM_MAINNET
3873
4018
  }
3874
4019
  });
3875
4020
 
@@ -4738,7 +4883,8 @@ var BorrowChain;
4738
4883
  }
4739
4884
  },
4740
4885
  kitContracts: {
4741
- bridge: BRIDGE_CONTRACT_EVM_MAINNET
4886
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
4887
+ adapter: ADAPTER_CONTRACT_EVM_MAINNET
4742
4888
  }
4743
4889
  });
4744
4890
 
@@ -6628,6 +6774,56 @@ var Chains = /*#__PURE__*/Object.freeze({
6628
6774
  Blockchain.World_Chain_Sepolia
6629
6775
  ]);
6630
6776
 
6777
+ /**
6778
+ * Check if a chain supports a specific type of custom smart contract logic.
6779
+ *
6780
+ * This function examines a chain definition to determine if it has a specific
6781
+ * contract type configured. When a chain has custom contract support, it can
6782
+ * use enhanced features and custom business logic in addition to the standard
6783
+ * CCTP approve/burn/mint flow.
6784
+ *
6785
+ * @param chain - The chain definition to examine for custom contract support.
6786
+ * @param contractType - The type of contract to check for (e.g., 'bridge').
6787
+ * @returns True if the chain supports the specified contract type with a valid address configured.
6788
+ *
6789
+ * @example
6790
+ * ```typescript
6791
+ * import { hasCustomContractSupport, Ethereum } from '@core/chains'
6792
+ *
6793
+ * // Check if a chain supports bridge contracts
6794
+ * if (hasCustomContractSupport(Ethereum, 'bridge')) {
6795
+ * // Runtime validation confirmed kitContracts.bridge exists
6796
+ * console.log('Bridge contract:', Ethereum.kitContracts?.bridge)
6797
+ * return executeCustomFlow(Ethereum.kitContracts?.bridge!, params)
6798
+ * } else {
6799
+ * console.log('Using standard CCTP flow')
6800
+ * return executeStandardFlow(params)
6801
+ * }
6802
+ * ```
6803
+ *
6804
+ * @example
6805
+ * ```typescript
6806
+ * // Usage in flow determination
6807
+ * function determineBridgingFlow(chain: ChainDefinition, params: BridgeParams) {
6808
+ * if (hasCustomContractSupport(chain, 'bridge')) {
6809
+ * return 'custom'
6810
+ * } else if (isCCTPV2Supported(chain)) {
6811
+ * return 'standard'
6812
+ * } else {
6813
+ * throw new Error(`Chain ${chain.name} is not supported for bridging`)
6814
+ * }
6815
+ * }
6816
+ *
6817
+ * // Future usage with other contract types (when added to KitContractType)
6818
+ * // if (hasCustomContractSupport(someChain, 'customType')) {
6819
+ * // console.log('Custom contract:', someChain.kitContracts?.customType)
6820
+ * // }
6821
+ * ```
6822
+ */ function hasCustomContractSupport(chain, contractType) {
6823
+ const contractAddress = chain.kitContracts?.[contractType];
6824
+ return typeof contractAddress === 'string' && contractAddress.trim().length > 0;
6825
+ }
6826
+
6631
6827
  /**
6632
6828
  * Temporary allowlist of chains permitted to initiate Gateway fast deposits.
6633
6829
  * Only chains keyed here are eligible; all others are rejected. Using the
@@ -6881,8 +7077,8 @@ var Chains = /*#__PURE__*/Object.freeze({
6881
7077
  * Zod schema for validating swap-specific chain identifiers.
6882
7078
  *
6883
7079
  * Validates chains based on:
6884
- * - CCTPv2 support (adapter contract deployed)
6885
- * - Mainnet only (no testnets)
7080
+ * - Membership in the SwapChain enum
7081
+ * - Mainnet or explicitly allowlisted testnet
6886
7082
  * - At least one supported token available
6887
7083
  *
6888
7084
  */ const swapChainIdentifierSchema = z.union([
@@ -7287,7 +7483,7 @@ const swapTokenEnumSchema = z.enum([
7287
7483
  *
7288
7484
  * A chain supports swaps if ALL conditions are met:
7289
7485
  * 1. Is a member of the {@link SwapChain} enum
7290
- * 2. Has CCTPv2 support (adapter contract deployed)
7486
+ * 2. Has an Adapter contract configured directly or through CCTP v2
7291
7487
  *
7292
7488
  * Testnets are allowed only when explicitly listed in the
7293
7489
  * {@link SwapChain} enum (e.g., Arc_Testnet).
@@ -7308,14 +7504,14 @@ const swapTokenEnumSchema = z.enum([
7308
7504
  * @remarks
7309
7505
  * Chains automatically become swap-enabled when:
7310
7506
  * - Added to the {@link SwapChain} enum
7311
- * - Adapter contract is deployed (CCTPv2)
7507
+ * - An Adapter contract is deployed and configured
7312
7508
  *
7313
7509
  * No code changes needed when new chains are added!
7314
7510
  */ function isSwapSupportedChain(chain) {
7315
7511
  if (!(chain.chain in SwapChain)) {
7316
7512
  return false;
7317
7513
  }
7318
- if (!isCCTPV2Supported(chain)) {
7514
+ if (!isCCTPV2Supported(chain) && !hasCustomContractSupport(chain, 'adapter')) {
7319
7515
  return false;
7320
7516
  }
7321
7517
  return true;
@@ -7326,7 +7522,7 @@ const swapTokenEnumSchema = z.enum([
7326
7522
  *
7327
7523
  * Dynamically filter chain definitions to include only those that:
7328
7524
  * - Are members of the {@link SwapChain} enum
7329
- * - Have CCTPv2 support
7525
+ * - Have an Adapter contract configured directly or through CCTP v2
7330
7526
  *
7331
7527
  * This function is pure and accepts chains as parameter to avoid
7332
7528
  * circular dependencies.
@@ -8199,11 +8395,13 @@ const swapTokenEnumSchema = z.enum([
8199
8395
  [Blockchain.Arbitrum]: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9',
8200
8396
  [Blockchain.Avalanche]: '0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7',
8201
8397
  [Blockchain.Celo]: '0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e',
8398
+ [Blockchain.Cronos]: '0x66e428c3f67a68878562e79A0234c1F83c208770',
8202
8399
  [Blockchain.Ethereum]: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
8203
8400
  [Blockchain.HyperEVM]: '0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb',
8204
8401
  [Blockchain.Ink]: '0x0200C29006150606B650577BBE7B6248F58470c1',
8205
8402
  [Blockchain.Linea]: '0xA219439258ca9da29E9Cc4cE5596924745e12B93',
8206
8403
  [Blockchain.Monad]: '0xe7cd86e13AC4309349F30B3435a9d337750fC82D',
8404
+ [Blockchain.Morph]: '0xe7cd86e13AC4309349F30B3435a9d337750fC82D',
8207
8405
  [Blockchain.NEAR]: 'usdt.tether-token.near',
8208
8406
  [Blockchain.Optimism]: '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58',
8209
8407
  [Blockchain.Polkadot_Asset_Hub]: '1984',
@@ -8262,6 +8460,7 @@ const swapTokenEnumSchema = z.enum([
8262
8460
  [Blockchain.Arbitrum]: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1',
8263
8461
  [Blockchain.Avalanche]: '0xd586E7F844cEa2F87f50152665BCbc2C279D8d70',
8264
8462
  [Blockchain.Base]: '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb',
8463
+ [Blockchain.Cronos]: '0xF2001B145b43032AAF5Ee2884e456CCd805F677D',
8265
8464
  [Blockchain.Ethereum]: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
8266
8465
  [Blockchain.Linea]: '0x4AF15ec2A0BD43Db75dd04E62FAA3B8EF36b00d5',
8267
8466
  [Blockchain.Optimism]: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1',
@@ -8324,7 +8523,9 @@ const swapTokenEnumSchema = z.enum([
8324
8523
  [Blockchain.Arbitrum]: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
8325
8524
  [Blockchain.Avalanche]: '0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB',
8326
8525
  [Blockchain.Base]: '0x4200000000000000000000000000000000000006',
8526
+ [Blockchain.Cronos]: '0xe44Fd7fCb2b1581822D0c862B68222998a0c299a',
8327
8527
  [Blockchain.Ethereum]: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
8528
+ [Blockchain.Morph]: '0x5300000000000000000000000000000000000011',
8328
8529
  [Blockchain.Optimism]: '0x4200000000000000000000000000000000000006',
8329
8530
  [Blockchain.Polygon]: '0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619',
8330
8531
  [Blockchain.Solana]: '7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs'
@@ -8343,6 +8544,7 @@ const swapTokenEnumSchema = z.enum([
8343
8544
  locators: {
8344
8545
  [Blockchain.Ethereum]: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599',
8345
8546
  [Blockchain.Arbitrum]: '0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f',
8547
+ [Blockchain.Cronos]: '0x062E66477Faf219F25D27dCED647BF57C3107d52',
8346
8548
  [Blockchain.Avalanche]: '0x50b7545627a5162F82A992c33b87aDc75187B218'
8347
8549
  }
8348
8550
  };
@@ -9361,7 +9563,7 @@ const swapTokenEnumSchema = z.enum([
9361
9563
  }
9362
9564
 
9363
9565
  var name$4 = "@circle-fin/bridge-kit";
9364
- var version$4 = "1.15.2";
9566
+ var version$4 = "1.16.0";
9365
9567
  var pkg$4 = {
9366
9568
  name: name$4,
9367
9569
  version: version$4};
@@ -11157,7 +11359,7 @@ const validateQuoteItemSchema = z.object({
11157
11359
  registerKit(`${pkg$4.name}/${pkg$4.version}`);
11158
11360
 
11159
11361
  var name$3 = "@circle-fin/swap-kit";
11160
- var version$3 = "1.7.1";
11362
+ var version$3 = "1.8.1";
11161
11363
  var pkg$3 = {
11162
11364
  name: name$3,
11163
11365
  version: version$3};
@@ -11499,6 +11701,64 @@ const optionalChainIdentifierField = chainIdentifierField.optional();
11499
11701
  }, {
11500
11702
  message: 'fee amount must be non-negative (>= 0)'
11501
11703
  });
11704
+ /**
11705
+ * Parses a display-only field leniently: a value `schema` rejects is replaced
11706
+ * with `fallback` instead of failing the response that carries it.
11707
+ * Implemented with `preprocess` rather than Zod's `.catch()` because static
11708
+ * analysis misreads `.catch` on the schema chain as an unhandled Promise
11709
+ * (S7785).
11710
+ */ const withFallback = (schema, fallback)=>z.preprocess((value)=>schema.safeParse(value).success ? value : fallback, schema);
11711
+ const swapRouteTokenSchema = z.object({
11712
+ symbol: z.string(),
11713
+ address: z.string(),
11714
+ decimals: z.number()
11715
+ });
11716
+ const swapRouteMetadataSchema = z.record(z.unknown()).optional();
11717
+ const swapRouteStepSchema = z.object({
11718
+ type: z.string(),
11719
+ tool: z.string(),
11720
+ toolName: z.string(),
11721
+ fromToken: swapRouteTokenSchema,
11722
+ toToken: swapRouteTokenSchema,
11723
+ estimate: z.object({
11724
+ fromAmount: z.string(),
11725
+ toAmount: z.string(),
11726
+ gasCostUSD: z.string().optional()
11727
+ }),
11728
+ metadata: swapRouteMetadataSchema
11729
+ });
11730
+ /**
11731
+ * Strict schema for a `SwapRoute`. Module-private: service responses
11732
+ * parse routes leniently through {@link responseSwapRouteSchema}.
11733
+ */ const swapRouteSchema = z.object({
11734
+ provider: z.string().min(1, 'provider must be a non-empty string'),
11735
+ steps: z.array(swapRouteStepSchema),
11736
+ metadata: swapRouteMetadataSchema
11737
+ });
11738
+ /**
11739
+ * Route details are display-only, so service responses parse them leniently:
11740
+ * steps or metadata in a shape the SDK does not know are dropped rather than
11741
+ * failing the quote or swap they describe. Steps are all-or-nothing: if any
11742
+ * step is malformed, `steps` is empty, so a route never silently loses a hop.
11743
+ */ const responseSwapRouteSchema = swapRouteSchema.extend({
11744
+ steps: withFallback(swapRouteSchema.shape.steps, []),
11745
+ metadata: withFallback(swapRouteMetadataSchema, undefined)
11746
+ });
11747
+ const createSwapFeeItemSchema = z.object({
11748
+ token: z.string({
11749
+ required_error: 'fee token is required',
11750
+ invalid_type_error: 'fee token must be a string'
11751
+ }).min(1, 'fee token must be a non-empty string'),
11752
+ amount: feeAmountSchema,
11753
+ decimals: z.number().int('fee token decimals must be an integer').nonnegative('fee token decimals must be a non-negative integer').optional(),
11754
+ symbol: z.string({
11755
+ invalid_type_error: 'fee token symbol must be a string'
11756
+ }).min(1, 'fee token symbol must be a non-empty string').optional(),
11757
+ // Descriptive metadata: a value the SDK cannot use is dropped rather than
11758
+ // failing the response that carries the fee.
11759
+ type: withFallback(z.string().min(1).optional(), undefined),
11760
+ chain: withFallback(z.string().min(1).optional(), undefined)
11761
+ });
11502
11762
  /**
11503
11763
  * Zod schema for validating API keys.
11504
11764
  *
@@ -11584,10 +11844,7 @@ const optionalChainIdentifierField = chainIdentifierField.optional();
11584
11844
  /** Quote information with estimated amounts and route */ quote: z.object({
11585
11845
  /** Estimated output amount (base units) */ estimatedAmount: z.string().min(1, 'estimatedAmount must be a non-empty string').regex(/^\d+$/, 'estimatedAmount must be a numeric string'),
11586
11846
  /** Guaranteed minimum output amount (base units) */ minAmount: z.string().min(1, 'minAmount must be a non-empty string').regex(/^\d+$/, 'minAmount must be a numeric string'),
11587
- /** Route information from DEX aggregator */ route: z.object({
11588
- provider: z.string().min(1, 'provider must be a non-empty string'),
11589
- steps: z.array(z.unknown())
11590
- })
11847
+ /** Route information from DEX aggregator */ route: responseSwapRouteSchema
11591
11848
  }),
11592
11849
  /** Fee context indicating where fees should be collected */ feeContext: quoteFeeContextSchema
11593
11850
  });
@@ -11803,19 +12060,6 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
11803
12060
  }).min(1, 'toChain must be a non-empty string if provided').optional(),
11804
12061
  apiKey: apiKeySchema.optional()
11805
12062
  });
11806
- /**
11807
- * Zod schema for validating CreateSwapResponse payloads.
11808
- */ const createSwapFeeItemSchema = z.object({
11809
- token: z.string({
11810
- required_error: 'fee token is required',
11811
- invalid_type_error: 'fee token must be a string'
11812
- }).min(1, 'fee token must be a non-empty string'),
11813
- amount: feeAmountSchema,
11814
- decimals: z.number().int('fee token decimals must be an integer').nonnegative('fee token decimals must be a non-negative integer').optional(),
11815
- symbol: z.string({
11816
- invalid_type_error: 'fee token symbol must be a string'
11817
- }).min(1, 'fee token symbol must be a non-empty string').optional()
11818
- });
11819
12063
  /**
11820
12064
  * Developer fee item schema with basis field.
11821
12065
  */ const createSwapDeveloperFeeItemSchema = createSwapFeeItemSchema.extend({
@@ -11953,6 +12197,8 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
11953
12197
  correlationId: z.preprocess((value)=>z.string().uuid().safeParse(value).success ? value : undefined, z.string().optional()),
11954
12198
  config: createSwapRequestBaseSchema.shape.config.optional(),
11955
12199
  fees: createSwapFeesSchema.optional(),
12200
+ // A route the SDK cannot parse is dropped instead of failing the swap.
12201
+ route: withFallback(responseSwapRouteSchema.optional(), undefined),
11956
12202
  transaction: createSwapTransactionSchema
11957
12203
  });
11958
12204
 
@@ -14374,6 +14620,47 @@ const abiParameterSchema = z.object({
14374
14620
  */ function hasSignTypedData(adapter) {
14375
14621
  return typeof adapter === 'object' && adapter !== null && 'signTypedData' in adapter && typeof adapter.signTypedData === 'function';
14376
14622
  }
14623
+ /**
14624
+ * Type guard to check if an adapter can actually produce an EIP-712
14625
+ * typed-data signature.
14626
+ *
14627
+ * @remarks
14628
+ * Strengthens {@link hasSignTypedData}: having a `signTypedData` method
14629
+ * does not guarantee it can succeed. Adapters whose signer is delegated
14630
+ * (e.g. through a signing strategy backed by a smart contract account)
14631
+ * expose the method but reject typed-data payloads at runtime. Such
14632
+ * adapters report their real capability through an optional
14633
+ * `supportsSignTypedData()` method, which this guard consults when
14634
+ * present. Adapters without the capability method are assumed able to
14635
+ * sign, preserving the previous duck-typing behavior.
14636
+ *
14637
+ * @param adapter - The adapter to check
14638
+ * @returns True if calling `signTypedData` can be expected to succeed
14639
+ *
14640
+ * @example
14641
+ * ```typescript
14642
+ * import { canSignTypedData } from '@core/adapter-evm'
14643
+ *
14644
+ * if (canSignTypedData(adapter)) {
14645
+ * const signature = await adapter.signTypedData(typedData, context)
14646
+ * } else {
14647
+ * // take an on-chain approval path instead of a permit signature
14648
+ * }
14649
+ * ```
14650
+ */ function canSignTypedData(adapter) {
14651
+ if (!hasSignTypedData(adapter)) {
14652
+ return false;
14653
+ }
14654
+ if (typeof adapter.supportsSignTypedData === 'function') {
14655
+ // The value is `boolean` per the interface, but a plain-JS adapter may
14656
+ // return anything; treat it as untrusted and coerce to a strict
14657
+ // boolean. Comparing an `unknown` (not a `boolean`) also keeps the
14658
+ // lint autofix from stripping this as a redundant `=== true`.
14659
+ const supported = adapter.supportsSignTypedData();
14660
+ return supported === true;
14661
+ }
14662
+ return true;
14663
+ }
14377
14664
 
14378
14665
  /** Inputs for {@link checkErc3009ValidityWindow}. */ /**
14379
14666
  * Check an ERC-3009 authorization validity window.
@@ -14915,6 +15202,16 @@ createTokenRegistry();
14915
15202
  name: 'USDC',
14916
15203
  version: '2'
14917
15204
  },
15205
+ Cronos: {
15206
+ chainId: Cronos.chainId,
15207
+ name: 'USDC',
15208
+ version: '2'
15209
+ },
15210
+ Morph: {
15211
+ chainId: Morph.chainId,
15212
+ name: 'USDC',
15213
+ version: '2'
15214
+ },
14918
15215
  Arc_Testnet: {
14919
15216
  chainId: ArcTestnet.chainId,
14920
15217
  name: 'USDC',
@@ -15003,6 +15300,7 @@ const optionalSwapChainIdentifierField = swapChainIdentifierField.optional();
15003
15300
  * - customFee: Optional fee configuration
15004
15301
  * - apiKey: Optional credential string
15005
15302
  * - kitKey: Optional credential string (deprecated alias for apiKey)
15303
+ * - batchTransactions: Optional boolean to opt out of atomic batching
15006
15304
  */ const swapConfigSchema = z.object({
15007
15305
  allowanceStrategy: allowanceStrategySchema.optional(),
15008
15306
  slippageBps: z.number().int().min(0).optional(),
@@ -15013,7 +15311,10 @@ const optionalSwapChainIdentifierField = swapChainIdentifierField.optional();
15013
15311
  })(z.string())).optional(),
15014
15312
  customFee: swapCustomFeeSchema.optional(),
15015
15313
  apiKey: z.string().optional(),
15016
- kitKey: z.string().optional()
15314
+ kitKey: z.string().optional(),
15315
+ batchTransactions: z.boolean({
15316
+ invalid_type_error: 'batchTransactions must be a boolean'
15317
+ }).optional()
15017
15318
  });
15018
15319
  const swapDestinationSchema = z.object({
15019
15320
  chain: optionalSwapChainIdentifierField,
@@ -15206,7 +15507,7 @@ new Set(Object.values(Blockchain));
15206
15507
  registerKit(`${pkg$3.name}/${pkg$3.version}`);
15207
15508
 
15208
15509
  var name$2 = "@circle-fin/earn-kit";
15209
- var version$2 = "1.8.1";
15510
+ var version$2 = "1.10.0";
15210
15511
  var pkg$2 = {
15211
15512
  name: name$2,
15212
15513
  version: version$2};
@@ -15313,6 +15614,61 @@ const EARN_BRIDGE_USDC_ERC3009_DOMAINS = {
15313
15614
  *
15314
15615
  * @internal
15315
15616
  */ const EARN_BRIDGE_MAX_VALIDITY_SECONDS = 86_400n;
15617
+ /**
15618
+ * Static fields of the EIP-712 domain for the `DepositIntent` typed-data
15619
+ * intent. `chainId` is excluded here — it must come from the deposit's
15620
+ * actual chain (see {@link buildDepositIntentTypedData}), since a mismatched
15621
+ * `chainId` in the domain separator would make every intent signature
15622
+ * invalid.
15623
+ *
15624
+ * `verifyingContract` is intentionally omitted — the intent is verified
15625
+ * off-chain by the Earn Service, not by a deployed contract.
15626
+ *
15627
+ * @internal
15628
+ */ const DEPOSIT_INTENT_DOMAIN = {
15629
+ name: 'Circle EarnKit',
15630
+ version: '1'
15631
+ };
15632
+ /**
15633
+ * EIP-712 type definition for the `DepositIntent` struct.
15634
+ *
15635
+ * @internal
15636
+ */ const DEPOSIT_INTENT_TYPES = {
15637
+ DepositIntent: [
15638
+ {
15639
+ name: 'wallet',
15640
+ type: 'address'
15641
+ },
15642
+ {
15643
+ name: 'vault',
15644
+ type: 'address'
15645
+ },
15646
+ {
15647
+ name: 'amount',
15648
+ type: 'uint256'
15649
+ },
15650
+ {
15651
+ name: 'deadline',
15652
+ type: 'uint256'
15653
+ }
15654
+ ]
15655
+ };
15656
+ /**
15657
+ * Lifetime of a `DepositIntent` signature, in seconds.
15658
+ *
15659
+ * The Earn Service rejects a deadline more than 300 seconds ahead of its own
15660
+ * clock. 240 seconds leaves room for clock skew between the client and the
15661
+ * service while still covering the deposit request that follows the
15662
+ * signature.
15663
+ *
15664
+ * @internal
15665
+ */ const DEPOSIT_INTENT_TTL_SECONDS = 240;
15666
+ /**
15667
+ * Protocol value the Earn Service reports for Veda vaults. A deposit into a
15668
+ * Veda vault needs a signed `DepositIntent`.
15669
+ *
15670
+ * @internal
15671
+ */ const VEDA_PROTOCOL = 'veda';
15316
15672
  /**
15317
15673
  * Display symbol for vault share tokens.
15318
15674
  *
@@ -16956,6 +17312,187 @@ function isCrossChainDepositStatusStep(step) {
16956
17312
  return step.name === 'status';
16957
17313
  }
16958
17314
 
17315
+ /**
17316
+ * Selector of `TellerWithMultiAssetSupport__SharesAreLocked()`.
17317
+ */ const SHARES_LOCKED_SELECTOR = '0xf64059db';
17318
+ /**
17319
+ * Readable reasons for Veda BoringVault Teller and Accountant reverts.
17320
+ */ const VEDA_REVERT_REASONS = {
17321
+ '0xe0f9e71d': 'The vault is paused',
17322
+ '0x3b3132f6': 'The vault exchange rate is paused',
17323
+ '0xc8bfa33e': 'The vault does not support this asset for this operation',
17324
+ '0x4b37cd2a': 'The asset amount must be greater than zero',
17325
+ '0x3a8c54c4': 'The share amount must be greater than zero',
17326
+ '0x4bd956a8': 'The deposit would mint fewer vault shares than the signed minimum',
17327
+ '0x9883d840': 'The withdrawal would pay out fewer assets than the signed minimum',
17328
+ '0xed32f3bb': 'The deposit would exceed the vault deposit cap',
17329
+ '0x1b8851f0': 'The permit failed and the token allowance is too low',
17330
+ '0x2821264f': 'The vault denied the share transfer'
17331
+ };
17332
+ /**
17333
+ * Matches a 4-byte selector in a message. The lookarounds stop a longer hex
17334
+ * value, such as an address or a hash, from matching.
17335
+ */ const SELECTOR_IN_TEXT = /(?<![0-9a-fx])0x[0-9a-f]{8}(?![0-9a-f])/gi;
17336
+ function toRecord(value) {
17337
+ return typeof value === 'object' && value !== null ? value : {};
17338
+ }
17339
+ function isVedaSelector(selector) {
17340
+ return selector === SHARES_LOCKED_SELECTOR || selector in VEDA_REVERT_REASONS;
17341
+ }
17342
+ /**
17343
+ * Reads the selector from the revert data on a trace. The `/next` adapters
17344
+ * keep it on `revertData`. The default adapters keep the whole raw error on
17345
+ * `rawError`.
17346
+ */ function selectorFromData(trace) {
17347
+ const data = typeof trace['revertData'] === 'string' ? trace['revertData'] : extractRevertData(trace['rawError']);
17348
+ const selector = data?.slice(0, 10).toLowerCase();
17349
+ return selector !== undefined && isVedaSelector(selector) ? selector : undefined;
17350
+ }
17351
+ /**
17352
+ * Reads the selector from error text, for wallets that return no revert
17353
+ * data and put the selector or the decoded error name in the message.
17354
+ */ function selectorFromText(texts) {
17355
+ for (const text of texts){
17356
+ if (typeof text !== 'string') {
17357
+ continue;
17358
+ }
17359
+ if (/SharesAreLocked/i.test(text)) {
17360
+ return SHARES_LOCKED_SELECTOR;
17361
+ }
17362
+ const selector = text.match(SELECTOR_IN_TEXT)?.map((match)=>match.toLowerCase()).find(isVedaSelector);
17363
+ if (selector !== undefined) {
17364
+ return selector;
17365
+ }
17366
+ }
17367
+ return undefined;
17368
+ }
17369
+ /**
17370
+ * Reads a Veda revert from one error, without looking at nested errors.
17371
+ */ function readVedaRevert(error) {
17372
+ const trace = toRecord(error.cause?.trace);
17373
+ const selector = selectorFromData(trace) ?? selectorFromText([
17374
+ error.message,
17375
+ trace['reason']
17376
+ ]);
17377
+ if (selector === undefined) {
17378
+ return undefined;
17379
+ }
17380
+ const chain = trace['chain'];
17381
+ return {
17382
+ selector,
17383
+ chain: typeof chain === 'string' ? chain : undefined
17384
+ };
17385
+ }
17386
+ /**
17387
+ * Returns the errors that a failed batch nests under `trace.cause` or
17388
+ * `trace.error`.
17389
+ */ function nestedErrors(error) {
17390
+ const trace = toRecord(error.cause?.trace);
17391
+ return [
17392
+ trace['cause'],
17393
+ trace['error']
17394
+ ].filter(isKitError);
17395
+ }
17396
+ /**
17397
+ * Finds a Veda revert on the error or on an error nested in its trace.
17398
+ */ function findVedaRevert(error) {
17399
+ const seen = new Set();
17400
+ const pending = [
17401
+ error
17402
+ ];
17403
+ let current = pending.pop();
17404
+ while(current !== undefined){
17405
+ if (!seen.has(current)) {
17406
+ seen.add(current);
17407
+ const revert = readVedaRevert(current);
17408
+ if (revert !== undefined) {
17409
+ return {
17410
+ ...revert,
17411
+ nested: current !== error
17412
+ };
17413
+ }
17414
+ pending.push(...nestedErrors(current));
17415
+ }
17416
+ current = pending.pop();
17417
+ }
17418
+ return undefined;
17419
+ }
17420
+ /**
17421
+ * Map a Veda vault revert to an Earn error.
17422
+ *
17423
+ * A Teller `SharesAreLocked()` revert becomes `EARN_SHARES_LOCKED`. Other
17424
+ * known Veda reverts keep their simulation failed or transaction reverted
17425
+ * code and get a readable reason. A failed batch that wraps a Veda revert
17426
+ * keeps its code, and the reason is added to its message. A timed out batch
17427
+ * is returned unchanged, because its outcome is unknown. Any other error is
17428
+ * returned unchanged.
17429
+ *
17430
+ * @param error - The error caught from an earn operation.
17431
+ * @returns The mapped error, or `error` when it is not a Veda revert.
17432
+ *
17433
+ * @example
17434
+ * ```typescript
17435
+ * import { mapVedaRevert } from './vedaRevert'
17436
+ *
17437
+ * const mapped = mapVedaRevert(error)
17438
+ * console.log(mapped.name) // 'EARN_SHARES_LOCKED' for a share lock revert
17439
+ * ```
17440
+ *
17441
+ * @internal
17442
+ */ function mapVedaRevert(error) {
17443
+ if (error.name === NetworkError.TIMEOUT.name) {
17444
+ return error;
17445
+ }
17446
+ const revert = findVedaRevert(error);
17447
+ if (revert === undefined) {
17448
+ return error;
17449
+ }
17450
+ const trace = toRecord(error.cause?.trace);
17451
+ const chain = typeof trace['chain'] === 'string' ? trace['chain'] : revert.chain ?? 'EVM';
17452
+ if (revert.selector === SHARES_LOCKED_SELECTOR) {
17453
+ return new KitError({
17454
+ ...EarnError.SHARES_LOCKED,
17455
+ // The lock can last hours, so an immediate retry fails the same way.
17456
+ recoverability: 'FATAL',
17457
+ 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.`,
17458
+ cause: {
17459
+ trace: {
17460
+ ...trace,
17461
+ selector: revert.selector,
17462
+ chain
17463
+ }
17464
+ }
17465
+ });
17466
+ }
17467
+ const readable = VEDA_REVERT_REASONS[revert.selector];
17468
+ if (readable === undefined) {
17469
+ return error;
17470
+ }
17471
+ const reason = `${readable} (${revert.selector})`;
17472
+ if (error.name === OnchainError.SIMULATION_FAILED.name) {
17473
+ return createSimulationFailedError(chain, reason, trace);
17474
+ }
17475
+ if (error.name === OnchainError.TRANSACTION_REVERTED.name) {
17476
+ return createTransactionRevertedError(chain, reason, trace);
17477
+ }
17478
+ if (!revert.nested) {
17479
+ return error;
17480
+ }
17481
+ return new KitError({
17482
+ code: error.code,
17483
+ name: error.name,
17484
+ type: error.type,
17485
+ recoverability: error.recoverability,
17486
+ message: `${error.message} Reason: ${reason}.`,
17487
+ cause: {
17488
+ trace: {
17489
+ ...trace,
17490
+ reason
17491
+ }
17492
+ }
17493
+ });
17494
+ }
17495
+
16959
17496
  const EARN_OPERATIONS = new Set([
16960
17497
  'deposit',
16961
17498
  'withdraw',
@@ -17001,6 +17538,10 @@ const EARN_OPERATIONS = new Set([
17001
17538
  * `RESUMABLE` so callers know `retry()` can resume from the failed phase. A
17002
17539
  * fatal underlying error stays fatal — it cannot be resumed.
17003
17540
  *
17541
+ * A Veda vault revert is mapped first (see {@link mapVedaRevert}): a share
17542
+ * lock becomes `EARN_SHARES_LOCKED`, and other known Veda reverts get a
17543
+ * readable reason.
17544
+ *
17004
17545
  * Non-{@link KitError} inputs (which should not occur on provider code paths)
17005
17546
  * are normalized to a retryable {@link ServiceError.INTERNAL_ERROR}.
17006
17547
  *
@@ -17022,7 +17563,7 @@ const EARN_OPERATIONS = new Set([
17022
17563
  * }
17023
17564
  * ```
17024
17565
  */ function augmentEarnError(error, context) {
17025
- const base = isKitError(error) ? error : new KitError({
17566
+ const base = isKitError(error) ? mapVedaRevert(error) : new KitError({
17026
17567
  ...ServiceError.INTERNAL_ERROR,
17027
17568
  recoverability: 'RETRYABLE',
17028
17569
  message: getErrorMessage(error)
@@ -17965,7 +18506,7 @@ const bridgeDepositPrepareReviewSchema = z.object({
17965
18506
  }
17966
18507
 
17967
18508
  var name$1 = "@circle-fin/provider-earn-service";
17968
- var version$1 = "1.7.1";
18509
+ var version$1 = "1.8.0";
17969
18510
  var pkg$1 = {
17970
18511
  name: name$1,
17971
18512
  version: version$1};
@@ -18317,8 +18858,9 @@ function toPositionInfo(data) {
18317
18858
  * Build signed deposit instructions via the Earn Service API.
18318
18859
  *
18319
18860
  * Call `POST /v1/earnKit/deposit` with the vault address, amount,
18320
- * wallet address, and chain. Return EIP-712 signed execution
18321
- * parameters for on-chain submission.
18861
+ * wallet address, and chain, plus the signed `DepositIntent` when one is
18862
+ * given. Return EIP-712 signed execution parameters for on-chain
18863
+ * submission.
18322
18864
  *
18323
18865
  * @param params - Deposit parameters
18324
18866
  * @returns Signed execution parameters for the deposit
@@ -18332,7 +18874,8 @@ function toPositionInfo(data) {
18332
18874
  vaultAddress: params.vaultAddress,
18333
18875
  amount: params.amount,
18334
18876
  address: params.address,
18335
- chain: params.chain
18877
+ chain: params.chain,
18878
+ ...params.intent
18336
18879
  };
18337
18880
  try {
18338
18881
  const response = await pollApiPost(url.toString(), requestBody, isDepositResponse, pollingConfig);
@@ -18344,6 +18887,232 @@ function toPositionInfo(data) {
18344
18887
  }
18345
18888
  }
18346
18889
 
18890
+ /**
18891
+ * Build the version-1 EarnKit `DepositIntent` EIP-712 typed data.
18892
+ *
18893
+ * The signed struct binds the depositing `wallet`, the target `vault`, the
18894
+ * deposit `amount` in the asset's smallest unit, and a signature `deadline`.
18895
+ * The Earn Service rebuilds the same struct from the deposit request and
18896
+ * rejects the deposit unless `wallet` signed it. The domain intentionally
18897
+ * omits `verifyingContract` because the service verifies the signature
18898
+ * off-chain.
18899
+ *
18900
+ * `domain.chainId` comes from the deposit's chain rather than a constant: the
18901
+ * domain separator must match what the service rebuilds, and a wrong
18902
+ * `chainId` would make every intent signature invalid.
18903
+ *
18904
+ * This function returns the fully-typed {@link DepositIntentTypedData} with
18905
+ * no assertion. {@link resolveDepositIntent} casts it to the adapter's
18906
+ * generic `EIP712Domain` shape only where it calls `adapter.signTypedData`,
18907
+ * since that shared interface always requires `verifyingContract`. The
18908
+ * adapter's runtime `eip712DomainSchema` treats `verifyingContract` as
18909
+ * optional, so the cast is safe at runtime. BorrowKit's
18910
+ * `buildWebhookRegistrationTypedData` works around the same gap.
18911
+ *
18912
+ * @param params - Wallet, vault, base-unit amount, deadline, and chain id.
18913
+ * @returns EIP-712 typed data ready to be cast for `adapter.signTypedData`.
18914
+ *
18915
+ * @example
18916
+ * ```typescript
18917
+ * const typedData = buildDepositIntentTypedData({
18918
+ * wallet: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
18919
+ * vault: '0x00000000000000000000000000000000000000a1',
18920
+ * amount: 100_000_000n,
18921
+ * deadline: 1_700_000_000n,
18922
+ * chainId: 5_042_002,
18923
+ * })
18924
+ * ```
18925
+ *
18926
+ * @internal
18927
+ */ function buildDepositIntentTypedData(params) {
18928
+ return {
18929
+ domain: {
18930
+ ...DEPOSIT_INTENT_DOMAIN,
18931
+ chainId: params.chainId
18932
+ },
18933
+ types: DEPOSIT_INTENT_TYPES,
18934
+ primaryType: 'DepositIntent',
18935
+ message: {
18936
+ wallet: params.wallet,
18937
+ vault: params.vault,
18938
+ amount: params.amount,
18939
+ deadline: params.deadline
18940
+ }
18941
+ };
18942
+ }
18943
+ /**
18944
+ * Earn Service error code for a missing, expired, or wrongly signed
18945
+ * `DepositIntent` (`DEPOSIT_INTENT_INVALID`).
18946
+ */ const DEPOSIT_INTENT_INVALID_CODE = 380418;
18947
+ /**
18948
+ * Sign a `DepositIntent` when the target vault needs one.
18949
+ *
18950
+ * A Veda vault mints shares straight to the depositing wallet, so the Earn
18951
+ * Service only builds a Veda deposit for a wallet that signed a
18952
+ * `DepositIntent` for the same vault and amount. The vault lookup exists only
18953
+ * to find out whether the vault is a Veda vault (and its asset decimals, to
18954
+ * sign the amount). Other vaults ignore the intent, so this returns no intent
18955
+ * for them and the wallet sees no extra signature prompt. A vault the service
18956
+ * does not return is also treated as not needing an intent; the deposit call
18957
+ * then reports why.
18958
+ *
18959
+ * A failed vault lookup does not stop the deposit: this returns the lookup
18960
+ * error with no intent, so a non-Veda deposit works as before. A Veda deposit
18961
+ * then gets `DEPOSIT_INTENT_INVALID` from the service, and
18962
+ * {@link resolveDepositError} reports the lookup error instead. The lookup
18963
+ * fails either by throwing or by returning the vault in its `errors` list (for
18964
+ * example `VAULT_REFRESH_BUSY`); both are kept the same way.
18965
+ *
18966
+ * @param params - Adapter, resolved chain, wallet, vault, human-readable
18967
+ * amount, and service config.
18968
+ * @returns The signed intent when the vault needs one, and the lookup error
18969
+ * when the vault lookup failed.
18970
+ * @throws {@link KitError} `InputError.VALIDATION_FAILED` when the vault needs
18971
+ * an intent but the chain is not EVM, the adapter cannot sign typed data, or
18972
+ * the amount has more decimal places than the vault asset.
18973
+ *
18974
+ * @example
18975
+ * ```typescript
18976
+ * const { intent } = await resolveDepositIntent({
18977
+ * adapter,
18978
+ * chain: ArcTestnet,
18979
+ * address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
18980
+ * vaultAddress: '0x00000000000000000000000000000000000000a1',
18981
+ * amount: '100',
18982
+ * config,
18983
+ * })
18984
+ * // intent -> { intentSignature: '0x…', intentDeadline: 1700000240 }
18985
+ * ```
18986
+ *
18987
+ * @internal
18988
+ */ async function resolveDepositIntent(params) {
18989
+ let lookup;
18990
+ try {
18991
+ lookup = await fetchVaults({
18992
+ // `resolveAdapterContext` already rejected chains the Earn Service does
18993
+ // not support, so the chain is an Earn chain here.
18994
+ vaults: [
18995
+ {
18996
+ chain: params.chain,
18997
+ vaultAddress: params.vaultAddress
18998
+ }
18999
+ ],
19000
+ config: params.config
19001
+ });
19002
+ } catch (lookupError) {
19003
+ return {
19004
+ lookupError
19005
+ };
19006
+ }
19007
+ const vaultError = lookup.errors.find((candidate)=>isSameAddress(candidate.vaultAddress, params.vaultAddress));
19008
+ if (vaultError !== undefined) {
19009
+ // Same handling as a thrown lookup error, mapped with the Earn Service
19010
+ // error table so it keeps its recoverability (VAULT_REFRESH_BUSY retries).
19011
+ return {
19012
+ lookupError: parseEarnApiError({
19013
+ responseBody: {
19014
+ code: vaultError.code,
19015
+ message: vaultError.message
19016
+ }
19017
+ }, {
19018
+ operation: 'getVaults'
19019
+ })
19020
+ };
19021
+ }
19022
+ const vault = lookup.vaults.find((candidate)=>isSameAddress(candidate.address, params.vaultAddress));
19023
+ if (vault?.protocol.toLowerCase() !== VEDA_PROTOCOL) {
19024
+ return {};
19025
+ }
19026
+ if (params.chain.type !== 'evm') {
19027
+ throw createValidationFailedError('from.chain', params.chain.chain, 'Veda vault deposits need an EVM chain to sign the deposit intent');
19028
+ }
19029
+ if (!canSignTypedData(params.adapter)) {
19030
+ throw createValidationFailedError('from.adapter', params.adapter, 'adapter must support signTypedData for Veda vault deposits');
19031
+ }
19032
+ const wallet = assertHexAddress('from.address', params.address, 'Depositor address must be a 0x-prefixed 20-byte hex address.');
19033
+ const assetDecimals = vault.liquidityProfile.totalDeposits.decimals;
19034
+ // Amount.parse truncates extra digits, but the service rejects them. Fail
19035
+ // before the wallet prompt instead of asking to sign a deposit that cannot
19036
+ // go through.
19037
+ assertDecimalPlaces('amount', params.amount, assetDecimals, `amount must have at most ${String(assetDecimals)} decimal places for the vault asset`);
19038
+ const deadline = Math.floor(Date.now() / 1000) + DEPOSIT_INTENT_TTL_SECONDS;
19039
+ const typedData = buildDepositIntentTypedData({
19040
+ wallet,
19041
+ vault: params.vaultAddress,
19042
+ amount: Amount.parse(params.amount, {
19043
+ decimals: assetDecimals
19044
+ }).raw,
19045
+ deadline: BigInt(deadline),
19046
+ chainId: params.chain.chainId
19047
+ });
19048
+ // `typedData.domain` intentionally omits `verifyingContract` (the Earn
19049
+ // Service verifies off-chain), but the shared `signTypedData` signature
19050
+ // requires the standard `EIP712Domain` shape. The adapter's runtime
19051
+ // `eip712DomainSchema` treats `verifyingContract` as optional, so this cast
19052
+ // is safe at runtime.
19053
+ const intentSignature = await params.adapter.signTypedData(typedData, {
19054
+ chain: params.chain,
19055
+ address: wallet
19056
+ });
19057
+ return {
19058
+ intent: {
19059
+ intentSignature,
19060
+ intentDeadline: deadline
19061
+ }
19062
+ };
19063
+ }
19064
+ /**
19065
+ * Pick the error to report for a failed deposit request.
19066
+ *
19067
+ * When the vault lookup failed, the SDK sent the deposit without an intent.
19068
+ * If the service then answers `DEPOSIT_INTENT_INVALID`, the vault is a Veda
19069
+ * vault and the real cause is the failed lookup, not a bad signature. Report
19070
+ * the lookup error, which keeps its own recoverability (often retryable),
19071
+ * instead of a fatal signature rejection.
19072
+ *
19073
+ * When the SDK did send an intent and the service still answers
19074
+ * `DEPOSIT_INTENT_INVALID`, the error stays a fatal signature rejection, but
19075
+ * its message adds that the intent may have expired: the deadline starts
19076
+ * before the wallet prompt, so a slow signer can submit an expired intent.
19077
+ * Calling `deposit()` again signs a new one. Every other error is reported as
19078
+ * is.
19079
+ *
19080
+ * @param resolution - The result of {@link resolveDepositIntent}.
19081
+ * @param error - The error from the deposit request.
19082
+ * @returns The error to throw.
19083
+ *
19084
+ * @example
19085
+ * ```typescript
19086
+ * try {
19087
+ * return await fetchDeposit({ ...params, intent: resolution.intent })
19088
+ * } catch (error) {
19089
+ * throw resolveDepositError(resolution, error)
19090
+ * }
19091
+ * ```
19092
+ *
19093
+ * @internal
19094
+ */ function resolveDepositError(resolution, error) {
19095
+ if (resolution.lookupError !== undefined && isKitError(error) && isEarnServiceCode(error.cause?.trace, DEPOSIT_INTENT_INVALID_CODE)) {
19096
+ return resolution.lookupError;
19097
+ }
19098
+ if (resolution.intent !== undefined && isKitError(error) && isEarnServiceCode(error.cause?.trace, DEPOSIT_INTENT_INVALID_CODE)) {
19099
+ return new KitError({
19100
+ code: error.code,
19101
+ name: error.name,
19102
+ type: error.type,
19103
+ recoverability: error.recoverability,
19104
+ 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.`,
19105
+ ...error.cause !== undefined && {
19106
+ cause: error.cause
19107
+ }
19108
+ });
19109
+ }
19110
+ return error;
19111
+ }
19112
+ function isEarnServiceCode(trace, code) {
19113
+ return typeof trace === 'object' && trace !== null && 'earnServiceCode' in trace && trace.earnServiceCode === code;
19114
+ }
19115
+
18347
19116
  /**
18348
19117
  * Build cross-chain deposit instructions via the bridge API.
18349
19118
  *
@@ -19226,7 +19995,7 @@ function reportTransactionFailure(reportContext, action, error) {
19226
19995
  }
19227
19996
  function transactionReportErrorCode(error) {
19228
19997
  if (isKitError(error)) {
19229
- return error.name;
19998
+ return mapVedaRevert(error).name;
19230
19999
  }
19231
20000
  const message = getErrorMessage(error);
19232
20001
  if (/user (rejected|denied)|rejected by user/i.test(message)) {
@@ -19572,13 +20341,31 @@ function finishElapsedWait(lastStatus, lastError) {
19572
20341
  const adapterContractAddress = requireAdapterContract(chain);
19573
20342
  const { adapter } = params.from;
19574
20343
  const vaultAddress = assertHexAddress('vaultAddress', params.vaultAddress, 'Vault address must be a 0x-prefixed 20-byte hex address.');
19575
- const { execId, executionParams, signature } = await this.runPhase(ctx, 'deposit', 'fetchParams', async ()=>fetchDeposit({
20344
+ const { execId, executionParams, signature } = await this.runPhase(ctx, 'deposit', 'fetchParams', async ()=>{
20345
+ // Veda vaults mint shares straight to `address`, so the service
20346
+ // needs proof that `address` asked for this deposit. Sign it here,
20347
+ // inside fetchParams, so a rejected prompt fails this phase.
20348
+ const resolution = await resolveDepositIntent({
20349
+ adapter,
20350
+ chain,
20351
+ address,
19576
20352
  vaultAddress,
19577
20353
  amount: params.amount,
19578
- address,
19579
- chain: apiChain,
19580
20354
  config
19581
- }), ()=>undefined);
20355
+ });
20356
+ try {
20357
+ return await fetchDeposit({
20358
+ vaultAddress,
20359
+ amount: params.amount,
20360
+ address,
20361
+ chain: apiChain,
20362
+ intent: resolution.intent,
20363
+ config
20364
+ });
20365
+ } catch (error) {
20366
+ throw resolveDepositError(resolution, error);
20367
+ }
20368
+ }, ()=>undefined);
19582
20369
  validateExecutionDeadline(executionParams);
19583
20370
  const transactionReportContext = {
19584
20371
  execId,
@@ -22585,7 +23372,7 @@ registerKit(`${pkg$2.name}/${pkg$2.version}`);
22585
23372
  };
22586
23373
 
22587
23374
  var name = "@circle-fin/borrow-kit";
22588
- var version = "1.0.0";
23375
+ var version = "1.1.1";
22589
23376
  var pkg = {
22590
23377
  name: name,
22591
23378
  version: version};