@circle-fin/app-kit 1.15.3 → 1.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/CHANGELOG.md +91 -0
  2. package/README.md +5 -2
  3. package/borrow.cjs +28146 -0
  4. package/borrow.cjs.map +1 -0
  5. package/borrow.d.cts +10752 -0
  6. package/borrow.d.mts +10752 -0
  7. package/borrow.d.ts +10752 -0
  8. package/borrow.mjs +28118 -0
  9. package/borrow.mjs.map +1 -0
  10. package/bridge.cjs +2084 -328
  11. package/bridge.d.cts +121 -18
  12. package/bridge.d.mts +121 -18
  13. package/bridge.d.ts +121 -18
  14. package/bridge.mjs +2084 -328
  15. package/chains.cjs +78 -7
  16. package/chains.d.cts +2 -0
  17. package/chains.d.mts +2 -0
  18. package/chains.d.ts +2 -0
  19. package/chains.mjs +78 -7
  20. package/context.cjs +1 -0
  21. package/context.d.cts +121 -18
  22. package/context.d.mts +121 -18
  23. package/context.d.ts +121 -18
  24. package/context.mjs +1 -0
  25. package/earn.cjs +5322 -3219
  26. package/earn.d.cts +1149 -984
  27. package/earn.d.mts +1149 -984
  28. package/earn.d.ts +1149 -984
  29. package/earn.mjs +5322 -3219
  30. package/estimateBridge.cjs +2082 -326
  31. package/estimateBridge.d.cts +121 -18
  32. package/estimateBridge.d.mts +121 -18
  33. package/estimateBridge.d.ts +121 -18
  34. package/estimateBridge.mjs +2082 -326
  35. package/estimateSwap.cjs +8817 -6974
  36. package/estimateSwap.d.cts +169 -18
  37. package/estimateSwap.d.mts +169 -18
  38. package/estimateSwap.d.ts +169 -18
  39. package/estimateSwap.mjs +8822 -6979
  40. package/index.cjs +33149 -20719
  41. package/index.d.cts +23174 -2736
  42. package/index.d.mts +23174 -2736
  43. package/index.d.ts +23174 -2736
  44. package/index.mjs +33090 -20713
  45. package/package.json +17 -7
  46. package/server.cjs +133 -12
  47. package/server.d.cts +29 -10
  48. package/server.d.mts +29 -10
  49. package/server.d.ts +29 -10
  50. package/server.mjs +133 -12
  51. package/swap.cjs +8817 -6974
  52. package/swap.d.cts +169 -18
  53. package/swap.d.mts +169 -18
  54. package/swap.d.ts +169 -18
  55. package/swap.mjs +8822 -6979
  56. package/unifiedBalance.cjs +528 -138
  57. package/unifiedBalance.d.cts +108 -11
  58. package/unifiedBalance.d.mts +108 -11
  59. package/unifiedBalance.d.ts +108 -11
  60. package/unifiedBalance.mjs +528 -138
package/bridge.mjs CHANGED
@@ -1360,6 +1360,34 @@ let activeRedactor = defaultMessageRedactor;
1360
1360
  return redactErrorMessage(String(error));
1361
1361
  }
1362
1362
 
1363
+ /**
1364
+ * Render an object value for an error message without letting the rendering
1365
+ * itself throw.
1366
+ *
1367
+ * `JSON.stringify` rejects `BigInt` and circular structures, and a rejected
1368
+ * value is exactly the kind of thing a validation error is built from: an
1369
+ * on-chain read or a receipt log carries `bigint` fields, so serializing one
1370
+ * naively replaces the intended error with a `TypeError`.
1371
+ *
1372
+ * @param value - The value to render.
1373
+ * @returns A JSON string with `bigint` fields stringified, or
1374
+ * `'[unserializable object]'` if serialization still fails.
1375
+ * @throws Never; serialization failures are caught and rendered as a fallback string.
1376
+ * @example
1377
+ * ```typescript
1378
+ * import { describeObjectValue } from '@core/errors'
1379
+ *
1380
+ * describeObjectValue({ amount: 1n })
1381
+ * // '{"amount":"1"}'
1382
+ * ```
1383
+ */ function describeObjectValue(value) {
1384
+ try {
1385
+ return JSON.stringify(value, (_key, entry)=>typeof entry === 'bigint' ? entry.toString() : entry);
1386
+ } catch {
1387
+ return '[unserializable object]';
1388
+ }
1389
+ }
1390
+
1363
1391
  /**
1364
1392
  * Creates error for network type mismatch between source and destination.
1365
1393
  *
@@ -1622,7 +1650,7 @@ let activeRedactor = defaultMessageRedactor;
1622
1650
  * // Message: "Validation failed for 'chainId': 999 - Unsupported chain ID"
1623
1651
  *
1624
1652
  * throw createValidationFailedError('config', { invalid: true }, 'Missing required properties')
1625
- * // Message: "Validation failed for 'config': [object Object] - Missing required properties"
1653
+ * // Message: "Validation failed for 'config': {"invalid":true} - Missing required properties"
1626
1654
  * ```
1627
1655
  */ function createValidationFailedError$1(field, value, reason) {
1628
1656
  // Convert value to string for display, handling different types appropriately
@@ -1630,7 +1658,7 @@ let activeRedactor = defaultMessageRedactor;
1630
1658
  if (typeof value === 'string') {
1631
1659
  valueString = `'${value}'`;
1632
1660
  } else if (typeof value === 'object' && value !== null) {
1633
- valueString = JSON.stringify(value);
1661
+ valueString = describeObjectValue(value);
1634
1662
  } else {
1635
1663
  valueString = String(value);
1636
1664
  }
@@ -2487,22 +2515,73 @@ let activeRedactor = defaultMessageRedactor;
2487
2515
  '0x9147d463': 'Final balance is less than the initial balance',
2488
2516
  '0xf4b3b1bc': 'Native token transfer to the beneficiary failed'
2489
2517
  };
2518
+ /**
2519
+ * Matches revert data: a 4-byte selector followed by whole ABI-encoded bytes.
2520
+ */ const REVERT_DATA_PATTERN = /^0x[0-9a-f]{8}(?:[0-9a-f]{2})*$/i;
2521
+ /**
2522
+ * Returns the revert data held by one error in a `cause` chain.
2523
+ *
2524
+ * Browser wallets (EIP-1193) wrap the node's error, so the revert data sits
2525
+ * one level down as `data.data`. viem's `getContractError` reads it the same
2526
+ * way, then keeps it on `ContractFunctionRevertedError.raw` and drops the
2527
+ * node's error from the chain.
2528
+ *
2529
+ * @param link - One error in the `cause` chain
2530
+ * @returns The `raw` or `data` string, or undefined if there is none
2531
+ */ function getRevertData(link) {
2532
+ if ('raw' in link && typeof link.raw === 'string' && REVERT_DATA_PATTERN.test(link.raw)) {
2533
+ return link.raw;
2534
+ }
2535
+ if (!('data' in link)) {
2536
+ return undefined;
2537
+ }
2538
+ const { data } = link;
2539
+ if (typeof data === 'string') {
2540
+ return data;
2541
+ }
2542
+ if (data !== null && typeof data === 'object' && 'data' in data && typeof data.data === 'string') {
2543
+ return data.data;
2544
+ }
2545
+ return undefined;
2546
+ }
2547
+ /**
2548
+ * Finds the first revert data on the error or in its `cause` chain that
2549
+ * `accept` takes.
2550
+ *
2551
+ * viem keeps the node's revert data on an inner `RpcRequestError`, not on
2552
+ * the error it throws. The `seen` set stops a self-referencing `cause` chain.
2553
+ *
2554
+ * @param error - The error object
2555
+ * @param accept - Returns true for the data shape the caller wants
2556
+ * @returns The data as written on the error, or undefined if not found
2557
+ */ function findRevertData(error, accept) {
2558
+ const seen = new Set();
2559
+ let current = error;
2560
+ while(current !== null && typeof current === 'object' && !seen.has(current)){
2561
+ seen.add(current);
2562
+ const data = getRevertData(current);
2563
+ if (data !== undefined && accept(data)) {
2564
+ return data;
2565
+ }
2566
+ current = 'cause' in current ? current.cause : undefined;
2567
+ }
2568
+ return undefined;
2569
+ }
2490
2570
  /**
2491
2571
  * Attempts to extract an error selector from an error object or message.
2492
2572
  *
2493
2573
  * Checks multiple sources in priority order:
2494
- * 1. `error.data` field
2574
+ * 1. `raw` or `data` field (or nested `data.data`) on the error or anywhere
2575
+ * in its `cause` chain
2495
2576
  * 2. Message pattern: "custom error 0x8baa579f"
2496
2577
  *
2497
2578
  * @param msg - The error message
2498
2579
  * @param error - The error object (optional)
2499
2580
  * @returns The 4 bytes selector , or undefined if not found
2500
2581
  */ function extractErrorSelector(msg, error) {
2501
- // Check error.data field
2502
- if (error !== null && typeof error === 'object' && 'data' in error) {
2503
- if (typeof error.data === 'string' && /^0x[0-9a-f]{8}$/i.test(error.data)) {
2504
- return error.data.toLowerCase();
2505
- }
2582
+ const data = findRevertData(error, (value)=>/^0x[0-9a-f]{8}$/i.test(value));
2583
+ if (data !== undefined) {
2584
+ return data.toLowerCase();
2506
2585
  }
2507
2586
  // Check for "custom error 0x8baa579f" in the message
2508
2587
  const match = /custom error (0x[0-9a-f]{8})\b/i.exec(msg);
@@ -3128,6 +3207,105 @@ let activeRedactor = defaultMessageRedactor;
3128
3207
  return chain;
3129
3208
  }
3130
3209
 
3210
+ /**
3211
+ * Extracts the HTTP status code from an error message.
3212
+ *
3213
+ * Attempts to parse HTTP status codes from common error message formats,
3214
+ * such as "HTTP 404" or "Status: 500".
3215
+ *
3216
+ * Shared by `parseApiError` and the backend-error-code parsers that route on
3217
+ * status when no backend code is available, so all of them recognise the same
3218
+ * message shapes.
3219
+ *
3220
+ * @param msg - The error message to extract from
3221
+ * @returns The extracted HTTP status code, or null if not found
3222
+ *
3223
+ * @example
3224
+ * ```typescript
3225
+ * const code = extractHttpStatusCode('HTTP 404')
3226
+ * // Returns: 404
3227
+ * ```
3228
+ *
3229
+ * @example
3230
+ * ```typescript
3231
+ * const code = extractHttpStatusCode('Status: 500 Internal Server Error')
3232
+ * // Returns: 500
3233
+ * ```
3234
+ *
3235
+ * @internal
3236
+ */ function extractHttpStatusCode(msg) {
3237
+ // Pattern: "HTTP 404" or "HTTP 404 - some message" or "Status: 404"
3238
+ const patterns = [
3239
+ /HTTP (\d{3})/i,
3240
+ /Status:\s*(\d{3})/i,
3241
+ /^(\d{3}) -/
3242
+ ];
3243
+ for (const pattern of patterns){
3244
+ const match = pattern.exec(msg);
3245
+ const codeStr = match?.at(1);
3246
+ if (codeStr !== undefined) {
3247
+ const code = Number.parseInt(codeStr, 10);
3248
+ // Validate it's a valid HTTP status code
3249
+ if (code >= 100 && code < 600) {
3250
+ return code;
3251
+ }
3252
+ }
3253
+ }
3254
+ return null;
3255
+ }
3256
+
3257
+ /**
3258
+ * Type guard that narrows an unknown error to one carrying a non-null
3259
+ * object `responseBody` property (attached by `makeApiRequest`).
3260
+ *
3261
+ * Shared by backend-error-code parsers (`parseEarnApiError`,
3262
+ * `parseBorrowApiError`) so the narrowing logic lives in one place.
3263
+ *
3264
+ * @param error - The raw error from the HTTP layer
3265
+ * @returns `true` when `error.responseBody` is a non-null object
3266
+ * @internal
3267
+ */ function hasResponseBody(error) {
3268
+ return typeof error === 'object' && error !== null && 'responseBody' in error && typeof error['responseBody'] === 'object' && error['responseBody'] !== null;
3269
+ }
3270
+ /**
3271
+ * Narrow an unknown value to a non-null object.
3272
+ *
3273
+ * @param value - The value to check
3274
+ * @returns `true` when `value` is a non-null object
3275
+ * @internal
3276
+ */ function isRecord(value) {
3277
+ return typeof value === 'object' && value !== null;
3278
+ }
3279
+ /**
3280
+ * Read the machine-readable error fields (`code`, `message`,
3281
+ * `externalMessage`) that a Circle service attaches to an HTTP error body.
3282
+ *
3283
+ * `platform-common-go` serializes app-thrown errors under a nested envelope —
3284
+ * `{ response: { status: { code, message, externalMessage } } }` — for every
3285
+ * HTTP status, so this returns that inner `response.status` object when
3286
+ * present. It otherwise falls back to the top-level body so a flattened shape
3287
+ * (e.g. the stablecoin-kits-proxy) is still read. Callers read
3288
+ * `code` / `message` / `externalMessage` off the returned object uniformly.
3289
+ *
3290
+ * @param error - The raw error from the HTTP layer
3291
+ * @returns The error-status fields, or `undefined` when the error carries no
3292
+ * response body
3293
+ * @internal
3294
+ */ function extractResponseBody$1(error) {
3295
+ if (!hasResponseBody(error)) {
3296
+ return undefined;
3297
+ }
3298
+ const body = error.responseBody;
3299
+ const response = body['response'];
3300
+ if (isRecord(response)) {
3301
+ const status = response['status'];
3302
+ if (isRecord(status)) {
3303
+ return status;
3304
+ }
3305
+ }
3306
+ return body;
3307
+ }
3308
+
3131
3309
  /**
3132
3310
  * Proxy-specific structured error codes carried in `responseBody.code`.
3133
3311
  *
@@ -3428,29 +3606,22 @@ let activeRedactor = defaultMessageRedactor;
3428
3606
  });
3429
3607
  }
3430
3608
  /**
3431
- * Type guard that narrows an unknown error to one carrying a non-null
3432
- * object `responseBody` property (attached by `makeApiRequest`).
3609
+ * Extract the machine-readable error fields that `makeApiRequest` attaches to
3610
+ * HTTP error instances.
3433
3611
  *
3434
- * @param error - The raw error from the HTTP layer
3435
- * @returns `true` when `error.responseBody` is a non-null object
3436
- * @internal
3437
- */ function hasResponseBody(error) {
3438
- return typeof error === 'object' && error !== null && 'responseBody' in error && typeof error['responseBody'] === 'object' && error['responseBody'] !== null;
3439
- }
3440
- /**
3441
- * Extract the `responseBody` property that `makeApiRequest` attaches to
3442
- * HTTP error instances when the server returns a JSON body.
3612
+ * Delegates to the shared extractor so this fall-through path reads the same
3613
+ * shape as the backend-code parsers: it unwraps the nested
3614
+ * `{ response: { status } }` envelope when present, otherwise returns the
3615
+ * top-level body. Routing both halves through one extractor keeps an
3616
+ * unmapped-code error from losing the server's `externalMessage`.
3443
3617
  *
3444
3618
  * @param error - The raw error from the HTTP layer
3445
- * @returns The parsed body cast to {@link ApiErrorResponseBody}, or undefined
3619
+ * @returns The parsed body as {@link ApiErrorResponseBody}, or undefined
3446
3620
  * @throws Never. Returns `undefined` when the error does not carry a valid
3447
3621
  * `responseBody`.
3448
3622
  * @internal
3449
3623
  */ function extractResponseBody(error) {
3450
- if (!hasResponseBody(error)) {
3451
- return undefined;
3452
- }
3453
- return error.responseBody;
3624
+ return extractResponseBody$1(error);
3454
3625
  }
3455
3626
  /**
3456
3627
  * Extract a field name from an {@link ApiFieldError}.
@@ -3559,46 +3730,6 @@ let activeRedactor = defaultMessageRedactor;
3559
3730
  }
3560
3731
  return topMessage ?? fieldDetail;
3561
3732
  }
3562
- /**
3563
- * Extracts the HTTP status code from an error message.
3564
- *
3565
- * Attempts to parse HTTP status codes from common error message formats,
3566
- * such as "HTTP 404" or "Status: 500".
3567
- *
3568
- * @param msg - The error message to extract from
3569
- * @returns The extracted HTTP status code, or null if not found
3570
- *
3571
- * @example
3572
- * ```typescript
3573
- * const code = extractHttpStatusCode('HTTP 404')
3574
- * // Returns: 404
3575
- * ```
3576
- *
3577
- * @example
3578
- * ```typescript
3579
- * const code = extractHttpStatusCode('Status: 500 Internal Server Error')
3580
- * // Returns: 500
3581
- * ```
3582
- */ function extractHttpStatusCode(msg) {
3583
- // Pattern: "HTTP 404" or "HTTP 404 - some message" or "Status: 404"
3584
- const patterns = [
3585
- /HTTP (\d{3})/i,
3586
- /Status:\s*(\d{3})/i,
3587
- /^(\d{3}) -/
3588
- ];
3589
- for (const pattern of patterns){
3590
- const match = pattern.exec(msg);
3591
- const codeStr = match?.at(1);
3592
- if (codeStr !== undefined) {
3593
- const code = Number.parseInt(codeStr, 10);
3594
- // Validate it's a valid HTTP status code
3595
- if (code >= 100 && code < 600) {
3596
- return code;
3597
- }
3598
- }
3599
- }
3600
- return null;
3601
- }
3602
3733
 
3603
3734
  /**
3604
3735
  * @packageDocumentation
@@ -3723,6 +3854,8 @@ var SwapChain;
3723
3854
  SwapChain["HyperEVM"] = "HyperEVM";
3724
3855
  SwapChain["Monad"] = "Monad";
3725
3856
  SwapChain["Arc"] = "Arc";
3857
+ SwapChain["Cronos"] = "Cronos";
3858
+ SwapChain["Morph"] = "Morph";
3726
3859
  // Testnet chains with swap support
3727
3860
  SwapChain["Arc_Testnet"] = "Arc_Testnet";
3728
3861
  })(SwapChain || (SwapChain = {}));
@@ -3854,6 +3987,11 @@ var EarnChain;
3854
3987
  "Arc",
3855
3988
  "Arc_Testnet"
3856
3989
  ];
3990
+ var BorrowChain;
3991
+ (function(BorrowChain) {
3992
+ BorrowChain["Arc"] = "Arc";
3993
+ BorrowChain["Arc_Testnet"] = "Arc_Testnet";
3994
+ })(BorrowChain || (BorrowChain = {}));
3857
3995
 
3858
3996
  /**
3859
3997
  * Helper function to define a chain with proper TypeScript typing.
@@ -4196,6 +4334,29 @@ var EarnChain;
4196
4334
  * on EVM-compatible testnet chains. Use this address for testnet adapter
4197
4335
  * integrations (e.g., Arc Testnet).
4198
4336
  */ const ADAPTER_CONTRACT_EVM_TESTNET = '0xBBD70b01a1CAbc96d5b7b129Ae1AAabdf50dd40b';
4337
+ /**
4338
+ * The sender-preserving batching contract on Arc (`Multicall3From`).
4339
+ *
4340
+ * This predeploy batches calls like the ubiquitous Multicall3, but routes each
4341
+ * subcall through Arc's `CallFrom` precompile
4342
+ * (`0x1800000000000000000000000000000000000003`), which executes on behalf of
4343
+ * the transaction's original sender. Every target therefore sees the calling
4344
+ * EOA as `msg.sender`, which is what makes a plain, un-upgraded EOA capable of
4345
+ * an atomic batch on Arc.
4346
+ *
4347
+ * Deployed at the same address on Arc Testnet and Arc Mainnet.
4348
+ *
4349
+ * @see https://docs.arc.io/arc/concepts/batched-transactions
4350
+ */ const BATCHER_CONTRACT_ARC_TESTNET = '0x522fAf9A91c41c443c66765030741e4AaCe147D0';
4351
+ /**
4352
+ * The sender-preserving batching contract on Arc Mainnet (`Multicall3From`).
4353
+ *
4354
+ * See {@link BATCHER_CONTRACT_ARC_TESTNET} for how this contract works.
4355
+ * Requires Arc's Zero7 hardfork, which activates the `CallFrom` precompile
4356
+ * this contract depends on.
4357
+ *
4358
+ * @see https://docs.arc.io/arc/concepts/batched-transactions
4359
+ */ const BATCHER_CONTRACT_ARC_MAINNET = '0x522fAf9A91c41c443c66765030741e4AaCe147D0';
4199
4360
  /**
4200
4361
  * The GatewayWallet contract address for EVM mainnet networks.
4201
4362
  *
@@ -4335,7 +4496,10 @@ var EarnChain;
4335
4496
  },
4336
4497
  kitContracts: {
4337
4498
  bridge: BRIDGE_CONTRACT_EVM_MAINNET,
4338
- adapter: ADAPTER_CONTRACT_EVM_MAINNET
4499
+ adapter: ADAPTER_CONTRACT_EVM_MAINNET,
4500
+ // Arc is the only chain with a sender-preserving batcher, because it
4501
+ // takes a precompile rather than an ordinary contract to build one.
4502
+ senderPreservingBatcher: BATCHER_CONTRACT_ARC_MAINNET
4339
4503
  },
4340
4504
  gateway: {
4341
4505
  domain: 26,
@@ -4406,7 +4570,10 @@ var EarnChain;
4406
4570
  },
4407
4571
  kitContracts: {
4408
4572
  bridge: BRIDGE_CONTRACT_EVM_TESTNET,
4409
- adapter: ADAPTER_CONTRACT_EVM_TESTNET
4573
+ adapter: ADAPTER_CONTRACT_EVM_TESTNET,
4574
+ // Arc is the only chain with a sender-preserving batcher, because it
4575
+ // takes a precompile rather than an ordinary contract to build one.
4576
+ senderPreservingBatcher: BATCHER_CONTRACT_ARC_TESTNET
4410
4577
  },
4411
4578
  gateway: {
4412
4579
  domain: 26,
@@ -5021,7 +5188,8 @@ var EarnChain;
5021
5188
  }
5022
5189
  },
5023
5190
  kitContracts: {
5024
- bridge: BRIDGE_CONTRACT_EVM_MAINNET
5191
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
5192
+ adapter: ADAPTER_CONTRACT_EVM_MAINNET
5025
5193
  }
5026
5194
  });
5027
5195
 
@@ -5890,7 +6058,8 @@ var EarnChain;
5890
6058
  }
5891
6059
  },
5892
6060
  kitContracts: {
5893
- bridge: BRIDGE_CONTRACT_EVM_MAINNET
6061
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
6062
+ adapter: ADAPTER_CONTRACT_EVM_MAINNET
5894
6063
  }
5895
6064
  });
5896
6065
 
@@ -8076,7 +8245,8 @@ var Chains = /*#__PURE__*/Object.freeze({
8076
8245
  cctpx: z.any().optional(),
8077
8246
  kitContracts: z.object({
8078
8247
  bridge: z.string().optional(),
8079
- adapter: z.string().optional()
8248
+ adapter: z.string().optional(),
8249
+ senderPreservingBatcher: z.string().optional()
8080
8250
  }).optional(),
8081
8251
  gateway: gatewayConfigSchema.optional()
8082
8252
  });
@@ -8187,8 +8357,8 @@ var Chains = /*#__PURE__*/Object.freeze({
8187
8357
  * Zod schema for validating swap-specific chain identifiers.
8188
8358
  *
8189
8359
  * Validates chains based on:
8190
- * - CCTPv2 support (adapter contract deployed)
8191
- * - Mainnet only (no testnets)
8360
+ * - Membership in the SwapChain enum
8361
+ * - Mainnet or explicitly allowlisted testnet
8192
8362
  * - At least one supported token available
8193
8363
  *
8194
8364
  */ const swapChainIdentifierSchema = z.union([
@@ -8272,6 +8442,38 @@ var Chains = /*#__PURE__*/Object.freeze({
8272
8442
  message: `"${chain.chain}" is not a supported earn chain. ` + `Supported chains: ${Object.values(EarnChain).join(', ')}`
8273
8443
  }))
8274
8444
  ]);
8445
+ /**
8446
+ * Zod schema for validating Borrow-specific chain identifiers.
8447
+ *
8448
+ * Validate that the provided chain is supported for Borrow Kit's loan
8449
+ * operations. Unlike {@link earnChainIdentifierSchema}, this includes Arc
8450
+ * mainnet as well as Arc Testnet.
8451
+ *
8452
+ * Accept a BorrowChain enum value, a matching string literal, or a
8453
+ * ChainDefinition for a supported chain.
8454
+ *
8455
+ * @example
8456
+ * ```typescript
8457
+ * import { borrowChainIdentifierSchema } from '@core/chains'
8458
+ * import { Arc, BorrowChain } from '@core/chains'
8459
+ *
8460
+ * // Valid
8461
+ * borrowChainIdentifierSchema.parse(BorrowChain.Arc)
8462
+ * borrowChainIdentifierSchema.parse('Arc')
8463
+ * borrowChainIdentifierSchema.parse(Arc)
8464
+ *
8465
+ * // Invalid (throws ZodError)
8466
+ * borrowChainIdentifierSchema.parse('Solana')
8467
+ * ```
8468
+ */ const borrowChainIdentifierSchema = z.union([
8469
+ z.string().refine((val)=>val in BorrowChain, (val)=>({
8470
+ message: `"${val}" is not a supported borrow chain. ` + `Supported chains: ${Object.values(BorrowChain).join(', ')}`
8471
+ })),
8472
+ z.nativeEnum(BorrowChain),
8473
+ chainDefinitionSchema$2.refine((chain)=>chain.chain in BorrowChain, (chain)=>({
8474
+ message: `"${chain.chain}" is not a supported borrow chain. ` + `Supported chains: ${Object.values(BorrowChain).join(', ')}`
8475
+ }))
8476
+ ]);
8275
8477
  // Widened views so `.includes()` accepts arbitrary strings under strict TS.
8276
8478
  const EARN_BRIDGE_SOURCE_CHAIN_VALUES = EARN_BRIDGE_SOURCE_BLOCKCHAINS;
8277
8479
  const EARN_BRIDGE_DESTINATION_CHAIN_VALUES = EARN_BRIDGE_DESTINATION_BLOCKCHAINS;
@@ -8482,8 +8684,13 @@ const swapTokenEnumSchema = z.enum([
8482
8684
  */ function resolveChainName(value) {
8483
8685
  if (value == null) return undefined;
8484
8686
  if (typeof value === 'string') return value;
8485
- if (typeof value === 'object' && 'chain' in value && typeof value.chain === 'string') {
8486
- return value.chain;
8687
+ try {
8688
+ if (typeof value === 'object' && 'chain' in value) {
8689
+ const chain = value.chain;
8690
+ if (typeof chain === 'string') return chain;
8691
+ }
8692
+ } catch {
8693
+ return undefined;
8487
8694
  }
8488
8695
  return undefined;
8489
8696
  }
@@ -8630,7 +8837,7 @@ const swapTokenEnumSchema = z.enum([
8630
8837
  *
8631
8838
  * A chain supports swaps if ALL conditions are met:
8632
8839
  * 1. Is a member of the {@link SwapChain} enum
8633
- * 2. Has CCTPv2 support (adapter contract deployed)
8840
+ * 2. Has an Adapter contract configured directly or through CCTP v2
8634
8841
  *
8635
8842
  * Testnets are allowed only when explicitly listed in the
8636
8843
  * {@link SwapChain} enum (e.g., Arc_Testnet).
@@ -8651,14 +8858,14 @@ const swapTokenEnumSchema = z.enum([
8651
8858
  * @remarks
8652
8859
  * Chains automatically become swap-enabled when:
8653
8860
  * - Added to the {@link SwapChain} enum
8654
- * - Adapter contract is deployed (CCTPv2)
8861
+ * - An Adapter contract is deployed and configured
8655
8862
  *
8656
8863
  * No code changes needed when new chains are added!
8657
8864
  */ function isSwapSupportedChain(chain) {
8658
8865
  if (!(chain.chain in SwapChain)) {
8659
8866
  return false;
8660
8867
  }
8661
- if (!isCCTPV2Supported(chain)) {
8868
+ if (!isCCTPV2Supported(chain) && !hasCustomContractSupport(chain, 'adapter')) {
8662
8869
  return false;
8663
8870
  }
8664
8871
  return true;
@@ -8669,7 +8876,7 @@ const swapTokenEnumSchema = z.enum([
8669
8876
  *
8670
8877
  * Dynamically filter chain definitions to include only those that:
8671
8878
  * - Are members of the {@link SwapChain} enum
8672
- * - Have CCTPv2 support
8879
+ * - Have an Adapter contract configured directly or through CCTP v2
8673
8880
  *
8674
8881
  * This function is pure and accepts chains as parameter to avoid
8675
8882
  * circular dependencies.
@@ -9191,6 +9398,7 @@ const swapTokenEnumSchema = z.enum([
9191
9398
  * - Retries on rate limiting (429)
9192
9399
  * - Does not retry on client errors (4xx, except 429)
9193
9400
  * - Does not retry on validation failures
9401
+ * - Does not retry when `config.isTerminalError` marks the error terminal
9194
9402
  *
9195
9403
  * Each HTTP request is given a configurable timeout before it is aborted.
9196
9404
  * Between retry attempts the wait depends on the `backoff` strategy. The
@@ -9270,7 +9478,10 @@ const swapTokenEnumSchema = z.enum([
9270
9478
  return await makeApiRequest(url, method, isValidType, effectiveConfig, body);
9271
9479
  } catch (err) {
9272
9480
  const error = err instanceof Error ? err : new Error(String(err));
9273
- if (!isRetryableError(error)) {
9481
+ // A caller-supplied predicate can mark an error terminal (e.g. a
9482
+ // deterministic 404) so it is surfaced immediately instead of burning
9483
+ // the retry budget on a non-transient answer.
9484
+ if (effectiveConfig.isTerminalError?.(error) === true || !isRetryableError(error)) {
9274
9485
  throw error;
9275
9486
  }
9276
9487
  lastError = error;
@@ -9354,7 +9565,24 @@ const swapTokenEnumSchema = z.enum([
9354
9565
  * // After this call, TypeScript knows params is of type BridgeParams
9355
9566
  * ```
9356
9567
  */ function validate(value, schema, context) {
9357
- const result = schema.safeParse(value);
9568
+ let result;
9569
+ try {
9570
+ result = schema.safeParse(value);
9571
+ } catch (error) {
9572
+ throw new KitError({
9573
+ ...InputError.VALIDATION_FAILED,
9574
+ recoverability: 'FATAL',
9575
+ message: `Invalid ${context}: reading a property threw`,
9576
+ cause: {
9577
+ trace: {
9578
+ validationErrors: [
9579
+ 'Reading a property of the value threw'
9580
+ ],
9581
+ originalError: error
9582
+ }
9583
+ }
9584
+ });
9585
+ }
9358
9586
  if (!result.success) {
9359
9587
  throw createValidationErrorFromZod(result.error, context);
9360
9588
  }
@@ -10082,11 +10310,13 @@ function parseOrThrow(value, schema, context) {
10082
10310
  [Blockchain.Arbitrum]: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9',
10083
10311
  [Blockchain.Avalanche]: '0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7',
10084
10312
  [Blockchain.Celo]: '0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e',
10313
+ [Blockchain.Cronos]: '0x66e428c3f67a68878562e79A0234c1F83c208770',
10085
10314
  [Blockchain.Ethereum]: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
10086
10315
  [Blockchain.HyperEVM]: '0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb',
10087
10316
  [Blockchain.Ink]: '0x0200C29006150606B650577BBE7B6248F58470c1',
10088
10317
  [Blockchain.Linea]: '0xA219439258ca9da29E9Cc4cE5596924745e12B93',
10089
10318
  [Blockchain.Monad]: '0xe7cd86e13AC4309349F30B3435a9d337750fC82D',
10319
+ [Blockchain.Morph]: '0xe7cd86e13AC4309349F30B3435a9d337750fC82D',
10090
10320
  [Blockchain.NEAR]: 'usdt.tether-token.near',
10091
10321
  [Blockchain.Optimism]: '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58',
10092
10322
  [Blockchain.Polkadot_Asset_Hub]: '1984',
@@ -10145,6 +10375,7 @@ function parseOrThrow(value, schema, context) {
10145
10375
  [Blockchain.Arbitrum]: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1',
10146
10376
  [Blockchain.Avalanche]: '0xd586E7F844cEa2F87f50152665BCbc2C279D8d70',
10147
10377
  [Blockchain.Base]: '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb',
10378
+ [Blockchain.Cronos]: '0xF2001B145b43032AAF5Ee2884e456CCd805F677D',
10148
10379
  [Blockchain.Ethereum]: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
10149
10380
  [Blockchain.Linea]: '0x4AF15ec2A0BD43Db75dd04E62FAA3B8EF36b00d5',
10150
10381
  [Blockchain.Optimism]: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1',
@@ -10207,7 +10438,9 @@ function parseOrThrow(value, schema, context) {
10207
10438
  [Blockchain.Arbitrum]: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
10208
10439
  [Blockchain.Avalanche]: '0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB',
10209
10440
  [Blockchain.Base]: '0x4200000000000000000000000000000000000006',
10441
+ [Blockchain.Cronos]: '0xe44Fd7fCb2b1581822D0c862B68222998a0c299a',
10210
10442
  [Blockchain.Ethereum]: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
10443
+ [Blockchain.Morph]: '0x5300000000000000000000000000000000000011',
10211
10444
  [Blockchain.Optimism]: '0x4200000000000000000000000000000000000006',
10212
10445
  [Blockchain.Polygon]: '0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619',
10213
10446
  [Blockchain.Solana]: '7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs'
@@ -10226,6 +10459,7 @@ function parseOrThrow(value, schema, context) {
10226
10459
  locators: {
10227
10460
  [Blockchain.Ethereum]: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599',
10228
10461
  [Blockchain.Arbitrum]: '0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f',
10462
+ [Blockchain.Cronos]: '0x062E66477Faf219F25D27dCED647BF57C3107d52',
10229
10463
  [Blockchain.Avalanche]: '0x50b7545627a5162F82A992c33b87aDc75187B218'
10230
10464
  }
10231
10465
  };
@@ -12055,11 +12289,11 @@ function resolveOptions(options) {
12055
12289
  void emitAnalyticsLog(buildPayload(config, stepEntry?.[1] ?? fallbackEventType, errorDetails, context));
12056
12290
  }
12057
12291
 
12058
- var name$2 = "@circle-fin/bridge-kit";
12059
- var version$3 = "1.15.1";
12060
- var pkg$3 = {
12061
- name: name$2,
12062
- version: version$3};
12292
+ var name$3 = "@circle-fin/bridge-kit";
12293
+ var version$4 = "1.15.3";
12294
+ var pkg$4 = {
12295
+ name: name$3,
12296
+ version: version$4};
12063
12297
 
12064
12298
  const assertCustomFeePolicySymbol = Symbol('assertCustomFeePolicy');
12065
12299
  /**
@@ -12628,6 +12862,12 @@ const READ_ACTION_KEY_SET = new Set(READ_ACTION_KEYS);
12628
12862
  /** EIP-2612 standard permit */ PermitType[PermitType["EIP2612"] = 1] = "EIP2612";
12629
12863
  })(PermitType || (PermitType = {}));
12630
12864
 
12865
+ var BorrowPermitType;
12866
+ (function(BorrowPermitType) {
12867
+ /** Tokens must be approved separately. */ BorrowPermitType[BorrowPermitType["NONE"] = 0] = "NONE";
12868
+ /** EIP-2612 permit calldata accompanies the token input. */ BorrowPermitType[BorrowPermitType["EIP2612"] = 1] = "EIP2612";
12869
+ })(BorrowPermitType || (BorrowPermitType = {}));
12870
+
12631
12871
  /**
12632
12872
  * Abstract base class for bridging providers that implement cross-chain bridging protocols.
12633
12873
  *
@@ -14615,7 +14855,11 @@ function createNoopLogger() {
14615
14855
  tokenId: bytes32Hex,
14616
14856
  domainId: nonNegativeInteger,
14617
14857
  tokenAddress: z.string(),
14618
- tokenManagerAddress: z.string()
14858
+ tokenManagerAddress: z.string(),
14859
+ // Operator-controlled per-chain kill switch (CCTPE-3979). Optional for
14860
+ // back-compat with older IRIS versions that do not surface it. Downstream
14861
+ // consumers in `findCCTPXDeployment` treat a missing value as `true`.
14862
+ enabled: z.boolean().optional()
14619
14863
  });
14620
14864
  /**
14621
14865
  * Schema for one token entry in the token registry response.
@@ -15099,17 +15343,23 @@ function createNoopLogger() {
15099
15343
  /**
15100
15344
  * Find a token's deployment on the given CCTP domain.
15101
15345
  *
15346
+ * A deployment row with `enabled === false` is treated as missing: an IRIS
15347
+ * operator has flipped the per-chain kill switch (CCTPE-3979), so the SDK must
15348
+ * not route through it even though the row still exists in the registry. A
15349
+ * missing or `true` `enabled` value remains routable — the field is optional on
15350
+ * older IRIS versions and absence is read as "allowed".
15351
+ *
15102
15352
  * @param token - The registry token whose deployments are searched.
15103
15353
  * @param domain - The CCTP domain id to match.
15104
15354
  * @returns The matching {@link CCTPXTokenDeployment}, or `undefined` when the
15105
- * token has no deployment on that domain.
15355
+ * token has no deployment on that domain or the deployment is disabled.
15106
15356
  *
15107
15357
  * @example
15108
15358
  * ```typescript
15109
15359
  * const deployment = findCCTPXDeployment(token, 0)
15110
15360
  * ```
15111
15361
  */ function findCCTPXDeployment(token, domain) {
15112
- return token.deployments.find((deployment)=>deployment.domainId === domain);
15362
+ return token.deployments.find((deployment)=>deployment.domainId === domain && deployment.enabled !== false);
15113
15363
  }
15114
15364
 
15115
15365
  /**
@@ -15211,6 +15461,24 @@ function createNoopLogger() {
15211
15461
  }
15212
15462
  }
15213
15463
  })
15464
+ ],
15465
+ // The token is not deployed on one of the route chains, or an operator has
15466
+ // flipped the per-chain `enabled` kill switch on the IRIS deployment row.
15467
+ // Either way the route is permanently unavailable until the operator intervenes;
15468
+ // a retry at the same source/destination will keep returning this error.
15469
+ [
15470
+ 'TOKEN_NOT_SUPPORTED_ON_ROUTE',
15471
+ ({ sourceDomain, destinationDomain })=>new KitError({
15472
+ ...InputError.UNSUPPORTED_ROUTE,
15473
+ recoverability: 'FATAL',
15474
+ message: 'CCTPx fee quote rejected the route: the token is not supported on ' + 'the source or destination chain (missing deployment or operator-disabled).',
15475
+ cause: {
15476
+ trace: {
15477
+ sourceDomain,
15478
+ destinationDomain
15479
+ }
15480
+ }
15481
+ })
15214
15482
  ]
15215
15483
  ]);
15216
15484
  /**
@@ -19751,7 +20019,7 @@ const defaultSleep = async (ms)=>new Promise((resolve)=>setTimeout(resolve, ms))
19751
20019
  * Must always contain both adapter and chain explicitly.
19752
20020
  *
19753
20021
  * Optionally includes address for developer-controlled adapters.
19754
- */ const adapterContextSchema$3 = z.object({
20022
+ */ const adapterContextSchema$4 = z.object({
19755
20023
  adapter: adapterSchema,
19756
20024
  chain: bridgeChainIdentifierSchema,
19757
20025
  address: z.string().optional()
@@ -19760,14 +20028,14 @@ const defaultSleep = async (ms)=>new Promise((resolve)=>setTimeout(resolve, ms))
19760
20028
  * Schema for validating BridgeDestinationWithAddress objects.
19761
20029
  * Contains an explicit recipientAddress along with adapter and chain.
19762
20030
  * The address format is validated based on the chain type (EVM or Solana).
19763
- */ const bridgeDestinationWithAddressSchema = adapterContextSchema$3.extend({
20031
+ */ const bridgeDestinationWithAddressSchema = adapterContextSchema$4.extend({
19764
20032
  recipientAddress: z.string().min(1, 'Recipient address is required'),
19765
20033
  useForwarder: z.boolean().optional()
19766
20034
  }).superRefine(createRecipientAddressValidator());
19767
20035
  /**
19768
20036
  * Schema for validating AdapterContext with optional useForwarder.
19769
20037
  * Extends adapterContextSchema with the useForwarder flag.
19770
- */ const adapterContextWithForwarderSchema = adapterContextSchema$3.extend({
20038
+ */ const adapterContextWithForwarderSchema = adapterContextSchema$4.extend({
19771
20039
  useForwarder: z.boolean().optional()
19772
20040
  });
19773
20041
  /**
@@ -19842,7 +20110,7 @@ const defaultSleep = async (ms)=>new Promise((resolve)=>setTimeout(resolve, ms))
19842
20110
  * }
19843
20111
  * ```
19844
20112
  */ const bridgeParamsWithChainIdentifierSchema = z.object({
19845
- from: adapterContextSchema$3.strict(),
20113
+ from: adapterContextSchema$4.strict(),
19846
20114
  to: bridgeDestinationSchema,
19847
20115
  amount: z.string().min(1, 'Required').pipe(createDecimalStringValidator({
19848
20116
  allowZero: false,
@@ -20002,7 +20270,7 @@ const defaultSleep = async (ms)=>new Promise((resolve)=>setTimeout(resolve, ms))
20002
20270
  const bridgeKitCaller = {
20003
20271
  type: 'kit',
20004
20272
  name: 'BridgeKit',
20005
- version: pkg$3.version
20273
+ version: pkg$4.version
20006
20274
  };
20007
20275
  // Create default runtime and tokens for invocation context resolution
20008
20276
  const defaults = {
@@ -23221,16 +23489,16 @@ const mockAttestationMessage = {
23221
23489
  return step;
23222
23490
  }
23223
23491
 
23224
- var version$2 = "1.14.0";
23225
- var pkg$2 = {
23226
- version: version$2};
23492
+ var version$3 = "1.14.2";
23493
+ var pkg$3 = {
23494
+ version: version$3};
23227
23495
 
23228
23496
  /**
23229
23497
  * Provider caller component for bridge operations.
23230
23498
  */ const BRIDGE_CALLER = {
23231
23499
  type: 'provider',
23232
23500
  name: 'CCTPV2BridgingProvider.bridge',
23233
- version: pkg$2.version
23501
+ version: pkg$3.version
23234
23502
  };
23235
23503
  /**
23236
23504
  * Resolve invocation context for bridge operations.
@@ -23603,7 +23871,7 @@ var pkg$2 = {
23603
23871
  */ const RETRY_CALLER = {
23604
23872
  type: 'provider',
23605
23873
  name: 'CCTPV2BridgingProvider.retry',
23606
- version: pkg$2.version
23874
+ version: pkg$3.version
23607
23875
  };
23608
23876
  /**
23609
23877
  * Resolve invocation context for retry operations.
@@ -25530,7 +25798,7 @@ function assertCCTPV2Config(config) {
25530
25798
  * Decimal string in token minor units, constrained to be strictly positive.
25531
25799
  *
25532
25800
  * @internal
25533
- */ const positiveAmountSchema = z.string().regex(/^\d+$/, 'must be a non-negative integer string')// Re-check the digit shape here: zod still runs this refinement when the
25801
+ */ const positiveAmountSchema$1 = z.string().regex(/^\d+$/, 'must be a non-negative integer string')// Re-check the digit shape here: zod still runs this refinement when the
25534
25802
  // regex check above fails ("dirty"), so guard BigInt() against throwing on a
25535
25803
  // non-numeric value before comparing.
25536
25804
  .refine((value)=>/^\d+$/.test(value) && BigInt(value) > 0n, 'must be greater than zero');
@@ -25610,7 +25878,7 @@ const preFinalityRequestSchema = z.object({
25610
25878
  */ const fetchFeeQuoteInputSchema = z.object({
25611
25879
  sourceDomain: z.number().int().nonnegative(),
25612
25880
  destinationDomain: z.number().int().nonnegative(),
25613
- amount: positiveAmountSchema,
25881
+ amount: positiveAmountSchema$1,
25614
25882
  feeToken: evmAddressSchema.optional(),
25615
25883
  requests: feeQuoteRequestsSchema,
25616
25884
  isTestnet: z.boolean(),
@@ -26487,7 +26755,7 @@ async function resolveExecutionQuote(binding, suppliedQuote, refundAddress) {
26487
26755
  * The ERC-20 `decimals` field is a `uint8`, so a provider reporting more than
26488
26756
  * this is returning something that cannot describe a real token.
26489
26757
  */ const MAX_TOKEN_DECIMALS = 255;
26490
- /** SDK name used in telemetry payloads. */ const SDK_NAME = resolveKitSdkName(pkg$3.name);
26758
+ /** SDK name used in telemetry payloads. */ const SDK_NAME = resolveKitSdkName(pkg$4.name);
26491
26759
  /**
26492
26760
  * Pick the most-relevant `txHash` to attach to an error telemetry payload.
26493
26761
  *
@@ -26528,7 +26796,7 @@ async function resolveExecutionQuote(binding, suppliedQuote, refundAddress) {
26528
26796
  */ const BRIDGE_KIT_CALLER = {
26529
26797
  type: 'kit',
26530
26798
  name: 'BridgeKit',
26531
- version: pkg$3.version
26799
+ version: pkg$4.version
26532
26800
  };
26533
26801
  /**
26534
26802
  * Merge BridgeKit's caller into the invocation metadata for retry operations.
@@ -26676,7 +26944,7 @@ async function resolveExecutionQuote(binding, suppliedQuote, refundAddress) {
26676
26944
  this.disableErrorReporting = config.disableErrorReporting === true;
26677
26945
  this.telemetryConfig = {
26678
26946
  sdkName: SDK_NAME,
26679
- sdkVersion: pkg$3.version,
26947
+ sdkVersion: pkg$4.version,
26680
26948
  disabled: this.disableErrorReporting
26681
26949
  };
26682
26950
  for (const provider of this.providers){
@@ -27400,7 +27668,7 @@ async function resolveExecutionQuote(binding, suppliedQuote, refundAddress) {
27400
27668
  }
27401
27669
 
27402
27670
  // Auto-register this kit for user agent tracking
27403
- registerKit(`${pkg$3.name}/${pkg$3.version}`);
27671
+ registerKit(`${pkg$4.name}/${pkg$4.version}`);
27404
27672
 
27405
27673
  /**
27406
27674
  * Create a BridgeKit instance with optional developer fee configuration.
@@ -27471,11 +27739,11 @@ registerKit(`${pkg$3.name}/${pkg$3.version}`);
27471
27739
  return kit;
27472
27740
  };
27473
27741
 
27474
- var name$1 = "@circle-fin/swap-kit";
27475
- var version$1 = "1.7.0";
27476
- var pkg$1 = {
27477
- name: name$1,
27478
- version: version$1};
27742
+ var name$2 = "@circle-fin/swap-kit";
27743
+ var version$2 = "1.8.0";
27744
+ var pkg$2 = {
27745
+ name: name$2,
27746
+ version: version$2};
27479
27747
 
27480
27748
  const chainIdentifierField = z.custom((value)=>chainIdentifierSchema.safeParse(value).success, {
27481
27749
  message: 'chain must be a valid chain identifier'
@@ -27556,7 +27824,7 @@ const optionalChainIdentifierField = chainIdentifierField.optional();
27556
27824
  *
27557
27825
  * @remarks
27558
27826
  * Optionally includes address for developer-controlled adapters.
27559
- */ const adapterContextSchema$2 = z.object({
27827
+ */ const adapterContextSchema$3 = z.object({
27560
27828
  adapter: adapterSchema,
27561
27829
  chain: chainIdentifierField,
27562
27830
  address: z.string().optional()
@@ -27577,7 +27845,7 @@ const optionalChainIdentifierField = chainIdentifierField.optional();
27577
27845
  * }
27578
27846
  * ```
27579
27847
  */ z.object({
27580
- from: adapterContextSchema$2,
27848
+ from: adapterContextSchema$3,
27581
27849
  tokenIn: z.string({
27582
27850
  required_error: 'tokenIn is required',
27583
27851
  invalid_type_error: 'tokenIn must be a string'
@@ -27815,25 +28083,83 @@ const optionalChainIdentifierField = chainIdentifierField.optional();
27815
28083
  message: 'fee amount must be non-negative (>= 0)'
27816
28084
  });
27817
28085
  /**
27818
- * Zod schema for validating API keys.
27819
- *
27820
- * Accepts a Circle platform API key (`<ENV>_API_KEY:<keyId>:<keySecret>`) and
27821
- * the legacy kit key (`KIT_KEY:<keyId>:<keySecret>`).
27822
- *
27823
- * @example
27824
- * ```typescript
27825
- * import { apiKeySchema } from '@core/service-client'
27826
- *
27827
- * const result = apiKeySchema.safeParse('TEST_API_KEY:e84d2546d4e321b2ff427dc988c89503:f84d2548d4e322b2ff427fc989c87503')
27828
- * if (!result.success) {
27829
- * console.error('Invalid API key format:', result.error.issues)
27830
- * }
27831
- * ```
27832
- */ const apiKeySchema = z.string({
27833
- required_error: 'API key is required',
27834
- invalid_type_error: 'Invalid API key format'
27835
- }).regex(API_KEY_PATTERN, 'Invalid API key format');
27836
- /**
28086
+ * Parses a display-only field leniently: a value `schema` rejects is replaced
28087
+ * with `fallback` instead of failing the response that carries it.
28088
+ * Implemented with `preprocess` rather than Zod's `.catch()` because static
28089
+ * analysis misreads `.catch` on the schema chain as an unhandled Promise
28090
+ * (S7785).
28091
+ */ const withFallback = (schema, fallback)=>z.preprocess((value)=>schema.safeParse(value).success ? value : fallback, schema);
28092
+ const swapRouteTokenSchema = z.object({
28093
+ symbol: z.string(),
28094
+ address: z.string(),
28095
+ decimals: z.number()
28096
+ });
28097
+ const swapRouteMetadataSchema = z.record(z.unknown()).optional();
28098
+ const swapRouteStepSchema = z.object({
28099
+ type: z.string(),
28100
+ tool: z.string(),
28101
+ toolName: z.string(),
28102
+ fromToken: swapRouteTokenSchema,
28103
+ toToken: swapRouteTokenSchema,
28104
+ estimate: z.object({
28105
+ fromAmount: z.string(),
28106
+ toAmount: z.string(),
28107
+ gasCostUSD: z.string().optional()
28108
+ }),
28109
+ metadata: swapRouteMetadataSchema
28110
+ });
28111
+ /**
28112
+ * Strict schema for a `SwapRoute`. Module-private: service responses
28113
+ * parse routes leniently through {@link responseSwapRouteSchema}.
28114
+ */ const swapRouteSchema = z.object({
28115
+ provider: z.string().min(1, 'provider must be a non-empty string'),
28116
+ steps: z.array(swapRouteStepSchema),
28117
+ metadata: swapRouteMetadataSchema
28118
+ });
28119
+ /**
28120
+ * Route details are display-only, so service responses parse them leniently:
28121
+ * steps or metadata in a shape the SDK does not know are dropped rather than
28122
+ * failing the quote or swap they describe. Steps are all-or-nothing: if any
28123
+ * step is malformed, `steps` is empty, so a route never silently loses a hop.
28124
+ */ const responseSwapRouteSchema = swapRouteSchema.extend({
28125
+ steps: withFallback(swapRouteSchema.shape.steps, []),
28126
+ metadata: withFallback(swapRouteMetadataSchema, undefined)
28127
+ });
28128
+ const createSwapFeeItemSchema = z.object({
28129
+ token: z.string({
28130
+ required_error: 'fee token is required',
28131
+ invalid_type_error: 'fee token must be a string'
28132
+ }).min(1, 'fee token must be a non-empty string'),
28133
+ amount: feeAmountSchema,
28134
+ decimals: z.number().int('fee token decimals must be an integer').nonnegative('fee token decimals must be a non-negative integer').optional(),
28135
+ symbol: z.string({
28136
+ invalid_type_error: 'fee token symbol must be a string'
28137
+ }).min(1, 'fee token symbol must be a non-empty string').optional(),
28138
+ // Descriptive metadata: a value the SDK cannot use is dropped rather than
28139
+ // failing the response that carries the fee.
28140
+ type: withFallback(z.string().min(1).optional(), undefined),
28141
+ chain: withFallback(z.string().min(1).optional(), undefined)
28142
+ });
28143
+ /**
28144
+ * Zod schema for validating API keys.
28145
+ *
28146
+ * Accepts a Circle platform API key (`<ENV>_API_KEY:<keyId>:<keySecret>`) and
28147
+ * the legacy kit key (`KIT_KEY:<keyId>:<keySecret>`).
28148
+ *
28149
+ * @example
28150
+ * ```typescript
28151
+ * import { apiKeySchema } from '@core/service-client'
28152
+ *
28153
+ * const result = apiKeySchema.safeParse('TEST_API_KEY:e84d2546d4e321b2ff427dc988c89503:f84d2548d4e322b2ff427fc989c87503')
28154
+ * if (!result.success) {
28155
+ * console.error('Invalid API key format:', result.error.issues)
28156
+ * }
28157
+ * ```
28158
+ */ const apiKeySchema = z.string({
28159
+ required_error: 'API key is required',
28160
+ invalid_type_error: 'Invalid API key format'
28161
+ }).regex(API_KEY_PATTERN, 'Invalid API key format');
28162
+ /**
27837
28163
  * Zod schema for platform fees configuration.
27838
28164
  *
27839
28165
  * Validates the fees object for swap quotes.
@@ -27899,10 +28225,7 @@ const optionalChainIdentifierField = chainIdentifierField.optional();
27899
28225
  /** Quote information with estimated amounts and route */ quote: z.object({
27900
28226
  /** 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'),
27901
28227
  /** 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'),
27902
- /** Route information from DEX aggregator */ route: z.object({
27903
- provider: z.string().min(1, 'provider must be a non-empty string'),
27904
- steps: z.array(z.unknown())
27905
- })
28228
+ /** Route information from DEX aggregator */ route: responseSwapRouteSchema
27906
28229
  }),
27907
28230
  /** Fee context indicating where fees should be collected */ feeContext: quoteFeeContextSchema
27908
28231
  });
@@ -28118,19 +28441,6 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
28118
28441
  }).min(1, 'toChain must be a non-empty string if provided').optional(),
28119
28442
  apiKey: apiKeySchema.optional()
28120
28443
  });
28121
- /**
28122
- * Zod schema for validating CreateSwapResponse payloads.
28123
- */ const createSwapFeeItemSchema = z.object({
28124
- token: z.string({
28125
- required_error: 'fee token is required',
28126
- invalid_type_error: 'fee token must be a string'
28127
- }).min(1, 'fee token must be a non-empty string'),
28128
- amount: feeAmountSchema,
28129
- decimals: z.number().int('fee token decimals must be an integer').nonnegative('fee token decimals must be a non-negative integer').optional(),
28130
- symbol: z.string({
28131
- invalid_type_error: 'fee token symbol must be a string'
28132
- }).min(1, 'fee token symbol must be a non-empty string').optional()
28133
- });
28134
28444
  /**
28135
28445
  * Developer fee item schema with basis field.
28136
28446
  */ const createSwapDeveloperFeeItemSchema = createSwapFeeItemSchema.extend({
@@ -28268,6 +28578,8 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
28268
28578
  correlationId: z.preprocess((value)=>z.string().uuid().safeParse(value).success ? value : undefined, z.string().optional()),
28269
28579
  config: createSwapRequestBaseSchema.shape.config.optional(),
28270
28580
  fees: createSwapFeesSchema.optional(),
28581
+ // A route the SDK cannot parse is dropped instead of failing the swap.
28582
+ route: withFallback(responseSwapRouteSchema.optional(), undefined),
28271
28583
  transaction: createSwapTransactionSchema
28272
28584
  });
28273
28585
 
@@ -28305,6 +28617,146 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
28305
28617
  return normalize(source.apiKey) ?? normalize(source.kitKey);
28306
28618
  };
28307
28619
 
28620
+ function isBigIntLike$1(value) {
28621
+ try {
28622
+ BigInt(value);
28623
+ return true;
28624
+ } catch {
28625
+ return false;
28626
+ }
28627
+ }
28628
+ function isNonNegativeBigIntLike$3(value) {
28629
+ try {
28630
+ return BigInt(value) >= 0n;
28631
+ } catch {
28632
+ return false;
28633
+ }
28634
+ }
28635
+ /**
28636
+ * A `0x`-prefixed, even-length hex byte string (`'0x'` for empty calldata).
28637
+ *
28638
+ * @internal
28639
+ */ const hexBytesSchema$3 = z.union([
28640
+ hexStringSchema,
28641
+ z.literal('0x')
28642
+ ]).refine((value)=>value.length % 2 === 0, {
28643
+ message: 'value must be a 0x-prefixed even-length hex string'
28644
+ });
28645
+ /**
28646
+ * A non-negative uint256, accepted as a decimal string or a `bigint`.
28647
+ *
28648
+ * @internal
28649
+ */ const uint256LikeSchema$3 = z.union([
28650
+ z.string(),
28651
+ z.bigint()
28652
+ ]).refine(isNonNegativeBigIntLike$3, {
28653
+ message: 'value must be a non-negative bigint-compatible string or bigint'
28654
+ });
28655
+ /**
28656
+ * A typed base-unit amount object as emitted by the API.
28657
+ *
28658
+ * @internal
28659
+ */ const amountJsonSchema$2 = z.object({
28660
+ raw: z.string().refine(isBigIntLike$1, {
28661
+ message: 'raw must be a bigint-compatible string'
28662
+ }),
28663
+ decimals: z.number().int().nonnegative()
28664
+ });
28665
+ /**
28666
+ * One instruction in a signed claim-rewards execution payload.
28667
+ *
28668
+ * `.passthrough()` so a backend that adds a field does not fail validation.
28669
+ *
28670
+ * @internal
28671
+ */ const claimRewardsInstructionSchema = z.object({
28672
+ target: evmAddressSchema$1,
28673
+ data: hexBytesSchema$3,
28674
+ value: uint256LikeSchema$3,
28675
+ tokenIn: evmAddressSchema$1,
28676
+ amountToApprove: uint256LikeSchema$3,
28677
+ tokenOut: evmAddressSchema$1,
28678
+ minTokenOut: uint256LikeSchema$3
28679
+ }).passthrough();
28680
+ const claimRewardsTokenSchema = z.object({
28681
+ token: evmAddressSchema$1,
28682
+ beneficiary: evmAddressSchema$1
28683
+ }).passthrough();
28684
+ /**
28685
+ * The signed execution payload the `earn.claimRewards` Adapter action takes.
28686
+ *
28687
+ * @internal
28688
+ */ const claimRewardsExecutionParamsSchema = z.object({
28689
+ instructions: z.tuple([
28690
+ claimRewardsInstructionSchema
28691
+ ]).rest(claimRewardsInstructionSchema),
28692
+ tokens: z.tuple([
28693
+ claimRewardsTokenSchema
28694
+ ]).rest(claimRewardsTokenSchema),
28695
+ execId: uint256LikeSchema$3,
28696
+ deadline: uint256LikeSchema$3,
28697
+ metadata: hexBytesSchema$3
28698
+ }).passthrough();
28699
+ /**
28700
+ * A single claimable reward entry in a claim-rewards response.
28701
+ *
28702
+ * @internal
28703
+ */ const claimedAmountSchema = z.object({
28704
+ token: evmAddressSchema$1,
28705
+ symbol: z.string(),
28706
+ amount: amountJsonSchema$2
28707
+ });
28708
+ /**
28709
+ * The `data` envelope of a claim-rewards response.
28710
+ *
28711
+ * `executionParams` and `signature` are present exactly when rewards are
28712
+ * claimable — never one without the other, and never either when `rewards`
28713
+ * is empty.
28714
+ *
28715
+ * @internal
28716
+ */ const claimRewardsPayloadSchema = z.object({
28717
+ rewards: z.array(claimedAmountSchema),
28718
+ executionParams: claimRewardsExecutionParamsSchema.optional(),
28719
+ signature: evmSignatureSchema.optional()
28720
+ }).refine((data)=>{
28721
+ const signedFieldCount = (data.executionParams === undefined ? 0 : 1) + (data.signature === undefined ? 0 : 1);
28722
+ if (data.rewards.length > 0) {
28723
+ return signedFieldCount === 2;
28724
+ }
28725
+ return signedFieldCount === 0;
28726
+ }, {
28727
+ message: 'claim rewards response must include executionParams and signature only when rewards are claimable'
28728
+ });
28729
+ /**
28730
+ * Zod schema for a claim-rewards API response.
28731
+ *
28732
+ * @internal
28733
+ */ z.object({
28734
+ data: claimRewardsPayloadSchema
28735
+ });
28736
+ /**
28737
+ * A single claimable reward entry in a claim-rewards quote response.
28738
+ *
28739
+ * @internal
28740
+ */ const claimRewardsQuoteRewardSchema = z.object({
28741
+ token: z.string(),
28742
+ symbol: z.string(),
28743
+ amount: amountJsonSchema$2
28744
+ });
28745
+ /**
28746
+ * The `data` envelope of a claim-rewards quote response.
28747
+ *
28748
+ * @internal
28749
+ */ const claimRewardsQuotePayloadSchema = z.object({
28750
+ rewards: z.array(claimRewardsQuoteRewardSchema)
28751
+ });
28752
+ /**
28753
+ * Zod schema for a claim-rewards quote API response.
28754
+ *
28755
+ * @internal
28756
+ */ z.object({
28757
+ data: claimRewardsQuotePayloadSchema
28758
+ });
28759
+
28308
28760
  /**
28309
28761
  * Zod schema for validating EVM adapter capabilities.
28310
28762
  *
@@ -28732,7 +29184,7 @@ const abiParameterSchema = z.object({
28732
29184
  * const result = evmPreparedChainRequestParamsSchema.safeParse(params)
28733
29185
  * ```
28734
29186
  */ z.object({
28735
- type: z.literal('evm'),
29187
+ type: z.literal('evm').optional(),
28736
29188
  abi: abiSchema,
28737
29189
  address: evmAddressSchema$1,
28738
29190
  functionName: z.string({
@@ -28849,7 +29301,7 @@ const abiParameterSchema = z.object({
28849
29301
  message: 'Private key must be a valid 64-character hex string. Please provide a valid private key with or without "0x" prefix.'
28850
29302
  }));
28851
29303
 
28852
- const hexBytesSchema$1 = z.string().regex(/^0x([0-9a-fA-F]{2})*$/, {
29304
+ const hexBytesSchema$2 = z.string().regex(/^0x([0-9a-fA-F]{2})*$/, {
28853
29305
  message: 'value must be a 0x-prefixed even-length hex string'
28854
29306
  }).transform((value)=>value);
28855
29307
  z.object({
@@ -28861,6 +29313,53 @@ z.object({
28861
29313
  amount: z.bigint().refine((value)=>value >= 0n, {
28862
29314
  message: 'amount must be a non-negative bigint'
28863
29315
  }),
29316
+ permitCalldata: hexBytesSchema$2
29317
+ }))
29318
+ }).passthrough();
29319
+
29320
+ const hexBytesSchema$1 = z.string().regex(/^0x([0-9a-fA-F]{2})*$/, {
29321
+ message: 'value must be a 0x-prefixed even-length hex string'
29322
+ }).transform((value)=>value);
29323
+ function isNonNegativeBigIntLike$2(value) {
29324
+ try {
29325
+ return BigInt(value) >= 0n;
29326
+ } catch {
29327
+ return false;
29328
+ }
29329
+ }
29330
+ const uint256LikeSchema$2 = z.union([
29331
+ z.string(),
29332
+ z.bigint()
29333
+ ]).refine(isNonNegativeBigIntLike$2, {
29334
+ message: 'value must be a non-negative bigint-compatible string or bigint'
29335
+ });
29336
+ const borrowExecutionParamsSchema$1 = z.object({
29337
+ instructions: z.array(z.object({
29338
+ target: evmAddressSchema$1,
29339
+ data: hexBytesSchema$1,
29340
+ value: uint256LikeSchema$2,
29341
+ tokenIn: evmAddressSchema$1,
29342
+ amountToApprove: uint256LikeSchema$2,
29343
+ tokenOut: evmAddressSchema$1,
29344
+ minTokenOut: uint256LikeSchema$2
29345
+ }).passthrough()).min(1),
29346
+ tokens: z.array(z.object({
29347
+ token: evmAddressSchema$1,
29348
+ beneficiary: evmAddressSchema$1
29349
+ }).passthrough()),
29350
+ execId: uint256LikeSchema$2,
29351
+ deadline: uint256LikeSchema$2,
29352
+ metadata: hexBytesSchema$1
29353
+ }).passthrough();
29354
+ z.object({
29355
+ executeParams: borrowExecutionParamsSchema$1,
29356
+ signature: evmSignatureSchema,
29357
+ tokenInputs: z.array(z.object({
29358
+ permitType: z.nativeEnum(BorrowPermitType),
29359
+ token: evmAddressSchema$1,
29360
+ amount: z.bigint().refine((value)=>value >= 0n, {
29361
+ message: 'amount must be a non-negative bigint'
29362
+ }),
28864
29363
  permitCalldata: hexBytesSchema$1
28865
29364
  }))
28866
29365
  }).passthrough();
@@ -29208,6 +29707,16 @@ createTokenRegistry();
29208
29707
  name: 'USDC',
29209
29708
  version: '2'
29210
29709
  },
29710
+ Cronos: {
29711
+ chainId: Cronos.chainId,
29712
+ name: 'USDC',
29713
+ version: '2'
29714
+ },
29715
+ Morph: {
29716
+ chainId: Morph.chainId,
29717
+ name: 'USDC',
29718
+ version: '2'
29719
+ },
29211
29720
  Arc_Testnet: {
29212
29721
  chainId: ArcTestnet.chainId,
29213
29722
  name: 'USDC',
@@ -29253,7 +29762,7 @@ const optionalSwapChainIdentifierField = swapChainIdentifierField.optional();
29253
29762
  * Schema for validating AdapterContext.
29254
29763
  * Must always contain both adapter and chain explicitly.
29255
29764
  * Optionally includes address for developer-controlled adapters.
29256
- */ const adapterContextSchema$1 = z.object({
29765
+ */ const adapterContextSchema$2 = z.object({
29257
29766
  adapter: adapterSchema,
29258
29767
  chain: swapChainIdentifierField,
29259
29768
  address: z.string().optional()
@@ -29296,6 +29805,7 @@ const optionalSwapChainIdentifierField = swapChainIdentifierField.optional();
29296
29805
  * - customFee: Optional fee configuration
29297
29806
  * - apiKey: Optional credential string
29298
29807
  * - kitKey: Optional credential string (deprecated alias for apiKey)
29808
+ * - batchTransactions: Optional boolean to opt out of atomic batching
29299
29809
  */ const swapConfigSchema = z.object({
29300
29810
  allowanceStrategy: allowanceStrategySchema.optional(),
29301
29811
  slippageBps: z.number().int().min(0).optional(),
@@ -29306,7 +29816,10 @@ const optionalSwapChainIdentifierField = swapChainIdentifierField.optional();
29306
29816
  })(z.string())).optional(),
29307
29817
  customFee: swapCustomFeeSchema.optional(),
29308
29818
  apiKey: z.string().optional(),
29309
- kitKey: z.string().optional()
29819
+ kitKey: z.string().optional(),
29820
+ batchTransactions: z.boolean({
29821
+ invalid_type_error: 'batchTransactions must be a boolean'
29822
+ }).optional()
29310
29823
  });
29311
29824
  const swapDestinationSchema = z.object({
29312
29825
  chain: optionalSwapChainIdentifierField,
@@ -29314,7 +29827,7 @@ const swapDestinationSchema = z.object({
29314
29827
  message: 'recipientAddress must be a valid blockchain address: EVM (0x + 40 hex chars) or Solana (base58, 32-44 chars)'
29315
29828
  }).optional()
29316
29829
  }).strict();
29317
- const EVM_ADDRESS_REGEX = /^0x[a-fA-F0-9]{40}$/;
29830
+ const EVM_ADDRESS_REGEX$1 = /^0x[a-fA-F0-9]{40}$/;
29318
29831
  const SOLANA_ADDRESS_REGEX = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
29319
29832
  const BASE58_CHARS_REGEX = /^[1-9A-HJ-NP-Za-km-z]+$/;
29320
29833
  /**
@@ -29357,7 +29870,7 @@ const MAX_DISPLAY_LENGTH = 30;
29357
29870
  */ let _symbolResult;
29358
29871
  const swapTokenSchema = z.string().superRefine((value, ctx)=>{
29359
29872
  if (value.startsWith('0x')) {
29360
- if (!EVM_ADDRESS_REGEX.test(value)) {
29873
+ if (!EVM_ADDRESS_REGEX$1.test(value)) {
29361
29874
  ctx.addIssue({
29362
29875
  code: z.ZodIssueCode.custom,
29363
29876
  message: `Invalid EVM token address format. Expected '0x' followed by 40 hexadecimal characters, but received: '${truncate(value)}'`
@@ -29452,7 +29965,7 @@ const amountInSchema = z.string().min(1, 'Required').pipe(createDecimalStringVal
29452
29965
  * }
29453
29966
  * ```
29454
29967
  */ z.object({
29455
- from: adapterContextSchema$1,
29968
+ from: adapterContextSchema$2,
29456
29969
  tokenIn: swapTokenSchema,
29457
29970
  tokenOut: swapTokenSchema,
29458
29971
  amountIn: amountInSchema,
@@ -29493,16 +30006,16 @@ new Set(Object.values(Blockchain));
29493
30006
 
29494
30007
  new Set(Object.values(Blockchain));
29495
30008
 
29496
- /** SDK name used in telemetry payloads. */ resolveKitSdkName(pkg$1.name);
30009
+ /** SDK name used in telemetry payloads. */ resolveKitSdkName(pkg$2.name);
29497
30010
 
29498
30011
  // Auto-register this kit for user agent tracking
29499
- registerKit(`${pkg$1.name}/${pkg$1.version}`);
30012
+ registerKit(`${pkg$2.name}/${pkg$2.version}`);
29500
30013
 
29501
- var name = "@circle-fin/earn-kit";
29502
- var version = "1.8.0";
29503
- var pkg = {
29504
- name: name,
29505
- version: version};
30014
+ var name$1 = "@circle-fin/earn-kit";
30015
+ var version$1 = "1.9.0";
30016
+ var pkg$1 = {
30017
+ name: name$1,
30018
+ version: version$1};
29506
30019
 
29507
30020
  /**
29508
30021
  * Map SDK chain identifiers to bridge API chain strings.
@@ -29574,7 +30087,7 @@ function isBigIntLike(value) {
29574
30087
  return false;
29575
30088
  }
29576
30089
  }
29577
- function isNonNegativeBigIntLike(value) {
30090
+ function isNonNegativeBigIntLike$1(value) {
29578
30091
  try {
29579
30092
  return BigInt(value) >= 0n;
29580
30093
  } catch {
@@ -29594,28 +30107,22 @@ const bridgeFeeTokenSchema = hexAddressSchema;
29594
30107
  * unchanged so signed execution params can be forwarded verbatim.
29595
30108
  *
29596
30109
  * @internal
29597
- */ const uint256LikeSchema = z.union([
30110
+ */ const uint256LikeSchema$1 = z.union([
29598
30111
  z.string(),
29599
30112
  z.bigint()
29600
- ]).refine(isNonNegativeBigIntLike, {
30113
+ ]).refine(isNonNegativeBigIntLike$1, {
29601
30114
  message: 'value must be a non-negative bigint-compatible string or bigint'
29602
30115
  });
29603
30116
  /**
29604
30117
  * Zod schema for typed amount objects emitted by the API.
29605
30118
  *
29606
30119
  * @internal
29607
- */ const amountJsonSchema = z.object({
30120
+ */ const amountJsonSchema$1 = z.object({
29608
30121
  raw: z.string().refine(isBigIntLike, {
29609
30122
  message: 'raw must be a bigint-compatible string'
29610
30123
  }),
29611
30124
  decimals: z.number().int().nonnegative()
29612
30125
  });
29613
- /** @internal */ const hexBytesSchema = z.union([
29614
- hexStringSchema,
29615
- z.literal('0x')
29616
- ]).refine((value)=>value.length % 2 === 0, {
29617
- message: 'value must be a 0x-prefixed even-length hex string'
29618
- });
29619
30126
  /** @internal */ const bytes32HexSchema = hexStringSchema.refine((value)=>value.length === BYTES32_HEX_LENGTH, {
29620
30127
  message: 'value must be a 0x-prefixed 32-byte hex string'
29621
30128
  });
@@ -29699,9 +30206,9 @@ const bridgeFeeTokenSchema = hexAddressSchema;
29699
30206
  *
29700
30207
  * @internal
29701
30208
  */ const liquidityProfileSchema = z.object({
29702
- totalDeposits: amountJsonSchema,
29703
- available: amountJsonSchema,
29704
- totalSupply: amountJsonSchema,
30209
+ totalDeposits: amountJsonSchema$1,
30210
+ available: amountJsonSchema$1,
30211
+ totalSupply: amountJsonSchema$1,
29705
30212
  status: z.enum([
29706
30213
  'active',
29707
30214
  'low_liquidity'
@@ -29738,8 +30245,8 @@ const bridgeFeeTokenSchema = hexAddressSchema;
29738
30245
  vaultFee: z.number(),
29739
30246
  rewards: z.array(vaultRewardSchema),
29740
30247
  collateral: z.array(collateralSchema),
29741
- totalDeposits: amountJsonSchema,
29742
- liquidity: amountJsonSchema,
30248
+ totalDeposits: amountJsonSchema$1,
30249
+ liquidity: amountJsonSchema$1,
29743
30250
  status: z.enum([
29744
30251
  'active',
29745
30252
  'low_liquidity'
@@ -29849,13 +30356,13 @@ const bridgeFeeTokenSchema = hexAddressSchema;
29849
30356
  */ const accruedRewardSchema = z.object({
29850
30357
  token: z.string(),
29851
30358
  symbol: z.string(),
29852
- amount: amountJsonSchema
30359
+ amount: amountJsonSchema$1
29853
30360
  });
29854
30361
  const positionPnlSchema = z.discriminatedUnion('status', [
29855
30362
  z.object({
29856
30363
  status: z.literal('available'),
29857
- principalDeposited: amountJsonSchema,
29858
- totalYieldEarned: amountJsonSchema
30364
+ principalDeposited: amountJsonSchema$1,
30365
+ totalYieldEarned: amountJsonSchema$1
29859
30366
  }),
29860
30367
  z.object({
29861
30368
  status: z.literal('pending')
@@ -29875,9 +30382,9 @@ const positionPnlSchema = z.discriminatedUnion('status', [
29875
30382
  vaultAddress: z.string(),
29876
30383
  vaultName: z.string(),
29877
30384
  asset: z.string(),
29878
- currentBalance: amountJsonSchema,
30385
+ currentBalance: amountJsonSchema$1,
29879
30386
  currentApy: z.number(),
29880
- shares: amountJsonSchema,
30387
+ shares: amountJsonSchema$1,
29881
30388
  pnl: positionPnlSchema,
29882
30389
  accruedRewards: z.array(accruedRewardSchema).optional(),
29883
30390
  rewardsUnavailableReason: z.string().optional()
@@ -29905,7 +30412,7 @@ const positionPnlSchema = z.discriminatedUnion('status', [
29905
30412
  * @internal
29906
30413
  */ const earnInstructionSchema = z.object({
29907
30414
  tokenIn: hexAddressSchema,
29908
- amountToApprove: uint256LikeSchema
30415
+ amountToApprove: uint256LikeSchema$1
29909
30416
  }).passthrough();
29910
30417
  /**
29911
30418
  * Zod schema for earn execution params returned by the earn service.
@@ -29919,50 +30426,10 @@ const positionPnlSchema = z.discriminatedUnion('status', [
29919
30426
  instructions: z.tuple([
29920
30427
  earnInstructionSchema
29921
30428
  ]).rest(earnInstructionSchema),
29922
- deadline: uint256LikeSchema
30429
+ deadline: uint256LikeSchema$1
29923
30430
  }).passthrough();
29924
30431
  /** @internal */ const depositExecutionParamsSchema = earnExecutionParamsSchema;
29925
30432
  /** @internal */ const withdrawExecutionParamsSchema = earnExecutionParamsSchema;
29926
- /**
29927
- * Zod schema for the claim rewards instruction fields that must be safe to
29928
- * ABI-encode.
29929
- *
29930
- * Claim rewards has no approval preflight, so this validates the forwarded
29931
- * signed payload at the provider boundary. Additional instruction fields are
29932
- * accepted and preserved.
29933
- *
29934
- * @internal
29935
- */ const claimRewardsInstructionSchema = z.object({
29936
- target: hexAddressSchema,
29937
- data: hexBytesSchema,
29938
- value: uint256LikeSchema,
29939
- tokenIn: hexAddressSchema,
29940
- amountToApprove: uint256LikeSchema,
29941
- tokenOut: hexAddressSchema,
29942
- minTokenOut: uint256LikeSchema
29943
- }).passthrough();
29944
- /** @internal */ const claimRewardsTokenSchema = z.object({
29945
- token: hexAddressSchema,
29946
- beneficiary: hexAddressSchema
29947
- }).passthrough();
29948
- /**
29949
- * Zod schema for signed claim rewards execution params.
29950
- *
29951
- * This enforces the required envelope and preserves additive fields so the
29952
- * adapter receives the signed params verbatim.
29953
- *
29954
- * @internal
29955
- */ const claimRewardsExecutionParamsSchema = z.object({
29956
- instructions: z.tuple([
29957
- claimRewardsInstructionSchema
29958
- ]).rest(claimRewardsInstructionSchema),
29959
- tokens: z.tuple([
29960
- claimRewardsTokenSchema
29961
- ]).rest(claimRewardsTokenSchema),
29962
- execId: uint256LikeSchema,
29963
- deadline: uint256LikeSchema,
29964
- metadata: hexBytesSchema
29965
- }).passthrough();
29966
30433
  /**
29967
30434
  * Zod schema for the deposit payload inside the API `data` envelope.
29968
30435
  *
@@ -30021,9 +30488,9 @@ const eip712DomainSchema = eip712DomainSchema$1.extend({
30021
30488
  message: z.object({
30022
30489
  from: hexAddressSchema,
30023
30490
  to: hexAddressSchema,
30024
- value: uint256LikeSchema,
30025
- validAfter: uint256LikeSchema,
30026
- validBefore: uint256LikeSchema,
30491
+ value: uint256LikeSchema$1,
30492
+ validAfter: uint256LikeSchema$1,
30493
+ validBefore: uint256LikeSchema$1,
30027
30494
  nonce: bytes32HexSchema
30028
30495
  }).passthrough()
30029
30496
  }).passthrough();
@@ -30041,7 +30508,7 @@ const eip712DomainSchema = eip712DomainSchema$1.extend({
30041
30508
  feeToken: bridgeFeeTokenSchema,
30042
30509
  items: z.array(z.object({
30043
30510
  type: z.string(),
30044
- amount: uint256LikeSchema
30511
+ amount: uint256LikeSchema$1
30045
30512
  }).passthrough())
30046
30513
  }).passthrough();
30047
30514
  /**
@@ -30057,7 +30524,7 @@ const eip712DomainSchema = eip712DomainSchema$1.extend({
30057
30524
  const bridgeDepositPrepareReviewSchema = z.object({
30058
30525
  sourceChain: z.string(),
30059
30526
  destinationChain: z.string(),
30060
- amount: amountJsonSchema,
30527
+ amount: amountJsonSchema$1,
30061
30528
  vaultAddress: hexAddressSchema
30062
30529
  }).passthrough();
30063
30530
  /** @internal */ const bridgeQuoteExpirySchema = z.discriminatedUnion('mode', [
@@ -30192,72 +30659,34 @@ const bridgeDepositPrepareReviewSchema = z.object({
30192
30659
  // ---------------------------------------------------------------------------
30193
30660
  // Claim rewards response schema
30194
30661
  // ---------------------------------------------------------------------------
30662
+ // ---------------------------------------------------------------------------
30663
+ // Deposit quote response schema
30664
+ // ---------------------------------------------------------------------------
30195
30665
  /**
30196
- * Zod schema for a claimed reward amount in the API response.
30666
+ * Zod schema for a fee entry in an EarnKit API response.
30667
+ *
30668
+ * Shared across deposit and withdrawal responses (and reusable for real
30669
+ * charged fees, not just quote estimates). `type` identifies the fee category.
30670
+ * For cross-chain deposit quotes this is the kits-proxy fee-quote item type
30671
+ * (e.g. `'FORWARD'`, `'PRE_FINALITY'`). For withdrawal quotes, Circle fees use
30672
+ * `type: 'circle'`. `status` qualifies the fee (e.g. `'estimated'` for a
30673
+ * pre-sign cross-chain fee). Both are omitted on plain fees.
30197
30674
  *
30198
30675
  * @internal
30199
- */ const claimedAmountSchema = z.object({
30200
- token: hexAddressSchema,
30201
- symbol: z.string(),
30202
- amount: amountJsonSchema
30676
+ */ const feeSchema = z.object({
30677
+ type: z.string().optional(),
30678
+ status: z.string().optional(),
30679
+ token: z.string(),
30680
+ amount: amountJsonSchema$1
30203
30681
  });
30204
30682
  /**
30205
- * Zod schema for the claim rewards payload inside the API `data` envelope.
30683
+ * Zod schema for a native gas-fee entry in an EarnKit quote response.
30206
30684
  *
30207
- * Enforce that `executionParams` and `signature` are either both
30208
- * present (claimable rewards) or both absent (nothing to claim).
30209
- *
30210
- * @internal
30211
- */ const claimRewardsPayloadSchema = z.object({
30212
- rewards: z.array(claimedAmountSchema),
30213
- executionParams: claimRewardsExecutionParamsSchema.optional(),
30214
- signature: hexSignatureSchema.optional()
30215
- }).refine((data)=>{
30216
- const signedFieldCount = (data.executionParams === undefined ? 0 : 1) + (data.signature === undefined ? 0 : 1);
30217
- if (data.rewards.length > 0) {
30218
- return signedFieldCount === 2;
30219
- }
30220
- return signedFieldCount === 0;
30221
- }, {
30222
- message: 'claim rewards response must include executionParams and signature only when rewards are claimable'
30223
- });
30224
- /**
30225
- * Zod schema for the `POST /v1/earnKit/claimRewards` API response.
30226
- *
30227
- * The Earn Service API wraps the claim rewards payload in a `data` envelope.
30228
- *
30229
- * @internal
30230
- */ z.object({
30231
- data: claimRewardsPayloadSchema
30232
- });
30233
- // ---------------------------------------------------------------------------
30234
- // Deposit quote response schema
30235
- // ---------------------------------------------------------------------------
30236
- /**
30237
- * Zod schema for a fee entry in an EarnKit API response.
30238
- *
30239
- * Shared across deposit and withdrawal responses (and reusable for real
30240
- * charged fees, not just quote estimates). `type` identifies the fee category.
30241
- * For cross-chain deposit quotes this is the kits-proxy fee-quote item type
30242
- * (e.g. `'FORWARD'`, `'PRE_FINALITY'`). For withdrawal quotes, Circle fees use
30243
- * `type: 'circle'`. `status` qualifies the fee (e.g. `'estimated'` for a
30244
- * pre-sign cross-chain fee). Both are omitted on plain fees.
30245
- *
30246
- * @internal
30247
- */ const feeSchema = z.object({
30248
- type: z.string().optional(),
30249
- status: z.string().optional(),
30250
- token: z.string(),
30251
- amount: amountJsonSchema
30252
- });
30253
- /**
30254
- * Zod schema for a native gas-fee entry in an EarnKit quote response.
30255
- *
30256
- * The Earn Service backend estimates gas server-side and returns one entry per
30257
- * action (`Approve`, `Deposit`, `Withdraw`). A successful estimate carries
30258
- * `fees` in the SDK `EstimatedGas` shape (`{ gas, gasPrice, fee }`), each a raw
30259
- * integer string in the chain's native base units. When the backend cannot
30260
- * estimate an action it returns `fees: null` with an `error` message instead.
30685
+ * The Earn Service backend estimates gas server-side and returns one entry per
30686
+ * action (`Approve`, `Deposit`, `Withdraw`). A successful estimate carries
30687
+ * `fees` in the SDK `EstimatedGas` shape (`{ gas, gasPrice, fee }`), each a raw
30688
+ * integer string in the chain's native base units. When the backend cannot
30689
+ * estimate an action it returns `fees: null` with an `error` message instead.
30261
30690
  *
30262
30691
  * The schema deliberately validates almost nothing beyond the envelope: `name`
30263
30692
  * is optional and `fees` is entirely unvalidated (`unknown`). ALL validation
@@ -30285,8 +30714,8 @@ const bridgeDepositPrepareReviewSchema = z.object({
30285
30714
  vaultAddress: z.string(),
30286
30715
  vaultName: z.string(),
30287
30716
  asset: z.string(),
30288
- depositAmount: amountJsonSchema,
30289
- expectedShares: amountJsonSchema,
30717
+ depositAmount: amountJsonSchema$1,
30718
+ expectedShares: amountJsonSchema$1,
30290
30719
  sharePrice: z.string(),
30291
30720
  currentApy: z.number(),
30292
30721
  fees: z.array(feeSchema).optional(),
@@ -30312,10 +30741,10 @@ const bridgeDepositPrepareReviewSchema = z.object({
30312
30741
  vaultAddress: z.string(),
30313
30742
  vaultName: z.string(),
30314
30743
  asset: z.string(),
30315
- withdrawAmount: amountJsonSchema,
30316
- sharesToRedeem: amountJsonSchema,
30744
+ withdrawAmount: amountJsonSchema$1,
30745
+ sharesToRedeem: amountJsonSchema$1,
30317
30746
  sharePrice: z.string(),
30318
- maxWithdrawable: amountJsonSchema,
30747
+ maxWithdrawable: amountJsonSchema$1,
30319
30748
  fees: z.array(feeSchema),
30320
30749
  gasFees: z.array(quoteGasFeeSchema).optional(),
30321
30750
  warnings: z.array(z.string()).optional()
@@ -30332,31 +30761,6 @@ const bridgeDepositPrepareReviewSchema = z.object({
30332
30761
  // ---------------------------------------------------------------------------
30333
30762
  // Claim rewards quote response schema
30334
30763
  // ---------------------------------------------------------------------------
30335
- /**
30336
- * Zod schema for a reward in the claim rewards quote response.
30337
- *
30338
- * @internal
30339
- */ const claimRewardsQuoteRewardSchema = z.object({
30340
- token: z.string(),
30341
- symbol: z.string(),
30342
- amount: amountJsonSchema
30343
- });
30344
- /**
30345
- * Zod schema for the inner claim rewards quote payload.
30346
- *
30347
- * @internal
30348
- */ const claimRewardsQuotePayloadSchema = z.object({
30349
- rewards: z.array(claimRewardsQuoteRewardSchema)
30350
- });
30351
- /**
30352
- * Zod schema for the `POST /v1/earnKit/claimRewards/quote` API response.
30353
- *
30354
- * The API wraps the payload in a `data` envelope.
30355
- *
30356
- * @internal
30357
- */ z.object({
30358
- data: claimRewardsQuotePayloadSchema
30359
- });
30360
30764
  // ---------------------------------------------------------------------------
30361
30765
  // Batch vault response schema
30362
30766
  // ---------------------------------------------------------------------------
@@ -30496,7 +30900,7 @@ createTokenRegistry();
30496
30900
  * fast instead of being silently stripped.
30497
30901
  *
30498
30902
  * @internal
30499
- */ const adapterContextSchema = z.object({
30903
+ */ const adapterContextSchema$1 = z.object({
30500
30904
  adapter: adapterSchema,
30501
30905
  // AdapterContext accepts a wider chain field, but runtime validation must
30502
30906
  // stay constrained to EarnChain identifiers.
@@ -30728,7 +31132,7 @@ const sourceAdapterContextSchema = z.object({
30728
31132
  * })
30729
31133
  * ```
30730
31134
  */ z.object({
30731
- from: adapterContextSchema,
31135
+ from: adapterContextSchema$1,
30732
31136
  vaultAddress: vaultAddressSchema,
30733
31137
  config: earnConfigSchema.optional()
30734
31138
  });
@@ -30783,7 +31187,7 @@ const sourceAdapterContextSchema = z.object({
30783
31187
  *
30784
31188
  * @internal Exported for branch-targeted validation in the deposit operation.
30785
31189
  */ const sameChainDepositParamsSchema = z.object({
30786
- from: adapterContextSchema,
31190
+ from: adapterContextSchema$1,
30787
31191
  to: z.undefined().optional(),
30788
31192
  vaultAddress: vaultAddressSchema,
30789
31193
  amount: amountSchema,
@@ -30854,7 +31258,7 @@ const crossChainDepositDestinationSchema = z.object({
30854
31258
  * })
30855
31259
  * ```
30856
31260
  */ z.object({
30857
- from: adapterContextSchema,
31261
+ from: adapterContextSchema$1,
30858
31262
  vaultAddress: vaultAddressSchema,
30859
31263
  amount: amountSchema,
30860
31264
  config: earnConfigSchema.optional()
@@ -30908,7 +31312,7 @@ const crossChainDepositDestinationSchema = z.object({
30908
31312
  * })
30909
31313
  * ```
30910
31314
  */ z.object({
30911
- from: adapterContextSchema,
31315
+ from: adapterContextSchema$1,
30912
31316
  vaultAddress: vaultAddressSchema,
30913
31317
  amount: amountSchema,
30914
31318
  config: earnConfigSchema.optional()
@@ -30928,12 +31332,12 @@ const crossChainDepositDestinationSchema = z.object({
30928
31332
  *
30929
31333
  * @internal
30930
31334
  */ z.object({
30931
- from: adapterContextSchema,
31335
+ from: adapterContextSchema$1,
30932
31336
  vaultAddress: vaultAddressSchema,
30933
31337
  config: earnConfigSchema.optional()
30934
31338
  });
30935
31339
  const sameChainGetDepositQuoteParamsSchema = z.object({
30936
- from: adapterContextSchema,
31340
+ from: adapterContextSchema$1,
30937
31341
  vaultAddress: vaultAddressSchema,
30938
31342
  amount: amountSchema,
30939
31343
  config: earnConfigSchema.optional()
@@ -31017,7 +31421,7 @@ function hasCrossChainDepositQuoteShape(params) {
31017
31421
  * })
31018
31422
  * ```
31019
31423
  */ z.object({
31020
- from: adapterContextSchema,
31424
+ from: adapterContextSchema$1,
31021
31425
  vaultAddress: vaultAddressSchema,
31022
31426
  amount: amountSchema,
31023
31427
  config: earnConfigSchema.optional()
@@ -31037,15 +31441,15 @@ function hasCrossChainDepositQuoteShape(params) {
31037
31441
  *
31038
31442
  * @internal
31039
31443
  */ z.object({
31040
- from: adapterContextSchema,
31444
+ from: adapterContextSchema$1,
31041
31445
  vaultAddress: vaultAddressSchema,
31042
31446
  config: earnConfigSchema.optional()
31043
31447
  });
31044
31448
 
31045
- /** SDK name used in telemetry payloads. */ resolveKitSdkName(pkg.name);
31449
+ /** SDK name used in telemetry payloads. */ resolveKitSdkName(pkg$1.name);
31046
31450
 
31047
31451
  // Auto-register this kit for user agent tracking
31048
- registerKit(`${pkg.name}/${pkg.version}`);
31452
+ registerKit(`${pkg$1.name}/${pkg$1.version}`);
31049
31453
 
31050
31454
  /**
31051
31455
  * Register event handlers from a context actions map to a kit instance.
@@ -31111,6 +31515,1358 @@ registerKit(`${pkg.name}/${pkg.version}`);
31111
31515
  }
31112
31516
  };
31113
31517
 
31518
+ var name = "@circle-fin/borrow-kit";
31519
+ var version = "1.1.0";
31520
+ var pkg = {
31521
+ name: name,
31522
+ version: version};
31523
+
31524
+ const HUMAN_READABLE_AMOUNT_REGEX$1 = /^(?:0|[1-9]\d*)(?:\.\d+)?$/;
31525
+ const EVM_ADDRESS_REGEX = /^0x[0-9a-fA-F]{40}$/;
31526
+ const POSITIVE_USDC_AMOUNT_REGEX = /^(?:0|[1-9]\d*)(?:\.\d{1,6})?$/;
31527
+ /**
31528
+ * True when `value` parses as a URL whose scheme is exactly `https:`.
31529
+ *
31530
+ * Uses the WHATWG `URL` parser rather than `startsWith('https://')` so the
31531
+ * check is scheme-normalized (case-insensitive, e.g. `HTTPS://...` still
31532
+ * passes) instead of a literal prefix match. `value` has already passed
31533
+ * `z.string().url()` by the time this runs, but a caller could still supply
31534
+ * a string that `URL` itself rejects, so the parse is guarded.
31535
+ */ function isHttpsUrl$1(value) {
31536
+ try {
31537
+ return new URL(value).protocol === 'https:';
31538
+ } catch {
31539
+ return false;
31540
+ }
31541
+ }
31542
+ const borrowServiceConfigSchema$1 = z.object({
31543
+ apiKey: z.string().optional(),
31544
+ baseUrl: z.string().url().optional()
31545
+ }).strict();
31546
+ /** Loan asset amounts are USDC, so they carry at most 6 decimal places. */ function positiveLoanAssetAmountSchema(field) {
31547
+ return z.string().regex(POSITIVE_USDC_AMOUNT_REGEX, `${field} must be a canonical decimal with at most 6 decimal places`).refine((value)=>/[1-9]/.test(value), {
31548
+ message: `${field} must be greater than zero`
31549
+ });
31550
+ }
31551
+ /**
31552
+ * Collateral amounts denominate an arbitrary token, so the decimal count is the
31553
+ * token's rather than USDC's — a satoshi-precision collateral is valid.
31554
+ */ function positiveCollateralAmountSchema(field) {
31555
+ return z.string().regex(HUMAN_READABLE_AMOUNT_REGEX$1, `${field} must be a canonical decimal`).refine((value)=>/[1-9]/.test(value), {
31556
+ message: `${field} must be greater than zero`
31557
+ });
31558
+ }
31559
+ /** @internal */ z.object({
31560
+ chain: chainIdentifierSchema,
31561
+ marketId: z.string().min(1),
31562
+ config: borrowServiceConfigSchema$1.optional()
31563
+ }).strict();
31564
+ /** @internal */ z.object({
31565
+ walletAddress: z.string().regex(EVM_ADDRESS_REGEX),
31566
+ chain: chainIdentifierSchema,
31567
+ pageSize: z.number().int().positive().optional(),
31568
+ pageAfter: z.string().min(1).optional(),
31569
+ config: borrowServiceConfigSchema$1.optional()
31570
+ }).strict();
31571
+ /** @internal */ const marketSortBySchema$1 = z.enum([
31572
+ 'lltv',
31573
+ 'borrowApy'
31574
+ ]);
31575
+ /** @internal */ const decimalFilterSchema$1 = z.string().regex(/^\d+(\.\d+)?$/, 'filter thresholds must be non-negative decimal strings');
31576
+ /**
31577
+ * Provider-level runtime schema for {@link ExploreMarketsServiceParams}.
31578
+ *
31579
+ * Mirrors the kit's `exploreMarketsParamsSchema` at the provider boundary so a
31580
+ * plain-JS caller invoking `BorrowServiceProvider.exploreMarkets` directly is
31581
+ * still validated before any filter reaches `url.searchParams` — including the
31582
+ * kit's `minLltv <= maxLltv` / `minBorrow <= maxBorrow` range check, which
31583
+ * otherwise lets an inverted range serialize into the request and return a
31584
+ * silently empty page.
31585
+ *
31586
+ * @internal
31587
+ */ z.object({
31588
+ chain: chainIdentifierSchema,
31589
+ minLltv: decimalFilterSchema$1.optional(),
31590
+ maxLltv: decimalFilterSchema$1.optional(),
31591
+ minBorrow: decimalFilterSchema$1.optional(),
31592
+ maxBorrow: decimalFilterSchema$1.optional(),
31593
+ sortBy: marketSortBySchema$1.optional(),
31594
+ pageSize: z.number().int().min(1).max(1000).optional(),
31595
+ pageAfter: z.string().min(1).optional(),
31596
+ config: borrowServiceConfigSchema$1.optional()
31597
+ }).strict().superRefine((value, ctx)=>{
31598
+ if (value.minLltv !== undefined && value.maxLltv !== undefined && Number(value.minLltv) > Number(value.maxLltv)) {
31599
+ ctx.addIssue({
31600
+ code: z.ZodIssueCode.custom,
31601
+ path: [
31602
+ 'maxLltv'
31603
+ ],
31604
+ message: 'maxLltv must be greater than or equal to minLltv'
31605
+ });
31606
+ }
31607
+ if (value.minBorrow !== undefined && value.maxBorrow !== undefined && Number(value.minBorrow) > Number(value.maxBorrow)) {
31608
+ ctx.addIssue({
31609
+ code: z.ZodIssueCode.custom,
31610
+ path: [
31611
+ 'maxBorrow'
31612
+ ],
31613
+ message: 'maxBorrow must be greater than or equal to minBorrow'
31614
+ });
31615
+ }
31616
+ });
31617
+ /** @internal */ const repayQuoteServiceParamsSchema = z.object({
31618
+ loanId: z.string().uuid(),
31619
+ chain: chainIdentifierSchema,
31620
+ repayAmount: positiveLoanAssetAmountSchema('repayAmount'),
31621
+ config: borrowServiceConfigSchema$1.optional()
31622
+ }).strict();
31623
+ /** @internal */ repayQuoteServiceParamsSchema.omit({
31624
+ chain: true
31625
+ }).extend({
31626
+ idempotencyKey: z.string().uuid().optional()
31627
+ });
31628
+ /** @internal */ const closeLoanQuoteServiceParamsSchema = z.object({
31629
+ loanId: z.string().uuid(),
31630
+ chain: chainIdentifierSchema,
31631
+ slippageBps: z.number().int().min(0).max(10_000).optional(),
31632
+ config: borrowServiceConfigSchema$1.optional()
31633
+ }).strict();
31634
+ /** Fields a borrow carries whichever way it names its position. @internal */ const borrowServiceParamsBaseShape = {
31635
+ loanAssetAmount: positiveLoanAssetAmountSchema('loanAssetAmount'),
31636
+ slippageBps: z.number().int().min(0).max(10_000).optional(),
31637
+ idempotencyKey: z.string().uuid().optional(),
31638
+ config: borrowServiceConfigSchema$1.optional()
31639
+ };
31640
+ /** A first-open: owner, chain and market are required together. @internal */ const openLoanServiceParamsSchema = z.object({
31641
+ ...borrowServiceParamsBaseShape,
31642
+ walletAddress: z.string().regex(EVM_ADDRESS_REGEX),
31643
+ chain: chainIdentifierSchema,
31644
+ marketId: z.string().min(1)
31645
+ }).strict();
31646
+ /** A borrow-more: the loan is the only addressing field. @internal */ const borrowMoreServiceParamsSchema = z.object({
31647
+ ...borrowServiceParamsBaseShape,
31648
+ loanId: z.string().uuid()
31649
+ }).strict();
31650
+ /**
31651
+ * The two ways a borrow names its position.
31652
+ *
31653
+ * A union of strict objects rather than one object with a refinement:
31654
+ * `.strict()` is what rejects a mixed body, and the service refuses `loanId`
31655
+ * next to any open-tuple field as well as an incomplete open tuple.
31656
+ *
31657
+ * @internal
31658
+ */ z.union([
31659
+ openLoanServiceParamsSchema,
31660
+ borrowMoreServiceParamsSchema
31661
+ ]);
31662
+ /** A first-open quote carries the complete owner/chain/market tuple. @internal */ const openLoanQuoteServiceParamsSchema = z.object({
31663
+ walletAddress: z.string().regex(EVM_ADDRESS_REGEX),
31664
+ chain: chainIdentifierSchema,
31665
+ marketId: z.string().min(1),
31666
+ loanAssetAmount: positiveLoanAssetAmountSchema('loanAssetAmount'),
31667
+ slippageBps: z.number().int().min(0).max(10_000).optional(),
31668
+ config: borrowServiceConfigSchema$1.optional()
31669
+ }).strict();
31670
+ /** A borrow-more quote needs only the existing loan ID. @internal */ const borrowMoreQuoteServiceParamsSchema = z.object({
31671
+ loanId: z.string().uuid(),
31672
+ loanAssetAmount: positiveLoanAssetAmountSchema('loanAssetAmount'),
31673
+ slippageBps: z.number().int().min(0).max(10_000).optional(),
31674
+ config: borrowServiceConfigSchema$1.optional()
31675
+ }).strict();
31676
+ /** @internal */ z.union([
31677
+ openLoanQuoteServiceParamsSchema,
31678
+ borrowMoreQuoteServiceParamsSchema
31679
+ ]);
31680
+ /** @internal */ z.object({
31681
+ chain: chainIdentifierSchema,
31682
+ marketId: z.string().min(1),
31683
+ loanAssetAmount: positiveLoanAssetAmountSchema('loanAssetAmount'),
31684
+ // `.finite()` is load-bearing, not decoration: the value is encoded as a
31685
+ // decimal string on the wire, and there is no decimal string for Infinity.
31686
+ targetHealthFactor: z.number().positive().finite(),
31687
+ config: borrowServiceConfigSchema$1.optional()
31688
+ }).strict();
31689
+ /** @internal */ z.object({
31690
+ chain: chainIdentifierSchema,
31691
+ marketId: z.string().min(1),
31692
+ collateralAmount: positiveCollateralAmountSchema('collateralAmount'),
31693
+ config: borrowServiceConfigSchema$1.optional()
31694
+ }).strict();
31695
+ /** @internal */ closeLoanQuoteServiceParamsSchema.omit({
31696
+ chain: true
31697
+ }).extend({
31698
+ idempotencyKey: z.string().uuid().optional()
31699
+ });
31700
+ /**
31701
+ * Provider-level runtime schema for the non-adapter portion of
31702
+ * {@link ClaimRewardsServiceParams} / {@link GetRewardsServiceParams}.
31703
+ *
31704
+ * `from` (the adapter context) is intentionally excluded — like
31705
+ * `repay`/`closeLoan`, it is validated structurally by TypeScript and resolved
31706
+ * via `resolveAdapterContext` rather than by a Zod schema, since it carries a
31707
+ * live adapter instance rather than plain data. `config` is the only field
31708
+ * that reaches the wire, so it is what gets validated here, matching every
31709
+ * other provider method's independent `parseOrThrow` call.
31710
+ *
31711
+ * @internal
31712
+ */ z.object({
31713
+ config: borrowServiceConfigSchema$1.optional()
31714
+ }).strict();
31715
+ /**
31716
+ * Provider-level runtime schema for {@link GetPositionServiceParams}.
31717
+ *
31718
+ * @internal
31719
+ */ z.object({
31720
+ loanId: z.string().uuid(),
31721
+ config: borrowServiceConfigSchema$1.optional()
31722
+ }).strict();
31723
+ /** @internal */ const withdrawCollateralQuoteServiceParamsSchema = z.object({
31724
+ loanId: z.string().uuid(),
31725
+ chain: chainIdentifierSchema,
31726
+ // Carries no decimal-place cap: the repayment is USDC, but the collateral
31727
+ // is the market's own token and may hold more precision than USDC's six.
31728
+ collateralAmount: positiveCollateralAmountSchema('collateralAmount'),
31729
+ slippageBps: z.number().int().min(0).max(10_000).optional(),
31730
+ config: borrowServiceConfigSchema$1.optional()
31731
+ }).strict();
31732
+ /** @internal */ withdrawCollateralQuoteServiceParamsSchema.omit({
31733
+ chain: true
31734
+ }).extend({
31735
+ idempotencyKey: z.string().uuid().optional()
31736
+ });
31737
+ /**
31738
+ * Adding collateral sizes no second leg, so there is no slippage bound.
31739
+ *
31740
+ * @internal
31741
+ */ const addCollateralQuoteServiceParamsSchema = z.object({
31742
+ loanId: z.string().uuid(),
31743
+ chain: chainIdentifierSchema,
31744
+ collateralAmount: positiveCollateralAmountSchema('collateralAmount'),
31745
+ config: borrowServiceConfigSchema$1.optional()
31746
+ }).strict();
31747
+ /** @internal */ addCollateralQuoteServiceParamsSchema.omit({
31748
+ chain: true
31749
+ }).extend({
31750
+ idempotencyKey: z.string().uuid().optional()
31751
+ });
31752
+ /** @internal */ const borrowAssetAmountSchema = z.object({
31753
+ token: z.string().min(1),
31754
+ tokenAddress: z.string().regex(EVM_ADDRESS_REGEX),
31755
+ decimals: z.number().int().min(0).max(255),
31756
+ amount: z.string().regex(HUMAN_READABLE_AMOUNT_REGEX$1)
31757
+ });
31758
+ /** @internal */ const healthFactorBandSchema = z.enum([
31759
+ 'SAFE',
31760
+ 'WARN',
31761
+ 'URGENT',
31762
+ 'IMMINENT',
31763
+ 'LIQUIDATABLE'
31764
+ ]);
31765
+ /**
31766
+ * Whether a loan's economic projection has converged.
31767
+ *
31768
+ * `PENDING` means the loan's identity and lifecycle are known but its
31769
+ * economics are not yet materialized — every economic field is `null` until
31770
+ * the service's repair queue fills them in. See {@link loanSchema}.
31771
+ *
31772
+ * @internal
31773
+ */ const loanDataStatusSchema = z.enum([
31774
+ 'READY',
31775
+ 'PENDING'
31776
+ ]);
31777
+ /**
31778
+ * Lifecycle status of a loan: still open (`active`) or fully wound down
31779
+ * (`closed`). Unlike {@link loanDataStatusSchema} this is not about whether
31780
+ * economics have converged — a `closed` loan is `READY` with every economic
31781
+ * field legitimately zeroed, not `null`. Shared by {@link loanSchema} and
31782
+ * {@link loanPayloadSchema}, which return the same field for the same entity.
31783
+ *
31784
+ * @internal
31785
+ */ const loanStatusSchema = z.enum([
31786
+ 'active',
31787
+ 'closed'
31788
+ ]);
31789
+ /**
31790
+ * Zod schema for one loan in the `GET /v1/borrowKit/loans` page.
31791
+ *
31792
+ * @remarks Every economic field is nullable because the service returns a
31793
+ * `PENDING` loan — identity and lifecycle fields set, all economics `null` —
31794
+ * while materialization, principal, debt, or market APY is still converging.
31795
+ * That is the documented representation of a freshly-created loan, so
31796
+ * declaring these non-nullable made the first read after a `borrow()` fail
31797
+ * validation. `dataStatus` tells callers which of the two shapes they hold.
31798
+ *
31799
+ * `status` distinguishes a fully wound-down loan — `READY` with legitimately
31800
+ * zeroed economics — from a live one. Without it a caller cannot tell the two
31801
+ * apart, which is what left a closed loan sitting in `getLoans` output looking
31802
+ * open. It is declared **required here and on {@link loanPayloadSchema}**: a
31803
+ * live `GET /v1/borrowKit/loans` page reports it on every loan, including a
31804
+ * closed one.
31805
+ *
31806
+ * @internal
31807
+ */ const loanSchema = z.object({
31808
+ loanId: z.string().uuid(),
31809
+ chain: z.string().min(1),
31810
+ marketId: z.string().min(1),
31811
+ walletAddress: z.string().regex(EVM_ADDRESS_REGEX),
31812
+ dataStatus: loanDataStatusSchema,
31813
+ collateral: borrowAssetAmountSchema.nullable(),
31814
+ borrowed: borrowAssetAmountSchema.nullable(),
31815
+ principalBorrowed: borrowAssetAmountSchema.nullable(),
31816
+ accruedInterest: borrowAssetAmountSchema.nullable(),
31817
+ borrowApy: z.number().nullable(),
31818
+ ltv: z.number().nullable(),
31819
+ healthFactor: z.number().nullable(),
31820
+ healthFactorBand: healthFactorBandSchema.nullable(),
31821
+ liquidationPrice: borrowAssetAmountSchema.nullable(),
31822
+ status: loanStatusSchema
31823
+ });
31824
+ /**
31825
+ * Zod schema for the `GET /v1/borrowKit/loans` API response. The Borrow
31826
+ * Service wraps every response in a `data` envelope.
31827
+ *
31828
+ * @internal
31829
+ */ z.object({
31830
+ data: z.object({
31831
+ loans: z.array(loanSchema),
31832
+ pagination: z.object({
31833
+ pageAfter: z.string().optional()
31834
+ })
31835
+ })
31836
+ });
31837
+ /** @internal */ const borrowFeeSchema = z.object({
31838
+ type: z.string(),
31839
+ token: z.string(),
31840
+ amount: borrowAssetAmountSchema
31841
+ });
31842
+ /** @internal */ const borrowQuoteGasFeeSchema = z.object({
31843
+ name: z.string(),
31844
+ fees: z.object({
31845
+ gas: z.string(),
31846
+ gasPrice: borrowAssetAmountSchema,
31847
+ fee: borrowAssetAmountSchema
31848
+ })
31849
+ });
31850
+ /** @internal */ const hexBytesSchema = z.string().regex(/^0x(?:[0-9a-fA-F]{2})*$/).transform((value)=>value);
31851
+ function isNonNegativeBigIntLike(value) {
31852
+ try {
31853
+ return BigInt(value) >= 0n;
31854
+ } catch {
31855
+ return false;
31856
+ }
31857
+ }
31858
+ /** @internal */ const uint256LikeSchema = z.union([
31859
+ z.string(),
31860
+ z.bigint()
31861
+ ]).refine(isNonNegativeBigIntLike, {
31862
+ message: 'value must be a non-negative bigint-compatible string or bigint'
31863
+ });
31864
+ /**
31865
+ * A quoted decimal that has to parse finite, for the response fields the
31866
+ * service marshals from a Go `decimal.Decimal`.
31867
+ *
31868
+ * Deliberately not `z.coerce.number()`: coercion maps `''` and whitespace to
31869
+ * `0`, so a service that stopped sending a value would read as a real `0.0` —
31870
+ * a health factor of zero is a liquidated position, so that failure mode
31871
+ * silently reports the opposite of the truth. Refusing anything unparseable
31872
+ * here also makes the `decimalToNumber` mapper total, with no fallback value to
31873
+ * invent downstream.
31874
+ */ const decimalNumericString = z.string().min(1).refine((value)=>value.trim() !== '' && Number.isFinite(Number(value)), {
31875
+ message: 'value must be a decimal string that parses to a finite number'
31876
+ });
31877
+ /** @internal */ const borrowExecutionParamsSchema = z.object({
31878
+ instructions: z.array(z.object({
31879
+ target: evmAddressSchema$1,
31880
+ data: hexBytesSchema,
31881
+ value: uint256LikeSchema,
31882
+ tokenIn: evmAddressSchema$1,
31883
+ amountToApprove: uint256LikeSchema,
31884
+ tokenOut: evmAddressSchema$1,
31885
+ minTokenOut: uint256LikeSchema
31886
+ }).passthrough()).min(1),
31887
+ tokens: z.array(z.object({
31888
+ token: evmAddressSchema$1,
31889
+ beneficiary: evmAddressSchema$1
31890
+ }).passthrough()),
31891
+ execId: uint256LikeSchema,
31892
+ deadline: uint256LikeSchema,
31893
+ metadata: hexBytesSchema
31894
+ }).passthrough();
31895
+ /**
31896
+ * Inner payload for a Borrow Service execution response, shared by every
31897
+ * write endpoint (`borrow`, `repay`, `addCollateral`, `closeLoan`,
31898
+ * `withdrawCollateral`). Kept separate from {@link loanExecutionSchema} so
31899
+ * {@link withdrawCollateralExecutionSchema} — the one execution response that
31900
+ * omits `chain` — can build on it without re-declaring every field.
31901
+ *
31902
+ * @internal
31903
+ */ const loanExecutionPayloadSchema = z.object({
31904
+ loanId: z.string().uuid(),
31905
+ chain: z.string().min(1),
31906
+ execId: z.string().min(1),
31907
+ signature: evmSignatureSchema,
31908
+ executionParams: borrowExecutionParamsSchema
31909
+ });
31910
+ /**
31911
+ * Zod schema for a Borrow Service execution API response. The Borrow Service
31912
+ * wraps every response in a `data` envelope.
31913
+ *
31914
+ * @internal
31915
+ */ z.object({
31916
+ data: loanExecutionPayloadSchema
31917
+ });
31918
+ /**
31919
+ * Zod schema for the `POST /v1/borrowKit/loans/borrow` execution API
31920
+ * response. Extends {@link loanExecutionPayloadSchema} with `fees`, the fee
31921
+ * legs the signed bundle charges at origination (empty when nothing is
31922
+ * charged) — a field only this endpoint's response carries.
31923
+ *
31924
+ * `fees` is optional here, and only here. The API spec documents it as always
31925
+ * present and `[]` when nothing is charged, but the deployed service omits the
31926
+ * key entirely; requiring it fails validation on a healthy `200` and turns
31927
+ * every borrow into a FATAL error. It is optional rather than defaulted
31928
+ * because the guard consuming this schema reports `safeParse().success` and
31929
+ * discards the parsed value, so a default would never reach a reader.
31930
+ *
31931
+ * The absence is preserved to the caller rather than normalised to `[]`:
31932
+ * `fetchBorrow` returns `response.data` unchanged and `BorrowResponse.fees` is
31933
+ * optional, so "the service named no legs" stays distinguishable from "nothing
31934
+ * was charged". `executeBorrow` is what resolves it, by reporting the fee
31935
+ * transfers the receipt paid out under `type: 'unknown'`.
31936
+ *
31937
+ * @internal
31938
+ */ z.object({
31939
+ data: loanExecutionPayloadSchema.extend({
31940
+ fees: z.array(borrowFeeSchema).optional()
31941
+ })
31942
+ });
31943
+ /** @internal */ const resultingHealthSchema = {
31944
+ resultingHealthFactor: z.number().nullable(),
31945
+ resultingLtv: z.number().nullable(),
31946
+ resultingBand: healthFactorBandSchema,
31947
+ liquidationPrice: borrowAssetAmountSchema.nullable()
31948
+ };
31949
+ /**
31950
+ * Zod schema for the `POST /v1/borrowKit/loans/:loan_id/repay/quote` API
31951
+ * response. The Borrow Service wraps every response in a `data` envelope.
31952
+ *
31953
+ * @internal
31954
+ */ z.object({
31955
+ data: z.object({
31956
+ chain: z.string().min(1),
31957
+ repayAmount: borrowAssetAmountSchema,
31958
+ fees: z.array(borrowFeeSchema),
31959
+ gasFees: z.array(borrowQuoteGasFeeSchema),
31960
+ ...resultingHealthSchema
31961
+ })
31962
+ });
31963
+ // ---------------------------------------------------------------------------
31964
+ // Loan position response schema (SE-6383: getPosition)
31965
+ // ---------------------------------------------------------------------------
31966
+ /**
31967
+ * Zod schema for the inner loan position payload.
31968
+ *
31969
+ * @remarks Matches the real `GET /v1/borrowKit/loans/:loan_id` wire shape
31970
+ * (Zenith's `borrowkit.LoanResponse`). Two fields in the original placeholder
31971
+ * did not exist on that contract and made every lookup of a real loan fail
31972
+ * validation: the owner address is sent as `walletAddress`, not `wallet`, and
31973
+ * the service sends no liquidation threshold at all — liquidation LTV is a
31974
+ * market property, served by `GET /v1/borrowKit/markets/:chain/:market_id` as
31975
+ * `lltv`, not a per-loan one.
31976
+ *
31977
+ * `collateral` and `borrowed` are nullable for the same reason they are on
31978
+ * {@link loanSchema}: this route returns the same `PENDING` representation —
31979
+ * null economics — for a loan whose on-chain create has not been observed
31980
+ * yet, which is exactly the state a `borrow()` leaves behind. `dataStatus`
31981
+ * distinguishes that from a `READY` loan whose economics happen to be zero.
31982
+ *
31983
+ * Declares the same economic field set as {@link loanSchema}, because Zenith
31984
+ * serves both routes from one struct (`borrowkit.LoanResponse`): a loan read
31985
+ * one at a time reports exactly what the same loan reports inside a page, so a
31986
+ * caller never has to list a wallet to recover a field. Both declare `status`
31987
+ * as required: a live `GET /v1/borrowKit/loans` page reports it on every loan,
31988
+ * including a closed one.
31989
+ *
31990
+ * `registeredAt` is the sole field on the wire that neither schema declares.
31991
+ *
31992
+ * @internal
31993
+ */ const loanPayloadSchema = z.object({
31994
+ loanId: z.string(),
31995
+ walletAddress: z.string(),
31996
+ chain: z.string(),
31997
+ marketId: z.string().min(1),
31998
+ dataStatus: loanDataStatusSchema,
31999
+ collateral: borrowAssetAmountSchema.nullable(),
32000
+ borrowed: borrowAssetAmountSchema.nullable(),
32001
+ principalBorrowed: borrowAssetAmountSchema.nullable(),
32002
+ accruedInterest: borrowAssetAmountSchema.nullable(),
32003
+ borrowApy: z.number().nullable(),
32004
+ ltv: z.number().nullable(),
32005
+ healthFactor: z.number().nullable(),
32006
+ healthFactorBand: healthFactorBandSchema.nullable(),
32007
+ liquidationPrice: borrowAssetAmountSchema.nullable(),
32008
+ status: loanStatusSchema
32009
+ });
32010
+ /**
32011
+ * Zod schema for the `GET /v1/borrowKit/loans/:loan_id` API response.
32012
+ *
32013
+ * @internal
32014
+ */ z.object({
32015
+ data: loanPayloadSchema
32016
+ });
32017
+ // ---------------------------------------------------------------------------
32018
+ // Inferred types
32019
+ // ---------------------------------------------------------------------------
32020
+ /**
32021
+ * Zod schema for the `POST /v1/borrowKit/loans/borrow/quote` API response.
32022
+ * The Borrow Service wraps every response in a `data` envelope.
32023
+ *
32024
+ * @internal
32025
+ */ z.object({
32026
+ data: z.object({
32027
+ chain: z.string().min(1),
32028
+ collateralAmount: borrowAssetAmountSchema,
32029
+ loanAssetAmount: borrowAssetAmountSchema,
32030
+ // Nullable: the service reports null when the curated market row has no
32031
+ // cached rate yet, rather than a misleading 0%. The loans and markets
32032
+ // schemas already allow it; this one did not, so an unpriced market failed
32033
+ // validation and surfaced the whole quote as SERVICE_UNKNOWN_ERROR.
32034
+ borrowApy: z.number().nullable(),
32035
+ fees: z.array(borrowFeeSchema),
32036
+ gasFees: z.array(borrowQuoteGasFeeSchema),
32037
+ ...resultingHealthSchema
32038
+ })
32039
+ });
32040
+ /**
32041
+ * Zod schema for the `POST /v1/borrowKit/loans/:loan_id/addCollateral/quote`
32042
+ * API response. The Borrow Service wraps every response in a `data` envelope.
32043
+ *
32044
+ * @internal
32045
+ */ z.object({
32046
+ data: z.object({
32047
+ chain: z.string().min(1),
32048
+ collateralAmount: borrowAssetAmountSchema,
32049
+ fees: z.array(borrowFeeSchema),
32050
+ gasFees: z.array(borrowQuoteGasFeeSchema),
32051
+ ...resultingHealthSchema
32052
+ })
32053
+ });
32054
+ /**
32055
+ * Zod schema for the `POST /v1/borrowKit/loans/:loan_id/closeLoan/quote` API
32056
+ * response. The Borrow Service wraps every response in a `data` envelope.
32057
+ *
32058
+ * @internal
32059
+ */ z.object({
32060
+ data: z.object({
32061
+ chain: z.string().min(1),
32062
+ repayAmount: borrowAssetAmountSchema,
32063
+ collateralAmount: borrowAssetAmountSchema,
32064
+ fees: z.array(borrowFeeSchema),
32065
+ gasFees: z.array(borrowQuoteGasFeeSchema),
32066
+ ...resultingHealthSchema
32067
+ })
32068
+ });
32069
+ /**
32070
+ * The collateral-withdrawal execution response is the one Borrow Service
32071
+ * execution body that omits `chain`, so it cannot reuse
32072
+ * {@link loanExecutionSchema} unchanged. Built on
32073
+ * {@link loanExecutionPayloadSchema} so the `data` envelope only needs to be
32074
+ * applied once.
32075
+ *
32076
+ * @internal
32077
+ */ z.object({
32078
+ data: loanExecutionPayloadSchema.omit({
32079
+ chain: true
32080
+ })
32081
+ });
32082
+ /**
32083
+ * Zod schema for the
32084
+ * `POST /v1/borrowKit/loans/:loan_id/withdrawCollateral/quote` API response.
32085
+ * The Borrow Service wraps every response in a `data` envelope.
32086
+ *
32087
+ * @internal
32088
+ */ z.object({
32089
+ data: z.object({
32090
+ chain: z.string().min(1),
32091
+ repayAmount: borrowAssetAmountSchema,
32092
+ collateralAmount: borrowAssetAmountSchema,
32093
+ fees: z.array(borrowFeeSchema),
32094
+ gasFees: z.array(borrowQuoteGasFeeSchema),
32095
+ ...resultingHealthSchema
32096
+ })
32097
+ });
32098
+ /**
32099
+ * Zod schema for the `POST /v1/borrowKit/loans/requiredCollateral/quote` API
32100
+ * response. The Borrow Service wraps every response in a `data` envelope.
32101
+ *
32102
+ * @remarks Deliberately not {@link resultingHealthSchema}. The two
32103
+ * market-scoped sizing quotes send four fields, not seven — no `resultingLtv`
32104
+ * and no `resultingBand` — so requiring the loan-scoped health block rejected
32105
+ * every well-formed response with `MALFORMED_RESPONSE`.
32106
+ *
32107
+ * `resultingHealthFactor` arrives as a decimal *string* on this route alone
32108
+ * (Zenith types it `decimal.Decimal`, which marshals quoted, where every other
32109
+ * route types it `*float64`). It is accepted as either encoding here and
32110
+ * narrowed to a `number` by the mapper — deliberately not via a Zod
32111
+ * `.transform`, because these schemas are consumed through boolean type guards
32112
+ * (`isRequiredCollateralQuote`), which discard a transform's output and would
32113
+ * leave the raw string flowing on while typed as a number.
32114
+ *
32115
+ * @internal
32116
+ */ z.object({
32117
+ data: z.object({
32118
+ chain: z.string().min(1),
32119
+ requiredCollateral: borrowAssetAmountSchema,
32120
+ resultingHealthFactor: z.union([
32121
+ z.number(),
32122
+ decimalNumericString
32123
+ ]).nullable(),
32124
+ liquidationPrice: borrowAssetAmountSchema.nullable()
32125
+ })
32126
+ });
32127
+ /**
32128
+ * Zod schema for the `POST /v1/borrowKit/loans/maxBorrow/quote` API response.
32129
+ * The Borrow Service wraps every response in a `data` envelope.
32130
+ *
32131
+ * @remarks Four fields, not seven — see
32132
+ * {@link requiredCollateralQuoteSchema}. `resultingHealthFactor` is a plain
32133
+ * JSON number here (Zenith types it `*float64` on this route), so unlike its
32134
+ * inverse it needs no string coercion.
32135
+ *
32136
+ * @internal
32137
+ */ z.object({
32138
+ data: z.object({
32139
+ chain: z.string().min(1),
32140
+ maxBorrowAmount: borrowAssetAmountSchema,
32141
+ resultingHealthFactor: z.number().nullable(),
32142
+ liquidationPrice: borrowAssetAmountSchema.nullable()
32143
+ })
32144
+ });
32145
+ /**
32146
+ * Zod schema for the `GET`/`POST /v1/borrowKit/integrators/config` API
32147
+ * response. The Borrow Service wraps every response in a `data` envelope.
32148
+ *
32149
+ * @internal
32150
+ */ z.object({
32151
+ data: z.object({
32152
+ integratorId: z.string().min(1),
32153
+ integratorFeeBps: z.number().int().min(0),
32154
+ integratorFeeAddress: z.string().regex(EVM_ADDRESS_REGEX).nullable(),
32155
+ createdAt: z.string().datetime({
32156
+ offset: true
32157
+ }),
32158
+ updatedAt: z.string().datetime({
32159
+ offset: true
32160
+ })
32161
+ })
32162
+ });
32163
+ /** @internal */ z.object({
32164
+ config: borrowServiceConfigSchema$1.optional()
32165
+ }).strict();
32166
+ /**
32167
+ * Both fee fields are always required, so neither half is accepted alone.
32168
+ *
32169
+ * `integratorFeeAddress` may be `null` only when `integratorFeeBps` is 0, which
32170
+ * is how an integrator turns a fee off and clears the recipient. The reverse is
32171
+ * still allowed: a 0 rate can keep an address, which pauses the fee without
32172
+ * forgetting where it used to go. A rate above 0 with no recipient is unusable.
32173
+ *
32174
+ * The rule spans two fields, so it cannot live on either one and cannot be
32175
+ * expressed in the type. `{ integratorFeeBps: 25, integratorFeeAddress: null }`
32176
+ * type-checks and fails here.
32177
+ *
32178
+ * @internal
32179
+ */ z.object({
32180
+ integratorFeeBps: z.number().int().min(0).max(10_000),
32181
+ integratorFeeAddress: z.string().regex(EVM_ADDRESS_REGEX).nullable(),
32182
+ config: borrowServiceConfigSchema$1.optional()
32183
+ }).strict().refine((params)=>params.integratorFeeAddress !== null || params.integratorFeeBps === 0, {
32184
+ message: 'integratorFeeAddress can only be null when integratorFeeBps is 0',
32185
+ path: [
32186
+ 'integratorFeeAddress'
32187
+ ]
32188
+ });
32189
+ /**
32190
+ * Zod schema for the webhook registration payload inside the API `data`
32191
+ * envelope, returned by
32192
+ * `POST /v1/borrowKit/loans/:loan_id/webhookRegistration`.
32193
+ *
32194
+ * @internal
32195
+ */ const webhookRegistrationPayloadSchema = z.object({
32196
+ loanId: z.string().uuid(),
32197
+ webhookUrl: z.string().nullish()
32198
+ });
32199
+ /**
32200
+ * Zod schema validating the caller-supplied `loanId` and `webhookUrl` inputs
32201
+ * to `BorrowServiceProvider.registerWebhook` before they're signed and
32202
+ * submitted.
32203
+ *
32204
+ * `loanId` must be a UUID, consistent with every other loan-scoped schema in
32205
+ * this package (`repayQuoteServiceParamsSchema`, `loanSchema`,
32206
+ * `loanExecutionSchema`). `webhookUrl` must be `null` (an explicit "unset"
32207
+ * intent) or a syntactically valid **https** URL string — an empty string is
32208
+ * neither, so it's rejected here rather than silently reaching the `?? ''`
32209
+ * coercion that `buildWebhookRegistrationTypedData` applies to a genuine
32210
+ * `null`. The scheme is restricted to `https:` because this URL is where
32211
+ * loan-lifecycle callbacks get delivered and is cryptographically signed by
32212
+ * the loan owner: allowing `http:` would let a signed registration point at
32213
+ * a plaintext endpoint (or a link-local/loopback address reachable only from
32214
+ * the callback sender's own network), and the SDK is the natural place to
32215
+ * refuse that before it's ever signed or transmitted.
32216
+ *
32217
+ * @internal
32218
+ */ z.object({
32219
+ loanId: z.string().uuid(),
32220
+ webhookUrl: z.string().url().refine((value)=>isHttpsUrl$1(value), 'webhookUrl must use the https scheme').nullable()
32221
+ }).strict();
32222
+ /**
32223
+ * Zod schema for the `POST /v1/borrowKit/loans/:loan_id/webhookRegistration`
32224
+ * API response.
32225
+ *
32226
+ * The Borrow Service API wraps the webhook registration payload in a `data`
32227
+ * envelope.
32228
+ *
32229
+ * @internal
32230
+ */ z.object({
32231
+ data: webhookRegistrationPayloadSchema
32232
+ });
32233
+ /** @internal */ const marketAssetSchema = z.object({
32234
+ symbol: z.string().min(1),
32235
+ address: z.string().regex(EVM_ADDRESS_REGEX),
32236
+ decimals: z.number().int().nonnegative()
32237
+ });
32238
+ /**
32239
+ * Wire shape for a typed token amount: raw atoms as an integer string plus the
32240
+ * token's decimals, matching `@core/amounts` {@link AmountJSON}.
32241
+ *
32242
+ * @internal
32243
+ */ const amountJsonSchema = z.object({
32244
+ raw: z.string().refine(isNonNegativeBigIntLike, {
32245
+ message: 'raw must be a non-negative bigint-compatible string'
32246
+ }),
32247
+ decimals: z.number().int().nonnegative()
32248
+ });
32249
+ /**
32250
+ * Wire shape for a single borrow market returned by
32251
+ * `GET /v1/borrowKit/markets/:chain/:market_id`. `chain` is the Borrow Service
32252
+ * API chain code (mapped back to the SDK chain by `toMarketInfo`); display
32253
+ * ratios arrive as numbers, token amounts as `{ raw, decimals }` objects.
32254
+ *
32255
+ * @remarks `lltv`, `borrowAssets`, `liquidity`, `borrowApy`, `utilization`,
32256
+ * and `refreshedAt` are all nullable because the service's economics are
32257
+ * cached projections, not live reads: a market that has been discovered but
32258
+ * not yet backfilled by the cache reports every economic field as `null`
32259
+ * rather than a placeholder value. An idle market keeps its last-known values
32260
+ * and old `refreshedAt` rather than reporting zeros, so `null` unambiguously
32261
+ * means "never backfilled." `borrowCap` is always `null` (Morpho Blue has no
32262
+ * per-market cap) but carried anyway so the field does not have to be added
32263
+ * later as a breaking change; it is also optional since the service may omit
32264
+ * it entirely.
32265
+ *
32266
+ * @internal
32267
+ */ const marketResponseSchema = z.object({
32268
+ marketId: z.string().min(1),
32269
+ protocol: z.enum([
32270
+ 'morpho'
32271
+ ]),
32272
+ chain: z.string().min(1),
32273
+ loanAsset: marketAssetSchema,
32274
+ collateralAsset: marketAssetSchema,
32275
+ lltv: z.number().nonnegative().nullable(),
32276
+ borrowCap: amountJsonSchema.nullable().optional(),
32277
+ borrowAssets: amountJsonSchema.nullable(),
32278
+ liquidity: amountJsonSchema.nullable(),
32279
+ borrowApy: z.number().nonnegative().nullable(),
32280
+ utilization: z.number().nonnegative().nullable(),
32281
+ refreshedAt: z.string().datetime({
32282
+ offset: true
32283
+ }).nullable()
32284
+ });
32285
+ /**
32286
+ * Zod schema for the `GET /v1/borrowKit/markets/:chain/:market_id` API
32287
+ * response. The Borrow Service wraps every response in a `data` envelope;
32288
+ * {@link marketResponseSchema} itself stays unwrapped since it is also reused
32289
+ * per-entry inside {@link listMarketsResponseSchema}.
32290
+ *
32291
+ * @internal
32292
+ */ z.object({
32293
+ data: marketResponseSchema
32294
+ });
32295
+ /**
32296
+ * Wire shape for a page of borrow markets returned by
32297
+ * `GET /v1/borrowKit/markets`. Reuses {@link marketResponseSchema} per entry so
32298
+ * the per-market wire format stays defined in one place, and adds the
32299
+ * pagination envelope: an opaque `pageAfter` cursor absent on the last page.
32300
+ * The Borrow Service wraps every response in a `data` envelope.
32301
+ *
32302
+ * @internal
32303
+ */ z.object({
32304
+ data: z.object({
32305
+ markets: z.array(marketResponseSchema),
32306
+ pagination: z.object({
32307
+ pageAfter: z.string().optional()
32308
+ })
32309
+ })
32310
+ });
32311
+
32312
+ /** @internal */ const BORROW_CHAIN_TO_API = {
32313
+ [Blockchain.Arc]: 'ARC',
32314
+ [Blockchain.Arc_Testnet]: 'ARC-TESTNET'
32315
+ };
32316
+
32317
+ Object.fromEntries(Object.entries(BORROW_CHAIN_TO_API).map(([sdkChain, apiChain])=>[
32318
+ apiChain,
32319
+ sdkChain
32320
+ ]));
32321
+
32322
+ /**
32323
+ * Schema for the Borrow Service configuration options.
32324
+ *
32325
+ * `apiKey` carries a Circle API key (`<ENV>_API_KEY:<keyId>:<keySecret>`) or a
32326
+ * legacy `KIT_KEY:<keyId>:<keySecret>` value. It is shape-checked only — as
32327
+ * with every other field here, the credential's format is the provider's call,
32328
+ * and `buildConfig` rejects a malformed one via `isValidApiKey`. The empty
32329
+ * string is allowed on purpose: `process.env.CIRCLE_API_KEY ?? ''` yields one
32330
+ * when the variable is unset, and the provider reads that as "no credential"
32331
+ * rather than as a malformed one.
32332
+ *
32333
+ * This schema is `.strict()`, so a field missing here is not merely unvalidated
32334
+ * — it is rejected outright, which is what made an `apiKey` config unusable
32335
+ * before it was listed.
32336
+ *
32337
+ * @internal
32338
+ */ const borrowServiceConfigSchema = z.object({
32339
+ apiKey: z.string().optional(),
32340
+ baseUrl: z.string().url('baseUrl must be a valid URL').optional()
32341
+ }).strict();
32342
+ /**
32343
+ * Runtime schema for {@link GetLoansParams}.
32344
+ *
32345
+ * @example
32346
+ * ```typescript
32347
+ * import { getLoansParamsSchema } from '@circle-fin/borrow-kit'
32348
+ *
32349
+ * const result = getLoansParamsSchema.safeParse({
32350
+ * walletAddress: '0x1111111111111111111111111111111111111111',
32351
+ * chain: 'Arc_Testnet',
32352
+ * })
32353
+ * ```
32354
+ */ z.object({
32355
+ walletAddress: evmAddressSchema$1,
32356
+ chain: chainIdentifierSchema,
32357
+ pageSize: z.number().int().positive().optional(),
32358
+ pageAfter: z.string().min(1).optional(),
32359
+ config: borrowServiceConfigSchema.optional()
32360
+ }).strict();
32361
+ /**
32362
+ * Runtime schema for {@link GetIntegratorConfigParams}.
32363
+ *
32364
+ * @example
32365
+ * ```typescript
32366
+ * import { getIntegratorConfigParamsSchema } from '@circle-fin/borrow-kit'
32367
+ *
32368
+ * const result = getIntegratorConfigParamsSchema.safeParse({
32369
+ * config: { apiKey: 'TEST_API_KEY:key-id:key-secret' },
32370
+ * })
32371
+ * ```
32372
+ */ z.object({
32373
+ config: borrowServiceConfigSchema.optional()
32374
+ }).strict();
32375
+ /**
32376
+ * Runtime schema for {@link GetMarketParams}.
32377
+ *
32378
+ * @example
32379
+ * ```typescript
32380
+ * import { getMarketParamsSchema } from '@circle-fin/borrow-kit'
32381
+ *
32382
+ * const result = getMarketParamsSchema.safeParse({
32383
+ * chain: 'Arc_Testnet',
32384
+ * marketId: '0xabc...',
32385
+ * })
32386
+ * ```
32387
+ */ z.object({
32388
+ chain: chainIdentifierSchema,
32389
+ marketId: z.string().min(1),
32390
+ config: borrowServiceConfigSchema.optional()
32391
+ }).strict();
32392
+ /**
32393
+ * Runtime schema for {@link SetIntegratorConfigParams}.
32394
+ *
32395
+ * Both fee fields are required, so neither half is accepted alone.
32396
+ * `integratorFeeAddress` may be `null` only when `integratorFeeBps` is 0, which
32397
+ * turns the fee off and clears the recipient. A rate above 0 with no recipient
32398
+ * is unusable and is rejected.
32399
+ *
32400
+ * @example
32401
+ * ```typescript
32402
+ * import { setIntegratorConfigParamsSchema } from '@circle-fin/borrow-kit'
32403
+ *
32404
+ * const result = setIntegratorConfigParamsSchema.safeParse({
32405
+ * integratorFeeBps: 25,
32406
+ * integratorFeeAddress: '0x1111111111111111111111111111111111111111',
32407
+ * })
32408
+ *
32409
+ * // Turning the fee off and clearing the recipient.
32410
+ * const cleared = setIntegratorConfigParamsSchema.safeParse({
32411
+ * integratorFeeBps: 0,
32412
+ * integratorFeeAddress: null,
32413
+ * })
32414
+ * ```
32415
+ */ z.object({
32416
+ integratorFeeBps: z.number().int().min(0).max(10_000),
32417
+ integratorFeeAddress: evmAddressSchema$1.nullable(),
32418
+ config: borrowServiceConfigSchema.optional()
32419
+ }).strict().refine((params)=>params.integratorFeeAddress !== null || params.integratorFeeBps === 0, {
32420
+ message: 'integratorFeeAddress can only be null when integratorFeeBps is 0',
32421
+ path: [
32422
+ 'integratorFeeAddress'
32423
+ ]
32424
+ });
32425
+ /** @internal */ const HUMAN_READABLE_AMOUNT_REGEX = /^(?:0|[1-9]\d*)(?:\.\d+)?$/;
32426
+ /**
32427
+ * @param maxDecimals - Decimal places the amount may carry, or `null` for no
32428
+ * cap. USDC amounts take the default six; an amount denominated in a market's
32429
+ * own collateral token may be finer, so those pass `null`.
32430
+ * @internal
32431
+ */ function createPositiveAmountSchema(field, maxDecimals = 6) {
32432
+ const positive = z.string().regex(HUMAN_READABLE_AMOUNT_REGEX, `${field} must be a human-readable decimal string`).refine((value)=>/[1-9]/.test(value), `${field} must be greater than zero`);
32433
+ if (maxDecimals === null) {
32434
+ return positive;
32435
+ }
32436
+ return positive.refine((value)=>(value.split('.')[1]?.length ?? 0) <= maxDecimals, `${field} must have at most ${maxDecimals.toString()} decimal places`);
32437
+ }
32438
+ /** @internal */ const positiveAmountSchema = createPositiveAmountSchema('repayAmount');
32439
+ const NIL_UUID = '00000000-0000-0000-0000-000000000000';
32440
+ const idempotencyKeySchema = z.string().uuid().refine((value)=>value !== NIL_UUID, {
32441
+ message: 'idempotencyKey must not be the nil UUID; generate a fresh one per write'
32442
+ });
32443
+ /** @internal */ const adapterContextSchema = z.object({
32444
+ // prepareAction is declared in the shape (rather than checked only via
32445
+ // .passthrough()) so its value is read off the input by property access:
32446
+ // .passthrough() only copies an input's own enumerable keys into its
32447
+ // output, which drops prototype methods like a class's prepareAction.
32448
+ adapter: adapterSchema.extend({
32449
+ prepareAction: z.unknown()
32450
+ }).passthrough().refine((adapter)=>typeof adapter.prepareAction === 'function', 'adapter must expose prepareAction'),
32451
+ chain: chainIdentifierSchema,
32452
+ address: evmAddressSchema$1.optional()
32453
+ }).strict();
32454
+ /**
32455
+ * Shared `superRefine` enforcing the address/adapter-capability contract: a
32456
+ * developer-controlled adapter must supply `address` (the kit cannot resolve
32457
+ * it), and a user-controlled adapter must not (the adapter itself resolves
32458
+ * it — a caller-supplied value would silently take precedence, letting the
32459
+ * two disagree). Applied to every schema whose `from` shape carries an
32460
+ * adapter with a declared `addressContext` capability, so every Borrow Kit
32461
+ * write rejects a mismatched `from` the same way.
32462
+ *
32463
+ * @internal
32464
+ */ function refineAddressContext(from, context) {
32465
+ const addressContext = from.adapter.capabilities?.addressContext;
32466
+ if (addressContext === 'developer-controlled' && from.address === undefined) {
32467
+ context.addIssue({
32468
+ code: z.ZodIssueCode.custom,
32469
+ message: 'address is required for developer-controlled adapters',
32470
+ path: [
32471
+ 'address'
32472
+ ]
32473
+ });
32474
+ }
32475
+ if (addressContext === 'user-controlled' && from.address !== undefined) {
32476
+ context.addIssue({
32477
+ code: z.ZodIssueCode.custom,
32478
+ message: 'address must be resolved by user-controlled adapters',
32479
+ path: [
32480
+ 'address'
32481
+ ]
32482
+ });
32483
+ }
32484
+ }
32485
+ /** @internal */ const borrowExecutionFromSchema = z.object({
32486
+ // prepareAction/validateChainSupport/capabilities are declared in the
32487
+ // shape (rather than checked only via .passthrough()) so their values are
32488
+ // read off the input by property access: .passthrough() only copies an
32489
+ // input's own enumerable keys into its output, which drops a prototype
32490
+ // method or getter like a class's prepareAction/validateChainSupport/
32491
+ // capabilities. capabilities is read below and by refineAddressContext.
32492
+ adapter: adapterSchema.extend({
32493
+ prepareAction: z.unknown(),
32494
+ validateChainSupport: z.unknown(),
32495
+ capabilities: z.unknown()
32496
+ }).passthrough().refine((adapter)=>typeof adapter.prepareAction === 'function', 'adapter must expose prepareAction').refine((adapter)=>typeof adapter.validateChainSupport === 'function', 'adapter must expose validateChainSupport').refine((adapter)=>adapter.capabilities?.addressContext === 'user-controlled' || adapter.capabilities?.addressContext === 'developer-controlled', 'adapter must declare its addressContext capability'),
32497
+ chain: chainIdentifierSchema,
32498
+ address: evmAddressSchema$1.optional()
32499
+ }).strict().superRefine(refineAddressContext);
32500
+ /**
32501
+ * Runtime schema for {@link RepayQuoteParams}.
32502
+ */ const repayQuoteParamsSchema = z.object({
32503
+ loanId: z.string().uuid(),
32504
+ chain: chainIdentifierSchema,
32505
+ repayAmount: positiveAmountSchema,
32506
+ config: borrowServiceConfigSchema.optional()
32507
+ }).strict();
32508
+ /** Runtime schema for {@link RepayParams}. */ repayQuoteParamsSchema.omit({
32509
+ chain: true
32510
+ }).extend({
32511
+ idempotencyKey: idempotencyKeySchema.optional(),
32512
+ from: borrowExecutionFromSchema
32513
+ });
32514
+ /** Runtime schema for requesting a full-close quote. */ const closeLoanQuoteParamsSchema = z.object({
32515
+ loanId: z.string().uuid(),
32516
+ chain: chainIdentifierSchema,
32517
+ slippageBps: z.number().int().min(0).max(10_000).optional(),
32518
+ config: borrowServiceConfigSchema.optional()
32519
+ }).strict();
32520
+ /** Runtime schema for atomically closing a loan. */ closeLoanQuoteParamsSchema.omit({
32521
+ chain: true
32522
+ }).extend({
32523
+ idempotencyKey: idempotencyKeySchema.optional(),
32524
+ from: borrowExecutionFromSchema
32525
+ });
32526
+ /**
32527
+ * Runtime schema for {@link GetPositionParams}.
32528
+ *
32529
+ * @example
32530
+ * ```typescript
32531
+ * import { getPositionParamsSchema } from '@circle-fin/borrow-kit'
32532
+ *
32533
+ * const result = getPositionParamsSchema.safeParse({
32534
+ * loanId: '11111111-1111-4111-8111-111111111111',
32535
+ * })
32536
+ * ```
32537
+ */ z.object({
32538
+ loanId: z.string().uuid(),
32539
+ config: borrowServiceConfigSchema.optional()
32540
+ }).strict();
32541
+ /**
32542
+ * Schema for the adapter context within Borrow operations.
32543
+ *
32544
+ * `.strict()` so a typo nested inside `from` fails fast instead of being
32545
+ * silently stripped. The address field is optional (resolved from the
32546
+ * adapter at runtime).
32547
+ *
32548
+ * @internal
32549
+ */ const borrowAdapterContextSchema = z.object({
32550
+ // signTypedData/capabilities are declared in the shape (rather than
32551
+ // checked only via .passthrough()) so their values are read off the input
32552
+ // by property access: .passthrough() only copies an input's own
32553
+ // enumerable keys into its output, which drops a prototype method or
32554
+ // getter like a class's signTypedData/capabilities. capabilities is read
32555
+ // by refineAddressContext below.
32556
+ adapter: adapterSchema.extend({
32557
+ signTypedData: z.unknown(),
32558
+ capabilities: z.unknown()
32559
+ }).passthrough().refine(// supportsSignTypedData is optional on SignTypedDataAdapter (see
32560
+ // @core/adapter-evm's hasSignTypedData/canSignTypedData): only
32561
+ // signTypedData itself is required here.
32562
+ (adapter)=>typeof adapter.signTypedData === 'function', 'adapter must support signTypedData'),
32563
+ // BorrowAdapterContext accepts a wider chain field, but runtime
32564
+ // validation must stay constrained to chains where loans exist. No cast
32565
+ // needed here: inferring straight from borrowChainIdentifierSchema keeps
32566
+ // this schema's exported type accurate (only the Borrow chain set),
32567
+ // unlike the previous `as unknown as typeof chainIdentifierSchema` cast,
32568
+ // which over-widened it to the full ChainIdentifier union.
32569
+ chain: borrowChainIdentifierSchema,
32570
+ address: z.string().min(1, 'address must not be empty').optional()
32571
+ }).strict()// Same address/adapter-capability contract every other Borrow Kit write
32572
+ // enforces via borrowExecutionFromSchema: a developer-controlled adapter
32573
+ // without `address` fails fast with a named message instead of falling
32574
+ // through to adapter.getAddress(); a user-controlled adapter with `address`
32575
+ // set is rejected instead of silently taking precedence.
32576
+ .superRefine(refineAddressContext);
32577
+ /** @internal */ const borrowParamsBaseSchema = z.object({
32578
+ from: adapterContextSchema,
32579
+ borrowAmount: createPositiveAmountSchema('borrowAmount'),
32580
+ slippageBps: z.number().int().min(0).max(10_000).optional(),
32581
+ idempotencyKey: idempotencyKeySchema.optional(),
32582
+ config: borrowServiceConfigSchema.optional()
32583
+ });
32584
+ /**
32585
+ * Runtime schema for {@link BorrowParams}.
32586
+ *
32587
+ * One strict object with a refinement, rather than a union of a market branch
32588
+ * and a loan branch: a union that fails every branch reports one `invalid_union`
32589
+ * issue, which costs the caller the field that actually failed. The refinement
32590
+ * states the rule it enforces, and every other field keeps naming itself.
32591
+ */ borrowParamsBaseSchema.extend({
32592
+ marketId: z.string().min(1).optional(),
32593
+ loanId: z.string().uuid().optional()
32594
+ }).strict().refine((params)=>params.marketId === undefined !== (params.loanId === undefined), {
32595
+ message: 'Provide exactly one of marketId or loanId'
32596
+ });
32597
+ /**
32598
+ * Schema for the adapter context within reward-claiming operations.
32599
+ *
32600
+ * Validate that a properly-shaped adapter instance and a valid chain
32601
+ * identifier are provided. The address field is optional at the schema
32602
+ * level (resolved from the adapter at runtime for user-controlled
32603
+ * adapters, required for developer-controlled ones — that distinction is
32604
+ * enforced by {@link BorrowAdapterContext}'s conditional `AddressField`,
32605
+ * not by this runtime schema). `.strict()` so a typo nested inside `from`
32606
+ * fails fast instead of being silently stripped.
32607
+ *
32608
+ * Reward claiming is restricted to the Borrow chain identifiers (Arc and
32609
+ * Arc Testnet), which is narrower than the general
32610
+ * {@link chainIdentifierSchema} `repay`/`borrow` accept.
32611
+ *
32612
+ * @internal
32613
+ */ const claimRewardsAdapterContextSchema = z.object({
32614
+ adapter: adapterSchema,
32615
+ chain: borrowChainIdentifierSchema,
32616
+ address: evmAddressSchema$1.optional()
32617
+ }).strict()// Same address/adapter-capability contract every other Borrow Kit write
32618
+ // enforces via borrowExecutionFromSchema: a developer-controlled adapter
32619
+ // without `address` fails fast with a named message instead of falling
32620
+ // through to adapter.getAddress(); a user-controlled adapter with `address`
32621
+ // set is rejected instead of silently taking precedence.
32622
+ .superRefine(refineAddressContext);
32623
+ /** @internal */ const borrowPreviewParamsBaseSchema = z.object({
32624
+ chain: chainIdentifierSchema,
32625
+ marketId: z.string().min(1),
32626
+ config: borrowServiceConfigSchema.optional()
32627
+ });
32628
+ /** Runtime schema for a first-open borrow quote. @internal */ const openLoanQuoteParamsSchema = borrowPreviewParamsBaseSchema.extend({
32629
+ walletAddress: evmAddressSchema$1,
32630
+ borrowAmount: createPositiveAmountSchema('borrowAmount'),
32631
+ slippageBps: z.number().int().min(0).max(10_000).optional()
32632
+ }).strict();
32633
+ /** Runtime schema for an existing-loan borrow quote. @internal */ const borrowMoreQuoteParamsSchema = z.object({
32634
+ loanId: z.string().uuid(),
32635
+ borrowAmount: createPositiveAmountSchema('borrowAmount'),
32636
+ slippageBps: z.number().int().min(0).max(10_000).optional(),
32637
+ config: borrowServiceConfigSchema.optional()
32638
+ }).strict();
32639
+ /** Runtime schema for {@link BorrowQuoteParams}. */ z.union([
32640
+ openLoanQuoteParamsSchema,
32641
+ borrowMoreQuoteParamsSchema
32642
+ ]);
32643
+ /** Runtime schema for {@link RequiredCollateralParams}. */ borrowPreviewParamsBaseSchema.extend({
32644
+ borrowAmount: createPositiveAmountSchema('borrowAmount'),
32645
+ targetHealthFactor: z.number().positive().finite()
32646
+ }).strict();
32647
+ // Uncapped: repayments are USDC, but collateral is the market's own token and
32648
+ // may hold more precision than USDC's six places.
32649
+ /** @internal */ const collateralAmountSchema = createPositiveAmountSchema('collateralAmount', null);
32650
+ /**
32651
+ * Validation schema for ClaimRewardsParams.
32652
+ *
32653
+ * @example
32654
+ * ```typescript
32655
+ * import { claimRewardsParamsSchema } from '@circle-fin/borrow-kit'
32656
+ *
32657
+ * const result = claimRewardsParamsSchema.safeParse({
32658
+ * from: { adapter, chain: 'Arc_Testnet' },
32659
+ * })
32660
+ * ```
32661
+ */ z.object({
32662
+ from: claimRewardsAdapterContextSchema,
32663
+ config: borrowServiceConfigSchema.optional()
32664
+ }).strict();
32665
+ /**
32666
+ * Validation schema for GetClaimRewardsQuoteParams.
32667
+ *
32668
+ * @example
32669
+ * ```typescript
32670
+ * import { getClaimRewardsQuoteParamsSchema } from '@circle-fin/borrow-kit'
32671
+ *
32672
+ * const result = getClaimRewardsQuoteParamsSchema.safeParse({
32673
+ * from: { adapter, chain: 'Arc_Testnet' },
32674
+ * })
32675
+ * ```
32676
+ */ z.object({
32677
+ from: claimRewardsAdapterContextSchema,
32678
+ config: borrowServiceConfigSchema.optional()
32679
+ }).strict();
32680
+ /** @internal */ const marketSortBySchema = z.enum([
32681
+ 'lltv',
32682
+ 'borrowApy'
32683
+ ]);
32684
+ /**
32685
+ * Build a schema for an optional non-negative decimal filter string.
32686
+ *
32687
+ * Accepts values like `'0'`, `'100'`, `'0.86'`; rejects negatives, leading
32688
+ * dots, and non-numeric input so a malformed filter fails fast.
32689
+ *
32690
+ * @param field - The filter field name, used in the rejection message.
32691
+ * @returns A Zod string schema constrained to a non-negative decimal form.
32692
+ * @internal
32693
+ */ function decimalFilterSchema(field) {
32694
+ return z.string().regex(/^\d+(\.\d+)?$/, `${field} must be a non-negative decimal string`);
32695
+ }
32696
+ const exploreMarketsParamsBase = z.object({
32697
+ chain: chainIdentifierSchema,
32698
+ minLltv: decimalFilterSchema('minLltv').optional(),
32699
+ maxLltv: decimalFilterSchema('maxLltv').optional(),
32700
+ minBorrow: decimalFilterSchema('minBorrow').optional(),
32701
+ maxBorrow: decimalFilterSchema('maxBorrow').optional(),
32702
+ sortBy: marketSortBySchema.optional(),
32703
+ // Provisional bounds: the backend has not published a pageSize range, so
32704
+ // reject only implausible values rather than plausible ones.
32705
+ pageSize: z.number().int('pageSize must be an integer').min(1, 'pageSize must be at least 1').max(1000, 'pageSize must be at most 1000').optional(),
32706
+ pageAfter: z.string().min(1, 'pageAfter must not be empty').optional(),
32707
+ config: borrowServiceConfigSchema.optional()
32708
+ }).strict();
32709
+ /**
32710
+ * Runtime schema for {@link MaxBorrowParams}.
32711
+ *
32712
+ * The collateral amount is not held to the loan asset's six decimal places: it
32713
+ * denominates the market's collateral token, whose precision may be finer.
32714
+ */ borrowPreviewParamsBaseSchema.extend({
32715
+ collateralAmount: collateralAmountSchema
32716
+ }).strict();
32717
+ /** Runtime schema for {@link WithdrawCollateralQuoteParams}. */ const withdrawCollateralQuoteParamsSchema = z.object({
32718
+ loanId: z.string().uuid(),
32719
+ chain: chainIdentifierSchema,
32720
+ collateralAmount: collateralAmountSchema,
32721
+ slippageBps: z.number().int().min(0).max(10_000).optional(),
32722
+ config: borrowServiceConfigSchema.optional()
32723
+ }).strict();
32724
+ /**
32725
+ * Maximum accepted signature deadline: 300 seconds from now, matching the
32726
+ * Borrow Service's own hard limit on `WebhookRegistration` deadlines. A wider
32727
+ * SDK-side window would let a caller sign a deadline the service always
32728
+ * rejects.
32729
+ *
32730
+ * @internal
32731
+ */ const MAX_DEADLINE_SKEW_SECONDS = 300;
32732
+ /**
32733
+ * True when `value` parses as a URL whose scheme is exactly `https:`.
32734
+ *
32735
+ * Uses the WHATWG `URL` parser rather than `startsWith('https://')` so the
32736
+ * check is scheme-normalized (case-insensitive, e.g. `HTTPS://...` still
32737
+ * passes) instead of a literal prefix match. `value` has already passed
32738
+ * `z.string().url()` by the time this runs, but a caller could still supply
32739
+ * a string that `URL` itself rejects, so the parse is guarded.
32740
+ *
32741
+ * @internal
32742
+ */ function isHttpsUrl(value) {
32743
+ try {
32744
+ return new URL(value).protocol === 'https:';
32745
+ } catch {
32746
+ return false;
32747
+ }
32748
+ }
32749
+ /**
32750
+ * Validation schema for RegisterWebhookParams.
32751
+ *
32752
+ * @example
32753
+ * ```typescript
32754
+ * import { registerWebhookParamsSchema } from '@circle-fin/borrow-kit'
32755
+ *
32756
+ * const result = registerWebhookParamsSchema.safeParse({
32757
+ * from: { adapter, chain: 'Arc_Testnet' },
32758
+ * loanId: '11111111-1111-4111-8111-111111111111',
32759
+ * webhookUrl: 'https://example.com/webhook',
32760
+ * })
32761
+ * ```
32762
+ */ z.object({
32763
+ from: borrowAdapterContextSchema,
32764
+ // loanId must be a UUID, consistent with every other loan-scoped schema
32765
+ // in this package (repayQuoteParamsSchema, closeLoanQuoteParamsSchema,
32766
+ // borrowParamsSchema) and with the provider-level
32767
+ // registerWebhookInputSchema, which already enforces this.
32768
+ loanId: z.string().uuid(),
32769
+ // Restricted to https: for the same reason as the provider-level
32770
+ // registerWebhookInputSchema — this URL receives a cryptographically
32771
+ // signed, owner-authorized registration, and http:// would let that
32772
+ // signed intent point at a plaintext or link-local/loopback endpoint.
32773
+ webhookUrl: z.string().url('webhookUrl must be a valid URL').refine((value)=>isHttpsUrl(value), 'webhookUrl must use the https scheme').nullable().optional(),
32774
+ deadline: z.number().int('deadline must be an integer unix-seconds timestamp').refine((deadline)=>deadline > Math.floor(Date.now() / 1000), 'deadline must be in the future — a past deadline would sign a message the Borrow Service always rejects').refine((deadline)=>deadline <= Math.floor(Date.now() / 1000) + MAX_DEADLINE_SKEW_SECONDS, `deadline must be at most ${String(MAX_DEADLINE_SKEW_SECONDS)} seconds from now`).optional(),
32775
+ config: borrowServiceConfigSchema.optional()
32776
+ }).strict();
32777
+ /**
32778
+ * Reject a filter range whose lower bound exceeds its upper bound.
32779
+ *
32780
+ * Each bound is validated independently, so `{ minLltv: '0.9', maxLltv: '0.1' }`
32781
+ * would otherwise pass and return a silently empty page; comparing the bounds
32782
+ * numerically turns that contradiction into a clear validation error. Shared by
32783
+ * the single-page and iterator schemas so the two cannot drift.
32784
+ *
32785
+ * @param value - The parsed explore filters.
32786
+ * @param ctx - The Zod refinement context that collects any range issues.
32787
+ * @internal
32788
+ */ function refineFilterRanges(value, ctx) {
32789
+ if (value.minLltv !== undefined && value.maxLltv !== undefined && Number(value.minLltv) > Number(value.maxLltv)) {
32790
+ ctx.addIssue({
32791
+ code: z.ZodIssueCode.custom,
32792
+ path: [
32793
+ 'maxLltv'
32794
+ ],
32795
+ message: 'maxLltv must be greater than or equal to minLltv'
32796
+ });
32797
+ }
32798
+ if (value.minBorrow !== undefined && value.maxBorrow !== undefined && Number(value.minBorrow) > Number(value.maxBorrow)) {
32799
+ ctx.addIssue({
32800
+ code: z.ZodIssueCode.custom,
32801
+ path: [
32802
+ 'maxBorrow'
32803
+ ],
32804
+ message: 'maxBorrow must be greater than or equal to minBorrow'
32805
+ });
32806
+ }
32807
+ }
32808
+ /**
32809
+ * Runtime schema for {@link ExploreMarketsParams}.
32810
+ *
32811
+ * `chain` is required; every filter is optional. `pageAfter` is an opaque
32812
+ * non-empty cursor and `pageSize` a positive integer (provisional upper bound
32813
+ * 1000, pending backend confirmation). Unknown keys are rejected, and a range
32814
+ * whose minimum exceeds its maximum is rejected rather than returning a
32815
+ * silently empty page.
32816
+ *
32817
+ * @example
32818
+ * ```typescript
32819
+ * import { exploreMarketsParamsSchema } from '@circle-fin/borrow-kit'
32820
+ *
32821
+ * const result = exploreMarketsParamsSchema.safeParse({
32822
+ * chain: 'Arc_Testnet',
32823
+ * minLltv: '0.8',
32824
+ * sortBy: 'borrowApy',
32825
+ * })
32826
+ * ```
32827
+ */ exploreMarketsParamsBase.superRefine(refineFilterRanges);
32828
+ /**
32829
+ * Runtime schema for {@link ExploreMarketsIteratorParams}.
32830
+ *
32831
+ * Identical to {@link exploreMarketsParamsSchema} minus `pageAfter`: the
32832
+ * iterator owns the cursor and advances through pages itself.
32833
+ *
32834
+ * @example
32835
+ * ```typescript
32836
+ * import { exploreMarketsIteratorParamsSchema } from '@circle-fin/borrow-kit'
32837
+ *
32838
+ * const result = exploreMarketsIteratorParamsSchema.safeParse({
32839
+ * chain: 'Arc_Testnet',
32840
+ * sortBy: 'borrowApy',
32841
+ * })
32842
+ * ```
32843
+ */ exploreMarketsParamsBase.omit({
32844
+ pageAfter: true
32845
+ }).superRefine(refineFilterRanges);
32846
+ /** Runtime schema for {@link WithdrawCollateralParams}. */ withdrawCollateralQuoteParamsSchema.omit({
32847
+ chain: true
32848
+ }).extend({
32849
+ idempotencyKey: idempotencyKeySchema.optional(),
32850
+ from: borrowExecutionFromSchema
32851
+ });
32852
+ /** Runtime schema for {@link AddCollateralQuoteParams}. */ const addCollateralQuoteParamsSchema = z.object({
32853
+ loanId: z.string().uuid(),
32854
+ chain: chainIdentifierSchema,
32855
+ collateralAmount: collateralAmountSchema,
32856
+ config: borrowServiceConfigSchema.optional()
32857
+ }).strict();
32858
+ /** Runtime schema for {@link AddCollateralParams}. */ addCollateralQuoteParamsSchema.omit({
32859
+ chain: true
32860
+ }).extend({
32861
+ idempotencyKey: idempotencyKeySchema.optional(),
32862
+ from: borrowExecutionFromSchema
32863
+ });
32864
+
32865
+ /** SDK name used in telemetry payloads. */ resolveKitSdkName(pkg.name);
32866
+
32867
+ // Auto-register this kit for user agent tracking
32868
+ registerKit(`${pkg.name}/${pkg.version}`);
32869
+
31114
32870
  async function bridge(context, params) {
31115
32871
  const kit = createBridgeKit(context);
31116
32872
  registerActionHandlers(kit, context.actions.bridge, 'bridge');