@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.cjs CHANGED
@@ -1367,6 +1367,34 @@ let activeRedactor = defaultMessageRedactor;
1367
1367
  return redactErrorMessage(String(error));
1368
1368
  }
1369
1369
 
1370
+ /**
1371
+ * Render an object value for an error message without letting the rendering
1372
+ * itself throw.
1373
+ *
1374
+ * `JSON.stringify` rejects `BigInt` and circular structures, and a rejected
1375
+ * value is exactly the kind of thing a validation error is built from: an
1376
+ * on-chain read or a receipt log carries `bigint` fields, so serializing one
1377
+ * naively replaces the intended error with a `TypeError`.
1378
+ *
1379
+ * @param value - The value to render.
1380
+ * @returns A JSON string with `bigint` fields stringified, or
1381
+ * `'[unserializable object]'` if serialization still fails.
1382
+ * @throws Never; serialization failures are caught and rendered as a fallback string.
1383
+ * @example
1384
+ * ```typescript
1385
+ * import { describeObjectValue } from '@core/errors'
1386
+ *
1387
+ * describeObjectValue({ amount: 1n })
1388
+ * // '{"amount":"1"}'
1389
+ * ```
1390
+ */ function describeObjectValue(value) {
1391
+ try {
1392
+ return JSON.stringify(value, (_key, entry)=>typeof entry === 'bigint' ? entry.toString() : entry);
1393
+ } catch {
1394
+ return '[unserializable object]';
1395
+ }
1396
+ }
1397
+
1370
1398
  /**
1371
1399
  * Creates error for network type mismatch between source and destination.
1372
1400
  *
@@ -1629,7 +1657,7 @@ let activeRedactor = defaultMessageRedactor;
1629
1657
  * // Message: "Validation failed for 'chainId': 999 - Unsupported chain ID"
1630
1658
  *
1631
1659
  * throw createValidationFailedError('config', { invalid: true }, 'Missing required properties')
1632
- * // Message: "Validation failed for 'config': [object Object] - Missing required properties"
1660
+ * // Message: "Validation failed for 'config': {"invalid":true} - Missing required properties"
1633
1661
  * ```
1634
1662
  */ function createValidationFailedError$1(field, value, reason) {
1635
1663
  // Convert value to string for display, handling different types appropriately
@@ -1637,7 +1665,7 @@ let activeRedactor = defaultMessageRedactor;
1637
1665
  if (typeof value === 'string') {
1638
1666
  valueString = `'${value}'`;
1639
1667
  } else if (typeof value === 'object' && value !== null) {
1640
- valueString = JSON.stringify(value);
1668
+ valueString = describeObjectValue(value);
1641
1669
  } else {
1642
1670
  valueString = String(value);
1643
1671
  }
@@ -2494,22 +2522,73 @@ let activeRedactor = defaultMessageRedactor;
2494
2522
  '0x9147d463': 'Final balance is less than the initial balance',
2495
2523
  '0xf4b3b1bc': 'Native token transfer to the beneficiary failed'
2496
2524
  };
2525
+ /**
2526
+ * Matches revert data: a 4-byte selector followed by whole ABI-encoded bytes.
2527
+ */ const REVERT_DATA_PATTERN = /^0x[0-9a-f]{8}(?:[0-9a-f]{2})*$/i;
2528
+ /**
2529
+ * Returns the revert data held by one error in a `cause` chain.
2530
+ *
2531
+ * Browser wallets (EIP-1193) wrap the node's error, so the revert data sits
2532
+ * one level down as `data.data`. viem's `getContractError` reads it the same
2533
+ * way, then keeps it on `ContractFunctionRevertedError.raw` and drops the
2534
+ * node's error from the chain.
2535
+ *
2536
+ * @param link - One error in the `cause` chain
2537
+ * @returns The `raw` or `data` string, or undefined if there is none
2538
+ */ function getRevertData(link) {
2539
+ if ('raw' in link && typeof link.raw === 'string' && REVERT_DATA_PATTERN.test(link.raw)) {
2540
+ return link.raw;
2541
+ }
2542
+ if (!('data' in link)) {
2543
+ return undefined;
2544
+ }
2545
+ const { data } = link;
2546
+ if (typeof data === 'string') {
2547
+ return data;
2548
+ }
2549
+ if (data !== null && typeof data === 'object' && 'data' in data && typeof data.data === 'string') {
2550
+ return data.data;
2551
+ }
2552
+ return undefined;
2553
+ }
2554
+ /**
2555
+ * Finds the first revert data on the error or in its `cause` chain that
2556
+ * `accept` takes.
2557
+ *
2558
+ * viem keeps the node's revert data on an inner `RpcRequestError`, not on
2559
+ * the error it throws. The `seen` set stops a self-referencing `cause` chain.
2560
+ *
2561
+ * @param error - The error object
2562
+ * @param accept - Returns true for the data shape the caller wants
2563
+ * @returns The data as written on the error, or undefined if not found
2564
+ */ function findRevertData(error, accept) {
2565
+ const seen = new Set();
2566
+ let current = error;
2567
+ while(current !== null && typeof current === 'object' && !seen.has(current)){
2568
+ seen.add(current);
2569
+ const data = getRevertData(current);
2570
+ if (data !== undefined && accept(data)) {
2571
+ return data;
2572
+ }
2573
+ current = 'cause' in current ? current.cause : undefined;
2574
+ }
2575
+ return undefined;
2576
+ }
2497
2577
  /**
2498
2578
  * Attempts to extract an error selector from an error object or message.
2499
2579
  *
2500
2580
  * Checks multiple sources in priority order:
2501
- * 1. `error.data` field
2581
+ * 1. `raw` or `data` field (or nested `data.data`) on the error or anywhere
2582
+ * in its `cause` chain
2502
2583
  * 2. Message pattern: "custom error 0x8baa579f"
2503
2584
  *
2504
2585
  * @param msg - The error message
2505
2586
  * @param error - The error object (optional)
2506
2587
  * @returns The 4 bytes selector , or undefined if not found
2507
2588
  */ function extractErrorSelector(msg, error) {
2508
- // Check error.data field
2509
- if (error !== null && typeof error === 'object' && 'data' in error) {
2510
- if (typeof error.data === 'string' && /^0x[0-9a-f]{8}$/i.test(error.data)) {
2511
- return error.data.toLowerCase();
2512
- }
2589
+ const data = findRevertData(error, (value)=>/^0x[0-9a-f]{8}$/i.test(value));
2590
+ if (data !== undefined) {
2591
+ return data.toLowerCase();
2513
2592
  }
2514
2593
  // Check for "custom error 0x8baa579f" in the message
2515
2594
  const match = /custom error (0x[0-9a-f]{8})\b/i.exec(msg);
@@ -3135,6 +3214,105 @@ let activeRedactor = defaultMessageRedactor;
3135
3214
  return chain;
3136
3215
  }
3137
3216
 
3217
+ /**
3218
+ * Extracts the HTTP status code from an error message.
3219
+ *
3220
+ * Attempts to parse HTTP status codes from common error message formats,
3221
+ * such as "HTTP 404" or "Status: 500".
3222
+ *
3223
+ * Shared by `parseApiError` and the backend-error-code parsers that route on
3224
+ * status when no backend code is available, so all of them recognise the same
3225
+ * message shapes.
3226
+ *
3227
+ * @param msg - The error message to extract from
3228
+ * @returns The extracted HTTP status code, or null if not found
3229
+ *
3230
+ * @example
3231
+ * ```typescript
3232
+ * const code = extractHttpStatusCode('HTTP 404')
3233
+ * // Returns: 404
3234
+ * ```
3235
+ *
3236
+ * @example
3237
+ * ```typescript
3238
+ * const code = extractHttpStatusCode('Status: 500 Internal Server Error')
3239
+ * // Returns: 500
3240
+ * ```
3241
+ *
3242
+ * @internal
3243
+ */ function extractHttpStatusCode(msg) {
3244
+ // Pattern: "HTTP 404" or "HTTP 404 - some message" or "Status: 404"
3245
+ const patterns = [
3246
+ /HTTP (\d{3})/i,
3247
+ /Status:\s*(\d{3})/i,
3248
+ /^(\d{3}) -/
3249
+ ];
3250
+ for (const pattern of patterns){
3251
+ const match = pattern.exec(msg);
3252
+ const codeStr = match?.at(1);
3253
+ if (codeStr !== undefined) {
3254
+ const code = Number.parseInt(codeStr, 10);
3255
+ // Validate it's a valid HTTP status code
3256
+ if (code >= 100 && code < 600) {
3257
+ return code;
3258
+ }
3259
+ }
3260
+ }
3261
+ return null;
3262
+ }
3263
+
3264
+ /**
3265
+ * Type guard that narrows an unknown error to one carrying a non-null
3266
+ * object `responseBody` property (attached by `makeApiRequest`).
3267
+ *
3268
+ * Shared by backend-error-code parsers (`parseEarnApiError`,
3269
+ * `parseBorrowApiError`) so the narrowing logic lives in one place.
3270
+ *
3271
+ * @param error - The raw error from the HTTP layer
3272
+ * @returns `true` when `error.responseBody` is a non-null object
3273
+ * @internal
3274
+ */ function hasResponseBody(error) {
3275
+ return typeof error === 'object' && error !== null && 'responseBody' in error && typeof error['responseBody'] === 'object' && error['responseBody'] !== null;
3276
+ }
3277
+ /**
3278
+ * Narrow an unknown value to a non-null object.
3279
+ *
3280
+ * @param value - The value to check
3281
+ * @returns `true` when `value` is a non-null object
3282
+ * @internal
3283
+ */ function isRecord(value) {
3284
+ return typeof value === 'object' && value !== null;
3285
+ }
3286
+ /**
3287
+ * Read the machine-readable error fields (`code`, `message`,
3288
+ * `externalMessage`) that a Circle service attaches to an HTTP error body.
3289
+ *
3290
+ * `platform-common-go` serializes app-thrown errors under a nested envelope —
3291
+ * `{ response: { status: { code, message, externalMessage } } }` — for every
3292
+ * HTTP status, so this returns that inner `response.status` object when
3293
+ * present. It otherwise falls back to the top-level body so a flattened shape
3294
+ * (e.g. the stablecoin-kits-proxy) is still read. Callers read
3295
+ * `code` / `message` / `externalMessage` off the returned object uniformly.
3296
+ *
3297
+ * @param error - The raw error from the HTTP layer
3298
+ * @returns The error-status fields, or `undefined` when the error carries no
3299
+ * response body
3300
+ * @internal
3301
+ */ function extractResponseBody$1(error) {
3302
+ if (!hasResponseBody(error)) {
3303
+ return undefined;
3304
+ }
3305
+ const body = error.responseBody;
3306
+ const response = body['response'];
3307
+ if (isRecord(response)) {
3308
+ const status = response['status'];
3309
+ if (isRecord(status)) {
3310
+ return status;
3311
+ }
3312
+ }
3313
+ return body;
3314
+ }
3315
+
3138
3316
  /**
3139
3317
  * Proxy-specific structured error codes carried in `responseBody.code`.
3140
3318
  *
@@ -3435,29 +3613,22 @@ let activeRedactor = defaultMessageRedactor;
3435
3613
  });
3436
3614
  }
3437
3615
  /**
3438
- * Type guard that narrows an unknown error to one carrying a non-null
3439
- * object `responseBody` property (attached by `makeApiRequest`).
3616
+ * Extract the machine-readable error fields that `makeApiRequest` attaches to
3617
+ * HTTP error instances.
3440
3618
  *
3441
- * @param error - The raw error from the HTTP layer
3442
- * @returns `true` when `error.responseBody` is a non-null object
3443
- * @internal
3444
- */ function hasResponseBody(error) {
3445
- return typeof error === 'object' && error !== null && 'responseBody' in error && typeof error['responseBody'] === 'object' && error['responseBody'] !== null;
3446
- }
3447
- /**
3448
- * Extract the `responseBody` property that `makeApiRequest` attaches to
3449
- * HTTP error instances when the server returns a JSON body.
3619
+ * Delegates to the shared extractor so this fall-through path reads the same
3620
+ * shape as the backend-code parsers: it unwraps the nested
3621
+ * `{ response: { status } }` envelope when present, otherwise returns the
3622
+ * top-level body. Routing both halves through one extractor keeps an
3623
+ * unmapped-code error from losing the server's `externalMessage`.
3450
3624
  *
3451
3625
  * @param error - The raw error from the HTTP layer
3452
- * @returns The parsed body cast to {@link ApiErrorResponseBody}, or undefined
3626
+ * @returns The parsed body as {@link ApiErrorResponseBody}, or undefined
3453
3627
  * @throws Never. Returns `undefined` when the error does not carry a valid
3454
3628
  * `responseBody`.
3455
3629
  * @internal
3456
3630
  */ function extractResponseBody(error) {
3457
- if (!hasResponseBody(error)) {
3458
- return undefined;
3459
- }
3460
- return error.responseBody;
3631
+ return extractResponseBody$1(error);
3461
3632
  }
3462
3633
  /**
3463
3634
  * Extract a field name from an {@link ApiFieldError}.
@@ -3566,46 +3737,6 @@ let activeRedactor = defaultMessageRedactor;
3566
3737
  }
3567
3738
  return topMessage ?? fieldDetail;
3568
3739
  }
3569
- /**
3570
- * Extracts the HTTP status code from an error message.
3571
- *
3572
- * Attempts to parse HTTP status codes from common error message formats,
3573
- * such as "HTTP 404" or "Status: 500".
3574
- *
3575
- * @param msg - The error message to extract from
3576
- * @returns The extracted HTTP status code, or null if not found
3577
- *
3578
- * @example
3579
- * ```typescript
3580
- * const code = extractHttpStatusCode('HTTP 404')
3581
- * // Returns: 404
3582
- * ```
3583
- *
3584
- * @example
3585
- * ```typescript
3586
- * const code = extractHttpStatusCode('Status: 500 Internal Server Error')
3587
- * // Returns: 500
3588
- * ```
3589
- */ function extractHttpStatusCode(msg) {
3590
- // Pattern: "HTTP 404" or "HTTP 404 - some message" or "Status: 404"
3591
- const patterns = [
3592
- /HTTP (\d{3})/i,
3593
- /Status:\s*(\d{3})/i,
3594
- /^(\d{3}) -/
3595
- ];
3596
- for (const pattern of patterns){
3597
- const match = pattern.exec(msg);
3598
- const codeStr = match?.at(1);
3599
- if (codeStr !== undefined) {
3600
- const code = Number.parseInt(codeStr, 10);
3601
- // Validate it's a valid HTTP status code
3602
- if (code >= 100 && code < 600) {
3603
- return code;
3604
- }
3605
- }
3606
- }
3607
- return null;
3608
- }
3609
3740
 
3610
3741
  /**
3611
3742
  * @packageDocumentation
@@ -3730,6 +3861,8 @@ var SwapChain;
3730
3861
  SwapChain["HyperEVM"] = "HyperEVM";
3731
3862
  SwapChain["Monad"] = "Monad";
3732
3863
  SwapChain["Arc"] = "Arc";
3864
+ SwapChain["Cronos"] = "Cronos";
3865
+ SwapChain["Morph"] = "Morph";
3733
3866
  // Testnet chains with swap support
3734
3867
  SwapChain["Arc_Testnet"] = "Arc_Testnet";
3735
3868
  })(SwapChain || (SwapChain = {}));
@@ -3861,6 +3994,11 @@ var EarnChain;
3861
3994
  "Arc",
3862
3995
  "Arc_Testnet"
3863
3996
  ];
3997
+ var BorrowChain;
3998
+ (function(BorrowChain) {
3999
+ BorrowChain["Arc"] = "Arc";
4000
+ BorrowChain["Arc_Testnet"] = "Arc_Testnet";
4001
+ })(BorrowChain || (BorrowChain = {}));
3864
4002
 
3865
4003
  /**
3866
4004
  * Helper function to define a chain with proper TypeScript typing.
@@ -4203,6 +4341,29 @@ var EarnChain;
4203
4341
  * on EVM-compatible testnet chains. Use this address for testnet adapter
4204
4342
  * integrations (e.g., Arc Testnet).
4205
4343
  */ const ADAPTER_CONTRACT_EVM_TESTNET = '0xBBD70b01a1CAbc96d5b7b129Ae1AAabdf50dd40b';
4344
+ /**
4345
+ * The sender-preserving batching contract on Arc (`Multicall3From`).
4346
+ *
4347
+ * This predeploy batches calls like the ubiquitous Multicall3, but routes each
4348
+ * subcall through Arc's `CallFrom` precompile
4349
+ * (`0x1800000000000000000000000000000000000003`), which executes on behalf of
4350
+ * the transaction's original sender. Every target therefore sees the calling
4351
+ * EOA as `msg.sender`, which is what makes a plain, un-upgraded EOA capable of
4352
+ * an atomic batch on Arc.
4353
+ *
4354
+ * Deployed at the same address on Arc Testnet and Arc Mainnet.
4355
+ *
4356
+ * @see https://docs.arc.io/arc/concepts/batched-transactions
4357
+ */ const BATCHER_CONTRACT_ARC_TESTNET = '0x522fAf9A91c41c443c66765030741e4AaCe147D0';
4358
+ /**
4359
+ * The sender-preserving batching contract on Arc Mainnet (`Multicall3From`).
4360
+ *
4361
+ * See {@link BATCHER_CONTRACT_ARC_TESTNET} for how this contract works.
4362
+ * Requires Arc's Zero7 hardfork, which activates the `CallFrom` precompile
4363
+ * this contract depends on.
4364
+ *
4365
+ * @see https://docs.arc.io/arc/concepts/batched-transactions
4366
+ */ const BATCHER_CONTRACT_ARC_MAINNET = '0x522fAf9A91c41c443c66765030741e4AaCe147D0';
4206
4367
  /**
4207
4368
  * The GatewayWallet contract address for EVM mainnet networks.
4208
4369
  *
@@ -4342,7 +4503,10 @@ var EarnChain;
4342
4503
  },
4343
4504
  kitContracts: {
4344
4505
  bridge: BRIDGE_CONTRACT_EVM_MAINNET,
4345
- adapter: ADAPTER_CONTRACT_EVM_MAINNET
4506
+ adapter: ADAPTER_CONTRACT_EVM_MAINNET,
4507
+ // Arc is the only chain with a sender-preserving batcher, because it
4508
+ // takes a precompile rather than an ordinary contract to build one.
4509
+ senderPreservingBatcher: BATCHER_CONTRACT_ARC_MAINNET
4346
4510
  },
4347
4511
  gateway: {
4348
4512
  domain: 26,
@@ -4413,7 +4577,10 @@ var EarnChain;
4413
4577
  },
4414
4578
  kitContracts: {
4415
4579
  bridge: BRIDGE_CONTRACT_EVM_TESTNET,
4416
- adapter: ADAPTER_CONTRACT_EVM_TESTNET
4580
+ adapter: ADAPTER_CONTRACT_EVM_TESTNET,
4581
+ // Arc is the only chain with a sender-preserving batcher, because it
4582
+ // takes a precompile rather than an ordinary contract to build one.
4583
+ senderPreservingBatcher: BATCHER_CONTRACT_ARC_TESTNET
4417
4584
  },
4418
4585
  gateway: {
4419
4586
  domain: 26,
@@ -5028,7 +5195,8 @@ var EarnChain;
5028
5195
  }
5029
5196
  },
5030
5197
  kitContracts: {
5031
- bridge: BRIDGE_CONTRACT_EVM_MAINNET
5198
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
5199
+ adapter: ADAPTER_CONTRACT_EVM_MAINNET
5032
5200
  }
5033
5201
  });
5034
5202
 
@@ -5897,7 +6065,8 @@ var EarnChain;
5897
6065
  }
5898
6066
  },
5899
6067
  kitContracts: {
5900
- bridge: BRIDGE_CONTRACT_EVM_MAINNET
6068
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET,
6069
+ adapter: ADAPTER_CONTRACT_EVM_MAINNET
5901
6070
  }
5902
6071
  });
5903
6072
 
@@ -8083,7 +8252,8 @@ var Chains = {
8083
8252
  cctpx: zod.z.any().optional(),
8084
8253
  kitContracts: zod.z.object({
8085
8254
  bridge: zod.z.string().optional(),
8086
- adapter: zod.z.string().optional()
8255
+ adapter: zod.z.string().optional(),
8256
+ senderPreservingBatcher: zod.z.string().optional()
8087
8257
  }).optional(),
8088
8258
  gateway: gatewayConfigSchema.optional()
8089
8259
  });
@@ -8194,8 +8364,8 @@ var Chains = {
8194
8364
  * Zod schema for validating swap-specific chain identifiers.
8195
8365
  *
8196
8366
  * Validates chains based on:
8197
- * - CCTPv2 support (adapter contract deployed)
8198
- * - Mainnet only (no testnets)
8367
+ * - Membership in the SwapChain enum
8368
+ * - Mainnet or explicitly allowlisted testnet
8199
8369
  * - At least one supported token available
8200
8370
  *
8201
8371
  */ const swapChainIdentifierSchema = zod.z.union([
@@ -8279,6 +8449,38 @@ var Chains = {
8279
8449
  message: `"${chain.chain}" is not a supported earn chain. ` + `Supported chains: ${Object.values(EarnChain).join(', ')}`
8280
8450
  }))
8281
8451
  ]);
8452
+ /**
8453
+ * Zod schema for validating Borrow-specific chain identifiers.
8454
+ *
8455
+ * Validate that the provided chain is supported for Borrow Kit's loan
8456
+ * operations. Unlike {@link earnChainIdentifierSchema}, this includes Arc
8457
+ * mainnet as well as Arc Testnet.
8458
+ *
8459
+ * Accept a BorrowChain enum value, a matching string literal, or a
8460
+ * ChainDefinition for a supported chain.
8461
+ *
8462
+ * @example
8463
+ * ```typescript
8464
+ * import { borrowChainIdentifierSchema } from '@core/chains'
8465
+ * import { Arc, BorrowChain } from '@core/chains'
8466
+ *
8467
+ * // Valid
8468
+ * borrowChainIdentifierSchema.parse(BorrowChain.Arc)
8469
+ * borrowChainIdentifierSchema.parse('Arc')
8470
+ * borrowChainIdentifierSchema.parse(Arc)
8471
+ *
8472
+ * // Invalid (throws ZodError)
8473
+ * borrowChainIdentifierSchema.parse('Solana')
8474
+ * ```
8475
+ */ const borrowChainIdentifierSchema = zod.z.union([
8476
+ zod.z.string().refine((val)=>val in BorrowChain, (val)=>({
8477
+ message: `"${val}" is not a supported borrow chain. ` + `Supported chains: ${Object.values(BorrowChain).join(', ')}`
8478
+ })),
8479
+ zod.z.nativeEnum(BorrowChain),
8480
+ chainDefinitionSchema$2.refine((chain)=>chain.chain in BorrowChain, (chain)=>({
8481
+ message: `"${chain.chain}" is not a supported borrow chain. ` + `Supported chains: ${Object.values(BorrowChain).join(', ')}`
8482
+ }))
8483
+ ]);
8282
8484
  // Widened views so `.includes()` accepts arbitrary strings under strict TS.
8283
8485
  const EARN_BRIDGE_SOURCE_CHAIN_VALUES = EARN_BRIDGE_SOURCE_BLOCKCHAINS;
8284
8486
  const EARN_BRIDGE_DESTINATION_CHAIN_VALUES = EARN_BRIDGE_DESTINATION_BLOCKCHAINS;
@@ -8489,8 +8691,13 @@ const swapTokenEnumSchema = zod.z.enum([
8489
8691
  */ function resolveChainName(value) {
8490
8692
  if (value == null) return undefined;
8491
8693
  if (typeof value === 'string') return value;
8492
- if (typeof value === 'object' && 'chain' in value && typeof value.chain === 'string') {
8493
- return value.chain;
8694
+ try {
8695
+ if (typeof value === 'object' && 'chain' in value) {
8696
+ const chain = value.chain;
8697
+ if (typeof chain === 'string') return chain;
8698
+ }
8699
+ } catch {
8700
+ return undefined;
8494
8701
  }
8495
8702
  return undefined;
8496
8703
  }
@@ -8637,7 +8844,7 @@ const swapTokenEnumSchema = zod.z.enum([
8637
8844
  *
8638
8845
  * A chain supports swaps if ALL conditions are met:
8639
8846
  * 1. Is a member of the {@link SwapChain} enum
8640
- * 2. Has CCTPv2 support (adapter contract deployed)
8847
+ * 2. Has an Adapter contract configured directly or through CCTP v2
8641
8848
  *
8642
8849
  * Testnets are allowed only when explicitly listed in the
8643
8850
  * {@link SwapChain} enum (e.g., Arc_Testnet).
@@ -8658,14 +8865,14 @@ const swapTokenEnumSchema = zod.z.enum([
8658
8865
  * @remarks
8659
8866
  * Chains automatically become swap-enabled when:
8660
8867
  * - Added to the {@link SwapChain} enum
8661
- * - Adapter contract is deployed (CCTPv2)
8868
+ * - An Adapter contract is deployed and configured
8662
8869
  *
8663
8870
  * No code changes needed when new chains are added!
8664
8871
  */ function isSwapSupportedChain(chain) {
8665
8872
  if (!(chain.chain in SwapChain)) {
8666
8873
  return false;
8667
8874
  }
8668
- if (!isCCTPV2Supported(chain)) {
8875
+ if (!isCCTPV2Supported(chain) && !hasCustomContractSupport(chain, 'adapter')) {
8669
8876
  return false;
8670
8877
  }
8671
8878
  return true;
@@ -8676,7 +8883,7 @@ const swapTokenEnumSchema = zod.z.enum([
8676
8883
  *
8677
8884
  * Dynamically filter chain definitions to include only those that:
8678
8885
  * - Are members of the {@link SwapChain} enum
8679
- * - Have CCTPv2 support
8886
+ * - Have an Adapter contract configured directly or through CCTP v2
8680
8887
  *
8681
8888
  * This function is pure and accepts chains as parameter to avoid
8682
8889
  * circular dependencies.
@@ -9198,6 +9405,7 @@ const swapTokenEnumSchema = zod.z.enum([
9198
9405
  * - Retries on rate limiting (429)
9199
9406
  * - Does not retry on client errors (4xx, except 429)
9200
9407
  * - Does not retry on validation failures
9408
+ * - Does not retry when `config.isTerminalError` marks the error terminal
9201
9409
  *
9202
9410
  * Each HTTP request is given a configurable timeout before it is aborted.
9203
9411
  * Between retry attempts the wait depends on the `backoff` strategy. The
@@ -9277,7 +9485,10 @@ const swapTokenEnumSchema = zod.z.enum([
9277
9485
  return await makeApiRequest(url, method, isValidType, effectiveConfig, body);
9278
9486
  } catch (err) {
9279
9487
  const error = err instanceof Error ? err : new Error(String(err));
9280
- if (!isRetryableError(error)) {
9488
+ // A caller-supplied predicate can mark an error terminal (e.g. a
9489
+ // deterministic 404) so it is surfaced immediately instead of burning
9490
+ // the retry budget on a non-transient answer.
9491
+ if (effectiveConfig.isTerminalError?.(error) === true || !isRetryableError(error)) {
9281
9492
  throw error;
9282
9493
  }
9283
9494
  lastError = error;
@@ -9361,7 +9572,24 @@ const swapTokenEnumSchema = zod.z.enum([
9361
9572
  * // After this call, TypeScript knows params is of type BridgeParams
9362
9573
  * ```
9363
9574
  */ function validate(value, schema, context) {
9364
- const result = schema.safeParse(value);
9575
+ let result;
9576
+ try {
9577
+ result = schema.safeParse(value);
9578
+ } catch (error) {
9579
+ throw new KitError({
9580
+ ...InputError.VALIDATION_FAILED,
9581
+ recoverability: 'FATAL',
9582
+ message: `Invalid ${context}: reading a property threw`,
9583
+ cause: {
9584
+ trace: {
9585
+ validationErrors: [
9586
+ 'Reading a property of the value threw'
9587
+ ],
9588
+ originalError: error
9589
+ }
9590
+ }
9591
+ });
9592
+ }
9365
9593
  if (!result.success) {
9366
9594
  throw createValidationErrorFromZod(result.error, context);
9367
9595
  }
@@ -10089,11 +10317,13 @@ function parseOrThrow(value, schema, context) {
10089
10317
  [Blockchain.Arbitrum]: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9',
10090
10318
  [Blockchain.Avalanche]: '0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7',
10091
10319
  [Blockchain.Celo]: '0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e',
10320
+ [Blockchain.Cronos]: '0x66e428c3f67a68878562e79A0234c1F83c208770',
10092
10321
  [Blockchain.Ethereum]: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
10093
10322
  [Blockchain.HyperEVM]: '0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb',
10094
10323
  [Blockchain.Ink]: '0x0200C29006150606B650577BBE7B6248F58470c1',
10095
10324
  [Blockchain.Linea]: '0xA219439258ca9da29E9Cc4cE5596924745e12B93',
10096
10325
  [Blockchain.Monad]: '0xe7cd86e13AC4309349F30B3435a9d337750fC82D',
10326
+ [Blockchain.Morph]: '0xe7cd86e13AC4309349F30B3435a9d337750fC82D',
10097
10327
  [Blockchain.NEAR]: 'usdt.tether-token.near',
10098
10328
  [Blockchain.Optimism]: '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58',
10099
10329
  [Blockchain.Polkadot_Asset_Hub]: '1984',
@@ -10152,6 +10382,7 @@ function parseOrThrow(value, schema, context) {
10152
10382
  [Blockchain.Arbitrum]: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1',
10153
10383
  [Blockchain.Avalanche]: '0xd586E7F844cEa2F87f50152665BCbc2C279D8d70',
10154
10384
  [Blockchain.Base]: '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb',
10385
+ [Blockchain.Cronos]: '0xF2001B145b43032AAF5Ee2884e456CCd805F677D',
10155
10386
  [Blockchain.Ethereum]: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
10156
10387
  [Blockchain.Linea]: '0x4AF15ec2A0BD43Db75dd04E62FAA3B8EF36b00d5',
10157
10388
  [Blockchain.Optimism]: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1',
@@ -10214,7 +10445,9 @@ function parseOrThrow(value, schema, context) {
10214
10445
  [Blockchain.Arbitrum]: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
10215
10446
  [Blockchain.Avalanche]: '0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB',
10216
10447
  [Blockchain.Base]: '0x4200000000000000000000000000000000000006',
10448
+ [Blockchain.Cronos]: '0xe44Fd7fCb2b1581822D0c862B68222998a0c299a',
10217
10449
  [Blockchain.Ethereum]: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
10450
+ [Blockchain.Morph]: '0x5300000000000000000000000000000000000011',
10218
10451
  [Blockchain.Optimism]: '0x4200000000000000000000000000000000000006',
10219
10452
  [Blockchain.Polygon]: '0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619',
10220
10453
  [Blockchain.Solana]: '7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs'
@@ -10233,6 +10466,7 @@ function parseOrThrow(value, schema, context) {
10233
10466
  locators: {
10234
10467
  [Blockchain.Ethereum]: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599',
10235
10468
  [Blockchain.Arbitrum]: '0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f',
10469
+ [Blockchain.Cronos]: '0x062E66477Faf219F25D27dCED647BF57C3107d52',
10236
10470
  [Blockchain.Avalanche]: '0x50b7545627a5162F82A992c33b87aDc75187B218'
10237
10471
  }
10238
10472
  };
@@ -12062,11 +12296,11 @@ function resolveOptions(options) {
12062
12296
  void emitAnalyticsLog(buildPayload(config, stepEntry?.[1] ?? fallbackEventType, errorDetails, context));
12063
12297
  }
12064
12298
 
12065
- var name$2 = "@circle-fin/bridge-kit";
12066
- var version$3 = "1.15.1";
12067
- var pkg$3 = {
12068
- name: name$2,
12069
- version: version$3};
12299
+ var name$3 = "@circle-fin/bridge-kit";
12300
+ var version$4 = "1.15.3";
12301
+ var pkg$4 = {
12302
+ name: name$3,
12303
+ version: version$4};
12070
12304
 
12071
12305
  const assertCustomFeePolicySymbol = Symbol('assertCustomFeePolicy');
12072
12306
  /**
@@ -12635,6 +12869,12 @@ const READ_ACTION_KEY_SET = new Set(READ_ACTION_KEYS);
12635
12869
  /** EIP-2612 standard permit */ PermitType[PermitType["EIP2612"] = 1] = "EIP2612";
12636
12870
  })(PermitType || (PermitType = {}));
12637
12871
 
12872
+ var BorrowPermitType;
12873
+ (function(BorrowPermitType) {
12874
+ /** Tokens must be approved separately. */ BorrowPermitType[BorrowPermitType["NONE"] = 0] = "NONE";
12875
+ /** EIP-2612 permit calldata accompanies the token input. */ BorrowPermitType[BorrowPermitType["EIP2612"] = 1] = "EIP2612";
12876
+ })(BorrowPermitType || (BorrowPermitType = {}));
12877
+
12638
12878
  /**
12639
12879
  * Abstract base class for bridging providers that implement cross-chain bridging protocols.
12640
12880
  *
@@ -14622,7 +14862,11 @@ function createNoopLogger() {
14622
14862
  tokenId: bytes32Hex,
14623
14863
  domainId: nonNegativeInteger,
14624
14864
  tokenAddress: zod.z.string(),
14625
- tokenManagerAddress: zod.z.string()
14865
+ tokenManagerAddress: zod.z.string(),
14866
+ // Operator-controlled per-chain kill switch (CCTPE-3979). Optional for
14867
+ // back-compat with older IRIS versions that do not surface it. Downstream
14868
+ // consumers in `findCCTPXDeployment` treat a missing value as `true`.
14869
+ enabled: zod.z.boolean().optional()
14626
14870
  });
14627
14871
  /**
14628
14872
  * Schema for one token entry in the token registry response.
@@ -15106,17 +15350,23 @@ function createNoopLogger() {
15106
15350
  /**
15107
15351
  * Find a token's deployment on the given CCTP domain.
15108
15352
  *
15353
+ * A deployment row with `enabled === false` is treated as missing: an IRIS
15354
+ * operator has flipped the per-chain kill switch (CCTPE-3979), so the SDK must
15355
+ * not route through it even though the row still exists in the registry. A
15356
+ * missing or `true` `enabled` value remains routable — the field is optional on
15357
+ * older IRIS versions and absence is read as "allowed".
15358
+ *
15109
15359
  * @param token - The registry token whose deployments are searched.
15110
15360
  * @param domain - The CCTP domain id to match.
15111
15361
  * @returns The matching {@link CCTPXTokenDeployment}, or `undefined` when the
15112
- * token has no deployment on that domain.
15362
+ * token has no deployment on that domain or the deployment is disabled.
15113
15363
  *
15114
15364
  * @example
15115
15365
  * ```typescript
15116
15366
  * const deployment = findCCTPXDeployment(token, 0)
15117
15367
  * ```
15118
15368
  */ function findCCTPXDeployment(token, domain) {
15119
- return token.deployments.find((deployment)=>deployment.domainId === domain);
15369
+ return token.deployments.find((deployment)=>deployment.domainId === domain && deployment.enabled !== false);
15120
15370
  }
15121
15371
 
15122
15372
  /**
@@ -15218,6 +15468,24 @@ function createNoopLogger() {
15218
15468
  }
15219
15469
  }
15220
15470
  })
15471
+ ],
15472
+ // The token is not deployed on one of the route chains, or an operator has
15473
+ // flipped the per-chain `enabled` kill switch on the IRIS deployment row.
15474
+ // Either way the route is permanently unavailable until the operator intervenes;
15475
+ // a retry at the same source/destination will keep returning this error.
15476
+ [
15477
+ 'TOKEN_NOT_SUPPORTED_ON_ROUTE',
15478
+ ({ sourceDomain, destinationDomain })=>new KitError({
15479
+ ...InputError.UNSUPPORTED_ROUTE,
15480
+ recoverability: 'FATAL',
15481
+ message: 'CCTPx fee quote rejected the route: the token is not supported on ' + 'the source or destination chain (missing deployment or operator-disabled).',
15482
+ cause: {
15483
+ trace: {
15484
+ sourceDomain,
15485
+ destinationDomain
15486
+ }
15487
+ }
15488
+ })
15221
15489
  ]
15222
15490
  ]);
15223
15491
  /**
@@ -19758,7 +20026,7 @@ const defaultSleep = async (ms)=>new Promise((resolve)=>setTimeout(resolve, ms))
19758
20026
  * Must always contain both adapter and chain explicitly.
19759
20027
  *
19760
20028
  * Optionally includes address for developer-controlled adapters.
19761
- */ const adapterContextSchema$3 = zod.z.object({
20029
+ */ const adapterContextSchema$4 = zod.z.object({
19762
20030
  adapter: adapterSchema,
19763
20031
  chain: bridgeChainIdentifierSchema,
19764
20032
  address: zod.z.string().optional()
@@ -19767,14 +20035,14 @@ const defaultSleep = async (ms)=>new Promise((resolve)=>setTimeout(resolve, ms))
19767
20035
  * Schema for validating BridgeDestinationWithAddress objects.
19768
20036
  * Contains an explicit recipientAddress along with adapter and chain.
19769
20037
  * The address format is validated based on the chain type (EVM or Solana).
19770
- */ const bridgeDestinationWithAddressSchema = adapterContextSchema$3.extend({
20038
+ */ const bridgeDestinationWithAddressSchema = adapterContextSchema$4.extend({
19771
20039
  recipientAddress: zod.z.string().min(1, 'Recipient address is required'),
19772
20040
  useForwarder: zod.z.boolean().optional()
19773
20041
  }).superRefine(createRecipientAddressValidator());
19774
20042
  /**
19775
20043
  * Schema for validating AdapterContext with optional useForwarder.
19776
20044
  * Extends adapterContextSchema with the useForwarder flag.
19777
- */ const adapterContextWithForwarderSchema = adapterContextSchema$3.extend({
20045
+ */ const adapterContextWithForwarderSchema = adapterContextSchema$4.extend({
19778
20046
  useForwarder: zod.z.boolean().optional()
19779
20047
  });
19780
20048
  /**
@@ -19849,7 +20117,7 @@ const defaultSleep = async (ms)=>new Promise((resolve)=>setTimeout(resolve, ms))
19849
20117
  * }
19850
20118
  * ```
19851
20119
  */ const bridgeParamsWithChainIdentifierSchema = zod.z.object({
19852
- from: adapterContextSchema$3.strict(),
20120
+ from: adapterContextSchema$4.strict(),
19853
20121
  to: bridgeDestinationSchema,
19854
20122
  amount: zod.z.string().min(1, 'Required').pipe(createDecimalStringValidator({
19855
20123
  allowZero: false,
@@ -20009,7 +20277,7 @@ const defaultSleep = async (ms)=>new Promise((resolve)=>setTimeout(resolve, ms))
20009
20277
  const bridgeKitCaller = {
20010
20278
  type: 'kit',
20011
20279
  name: 'BridgeKit',
20012
- version: pkg$3.version
20280
+ version: pkg$4.version
20013
20281
  };
20014
20282
  // Create default runtime and tokens for invocation context resolution
20015
20283
  const defaults = {
@@ -23228,16 +23496,16 @@ const mockAttestationMessage = {
23228
23496
  return step;
23229
23497
  }
23230
23498
 
23231
- var version$2 = "1.14.0";
23232
- var pkg$2 = {
23233
- version: version$2};
23499
+ var version$3 = "1.14.2";
23500
+ var pkg$3 = {
23501
+ version: version$3};
23234
23502
 
23235
23503
  /**
23236
23504
  * Provider caller component for bridge operations.
23237
23505
  */ const BRIDGE_CALLER = {
23238
23506
  type: 'provider',
23239
23507
  name: 'CCTPV2BridgingProvider.bridge',
23240
- version: pkg$2.version
23508
+ version: pkg$3.version
23241
23509
  };
23242
23510
  /**
23243
23511
  * Resolve invocation context for bridge operations.
@@ -23610,7 +23878,7 @@ var pkg$2 = {
23610
23878
  */ const RETRY_CALLER = {
23611
23879
  type: 'provider',
23612
23880
  name: 'CCTPV2BridgingProvider.retry',
23613
- version: pkg$2.version
23881
+ version: pkg$3.version
23614
23882
  };
23615
23883
  /**
23616
23884
  * Resolve invocation context for retry operations.
@@ -25537,7 +25805,7 @@ function assertCCTPV2Config(config) {
25537
25805
  * Decimal string in token minor units, constrained to be strictly positive.
25538
25806
  *
25539
25807
  * @internal
25540
- */ const positiveAmountSchema = zod.z.string().regex(/^\d+$/, 'must be a non-negative integer string')// Re-check the digit shape here: zod still runs this refinement when the
25808
+ */ const positiveAmountSchema$1 = zod.z.string().regex(/^\d+$/, 'must be a non-negative integer string')// Re-check the digit shape here: zod still runs this refinement when the
25541
25809
  // regex check above fails ("dirty"), so guard BigInt() against throwing on a
25542
25810
  // non-numeric value before comparing.
25543
25811
  .refine((value)=>/^\d+$/.test(value) && BigInt(value) > 0n, 'must be greater than zero');
@@ -25617,7 +25885,7 @@ const preFinalityRequestSchema = zod.z.object({
25617
25885
  */ const fetchFeeQuoteInputSchema = zod.z.object({
25618
25886
  sourceDomain: zod.z.number().int().nonnegative(),
25619
25887
  destinationDomain: zod.z.number().int().nonnegative(),
25620
- amount: positiveAmountSchema,
25888
+ amount: positiveAmountSchema$1,
25621
25889
  feeToken: evmAddressSchema.optional(),
25622
25890
  requests: feeQuoteRequestsSchema,
25623
25891
  isTestnet: zod.z.boolean(),
@@ -26494,7 +26762,7 @@ async function resolveExecutionQuote(binding, suppliedQuote, refundAddress) {
26494
26762
  * The ERC-20 `decimals` field is a `uint8`, so a provider reporting more than
26495
26763
  * this is returning something that cannot describe a real token.
26496
26764
  */ const MAX_TOKEN_DECIMALS = 255;
26497
- /** SDK name used in telemetry payloads. */ const SDK_NAME = resolveKitSdkName(pkg$3.name);
26765
+ /** SDK name used in telemetry payloads. */ const SDK_NAME = resolveKitSdkName(pkg$4.name);
26498
26766
  /**
26499
26767
  * Pick the most-relevant `txHash` to attach to an error telemetry payload.
26500
26768
  *
@@ -26535,7 +26803,7 @@ async function resolveExecutionQuote(binding, suppliedQuote, refundAddress) {
26535
26803
  */ const BRIDGE_KIT_CALLER = {
26536
26804
  type: 'kit',
26537
26805
  name: 'BridgeKit',
26538
- version: pkg$3.version
26806
+ version: pkg$4.version
26539
26807
  };
26540
26808
  /**
26541
26809
  * Merge BridgeKit's caller into the invocation metadata for retry operations.
@@ -26683,7 +26951,7 @@ async function resolveExecutionQuote(binding, suppliedQuote, refundAddress) {
26683
26951
  this.disableErrorReporting = config.disableErrorReporting === true;
26684
26952
  this.telemetryConfig = {
26685
26953
  sdkName: SDK_NAME,
26686
- sdkVersion: pkg$3.version,
26954
+ sdkVersion: pkg$4.version,
26687
26955
  disabled: this.disableErrorReporting
26688
26956
  };
26689
26957
  for (const provider of this.providers){
@@ -27407,7 +27675,7 @@ async function resolveExecutionQuote(binding, suppliedQuote, refundAddress) {
27407
27675
  }
27408
27676
 
27409
27677
  // Auto-register this kit for user agent tracking
27410
- registerKit(`${pkg$3.name}/${pkg$3.version}`);
27678
+ registerKit(`${pkg$4.name}/${pkg$4.version}`);
27411
27679
 
27412
27680
  /**
27413
27681
  * Create a BridgeKit instance with optional developer fee configuration.
@@ -27478,11 +27746,11 @@ registerKit(`${pkg$3.name}/${pkg$3.version}`);
27478
27746
  return kit;
27479
27747
  };
27480
27748
 
27481
- var name$1 = "@circle-fin/swap-kit";
27482
- var version$1 = "1.7.0";
27483
- var pkg$1 = {
27484
- name: name$1,
27485
- version: version$1};
27749
+ var name$2 = "@circle-fin/swap-kit";
27750
+ var version$2 = "1.8.0";
27751
+ var pkg$2 = {
27752
+ name: name$2,
27753
+ version: version$2};
27486
27754
 
27487
27755
  const chainIdentifierField = zod.z.custom((value)=>chainIdentifierSchema.safeParse(value).success, {
27488
27756
  message: 'chain must be a valid chain identifier'
@@ -27563,7 +27831,7 @@ const optionalChainIdentifierField = chainIdentifierField.optional();
27563
27831
  *
27564
27832
  * @remarks
27565
27833
  * Optionally includes address for developer-controlled adapters.
27566
- */ const adapterContextSchema$2 = zod.z.object({
27834
+ */ const adapterContextSchema$3 = zod.z.object({
27567
27835
  adapter: adapterSchema,
27568
27836
  chain: chainIdentifierField,
27569
27837
  address: zod.z.string().optional()
@@ -27584,7 +27852,7 @@ const optionalChainIdentifierField = chainIdentifierField.optional();
27584
27852
  * }
27585
27853
  * ```
27586
27854
  */ zod.z.object({
27587
- from: adapterContextSchema$2,
27855
+ from: adapterContextSchema$3,
27588
27856
  tokenIn: zod.z.string({
27589
27857
  required_error: 'tokenIn is required',
27590
27858
  invalid_type_error: 'tokenIn must be a string'
@@ -27822,25 +28090,83 @@ const optionalChainIdentifierField = chainIdentifierField.optional();
27822
28090
  message: 'fee amount must be non-negative (>= 0)'
27823
28091
  });
27824
28092
  /**
27825
- * Zod schema for validating API keys.
27826
- *
27827
- * Accepts a Circle platform API key (`<ENV>_API_KEY:<keyId>:<keySecret>`) and
27828
- * the legacy kit key (`KIT_KEY:<keyId>:<keySecret>`).
27829
- *
27830
- * @example
27831
- * ```typescript
27832
- * import { apiKeySchema } from '@core/service-client'
27833
- *
27834
- * const result = apiKeySchema.safeParse('TEST_API_KEY:e84d2546d4e321b2ff427dc988c89503:f84d2548d4e322b2ff427fc989c87503')
27835
- * if (!result.success) {
27836
- * console.error('Invalid API key format:', result.error.issues)
27837
- * }
27838
- * ```
27839
- */ const apiKeySchema = zod.z.string({
27840
- required_error: 'API key is required',
27841
- invalid_type_error: 'Invalid API key format'
27842
- }).regex(API_KEY_PATTERN, 'Invalid API key format');
27843
- /**
28093
+ * Parses a display-only field leniently: a value `schema` rejects is replaced
28094
+ * with `fallback` instead of failing the response that carries it.
28095
+ * Implemented with `preprocess` rather than Zod's `.catch()` because static
28096
+ * analysis misreads `.catch` on the schema chain as an unhandled Promise
28097
+ * (S7785).
28098
+ */ const withFallback = (schema, fallback)=>zod.z.preprocess((value)=>schema.safeParse(value).success ? value : fallback, schema);
28099
+ const swapRouteTokenSchema = zod.z.object({
28100
+ symbol: zod.z.string(),
28101
+ address: zod.z.string(),
28102
+ decimals: zod.z.number()
28103
+ });
28104
+ const swapRouteMetadataSchema = zod.z.record(zod.z.unknown()).optional();
28105
+ const swapRouteStepSchema = zod.z.object({
28106
+ type: zod.z.string(),
28107
+ tool: zod.z.string(),
28108
+ toolName: zod.z.string(),
28109
+ fromToken: swapRouteTokenSchema,
28110
+ toToken: swapRouteTokenSchema,
28111
+ estimate: zod.z.object({
28112
+ fromAmount: zod.z.string(),
28113
+ toAmount: zod.z.string(),
28114
+ gasCostUSD: zod.z.string().optional()
28115
+ }),
28116
+ metadata: swapRouteMetadataSchema
28117
+ });
28118
+ /**
28119
+ * Strict schema for a `SwapRoute`. Module-private: service responses
28120
+ * parse routes leniently through {@link responseSwapRouteSchema}.
28121
+ */ const swapRouteSchema = zod.z.object({
28122
+ provider: zod.z.string().min(1, 'provider must be a non-empty string'),
28123
+ steps: zod.z.array(swapRouteStepSchema),
28124
+ metadata: swapRouteMetadataSchema
28125
+ });
28126
+ /**
28127
+ * Route details are display-only, so service responses parse them leniently:
28128
+ * steps or metadata in a shape the SDK does not know are dropped rather than
28129
+ * failing the quote or swap they describe. Steps are all-or-nothing: if any
28130
+ * step is malformed, `steps` is empty, so a route never silently loses a hop.
28131
+ */ const responseSwapRouteSchema = swapRouteSchema.extend({
28132
+ steps: withFallback(swapRouteSchema.shape.steps, []),
28133
+ metadata: withFallback(swapRouteMetadataSchema, undefined)
28134
+ });
28135
+ const createSwapFeeItemSchema = zod.z.object({
28136
+ token: zod.z.string({
28137
+ required_error: 'fee token is required',
28138
+ invalid_type_error: 'fee token must be a string'
28139
+ }).min(1, 'fee token must be a non-empty string'),
28140
+ amount: feeAmountSchema,
28141
+ decimals: zod.z.number().int('fee token decimals must be an integer').nonnegative('fee token decimals must be a non-negative integer').optional(),
28142
+ symbol: zod.z.string({
28143
+ invalid_type_error: 'fee token symbol must be a string'
28144
+ }).min(1, 'fee token symbol must be a non-empty string').optional(),
28145
+ // Descriptive metadata: a value the SDK cannot use is dropped rather than
28146
+ // failing the response that carries the fee.
28147
+ type: withFallback(zod.z.string().min(1).optional(), undefined),
28148
+ chain: withFallback(zod.z.string().min(1).optional(), undefined)
28149
+ });
28150
+ /**
28151
+ * Zod schema for validating API keys.
28152
+ *
28153
+ * Accepts a Circle platform API key (`<ENV>_API_KEY:<keyId>:<keySecret>`) and
28154
+ * the legacy kit key (`KIT_KEY:<keyId>:<keySecret>`).
28155
+ *
28156
+ * @example
28157
+ * ```typescript
28158
+ * import { apiKeySchema } from '@core/service-client'
28159
+ *
28160
+ * const result = apiKeySchema.safeParse('TEST_API_KEY:e84d2546d4e321b2ff427dc988c89503:f84d2548d4e322b2ff427fc989c87503')
28161
+ * if (!result.success) {
28162
+ * console.error('Invalid API key format:', result.error.issues)
28163
+ * }
28164
+ * ```
28165
+ */ const apiKeySchema = zod.z.string({
28166
+ required_error: 'API key is required',
28167
+ invalid_type_error: 'Invalid API key format'
28168
+ }).regex(API_KEY_PATTERN, 'Invalid API key format');
28169
+ /**
27844
28170
  * Zod schema for platform fees configuration.
27845
28171
  *
27846
28172
  * Validates the fees object for swap quotes.
@@ -27906,10 +28232,7 @@ const optionalChainIdentifierField = chainIdentifierField.optional();
27906
28232
  /** Quote information with estimated amounts and route */ quote: zod.z.object({
27907
28233
  /** Estimated output amount (base units) */ estimatedAmount: zod.z.string().min(1, 'estimatedAmount must be a non-empty string').regex(/^\d+$/, 'estimatedAmount must be a numeric string'),
27908
28234
  /** Guaranteed minimum output amount (base units) */ minAmount: zod.z.string().min(1, 'minAmount must be a non-empty string').regex(/^\d+$/, 'minAmount must be a numeric string'),
27909
- /** Route information from DEX aggregator */ route: zod.z.object({
27910
- provider: zod.z.string().min(1, 'provider must be a non-empty string'),
27911
- steps: zod.z.array(zod.z.unknown())
27912
- })
28235
+ /** Route information from DEX aggregator */ route: responseSwapRouteSchema
27913
28236
  }),
27914
28237
  /** Fee context indicating where fees should be collected */ feeContext: quoteFeeContextSchema
27915
28238
  });
@@ -28125,19 +28448,6 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
28125
28448
  }).min(1, 'toChain must be a non-empty string if provided').optional(),
28126
28449
  apiKey: apiKeySchema.optional()
28127
28450
  });
28128
- /**
28129
- * Zod schema for validating CreateSwapResponse payloads.
28130
- */ const createSwapFeeItemSchema = zod.z.object({
28131
- token: zod.z.string({
28132
- required_error: 'fee token is required',
28133
- invalid_type_error: 'fee token must be a string'
28134
- }).min(1, 'fee token must be a non-empty string'),
28135
- amount: feeAmountSchema,
28136
- decimals: zod.z.number().int('fee token decimals must be an integer').nonnegative('fee token decimals must be a non-negative integer').optional(),
28137
- symbol: zod.z.string({
28138
- invalid_type_error: 'fee token symbol must be a string'
28139
- }).min(1, 'fee token symbol must be a non-empty string').optional()
28140
- });
28141
28451
  /**
28142
28452
  * Developer fee item schema with basis field.
28143
28453
  */ const createSwapDeveloperFeeItemSchema = createSwapFeeItemSchema.extend({
@@ -28275,6 +28585,8 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
28275
28585
  correlationId: zod.z.preprocess((value)=>zod.z.string().uuid().safeParse(value).success ? value : undefined, zod.z.string().optional()),
28276
28586
  config: createSwapRequestBaseSchema.shape.config.optional(),
28277
28587
  fees: createSwapFeesSchema.optional(),
28588
+ // A route the SDK cannot parse is dropped instead of failing the swap.
28589
+ route: withFallback(responseSwapRouteSchema.optional(), undefined),
28278
28590
  transaction: createSwapTransactionSchema
28279
28591
  });
28280
28592
 
@@ -28312,6 +28624,146 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
28312
28624
  return normalize(source.apiKey) ?? normalize(source.kitKey);
28313
28625
  };
28314
28626
 
28627
+ function isBigIntLike$1(value) {
28628
+ try {
28629
+ BigInt(value);
28630
+ return true;
28631
+ } catch {
28632
+ return false;
28633
+ }
28634
+ }
28635
+ function isNonNegativeBigIntLike$3(value) {
28636
+ try {
28637
+ return BigInt(value) >= 0n;
28638
+ } catch {
28639
+ return false;
28640
+ }
28641
+ }
28642
+ /**
28643
+ * A `0x`-prefixed, even-length hex byte string (`'0x'` for empty calldata).
28644
+ *
28645
+ * @internal
28646
+ */ const hexBytesSchema$3 = zod.z.union([
28647
+ hexStringSchema,
28648
+ zod.z.literal('0x')
28649
+ ]).refine((value)=>value.length % 2 === 0, {
28650
+ message: 'value must be a 0x-prefixed even-length hex string'
28651
+ });
28652
+ /**
28653
+ * A non-negative uint256, accepted as a decimal string or a `bigint`.
28654
+ *
28655
+ * @internal
28656
+ */ const uint256LikeSchema$3 = zod.z.union([
28657
+ zod.z.string(),
28658
+ zod.z.bigint()
28659
+ ]).refine(isNonNegativeBigIntLike$3, {
28660
+ message: 'value must be a non-negative bigint-compatible string or bigint'
28661
+ });
28662
+ /**
28663
+ * A typed base-unit amount object as emitted by the API.
28664
+ *
28665
+ * @internal
28666
+ */ const amountJsonSchema$2 = zod.z.object({
28667
+ raw: zod.z.string().refine(isBigIntLike$1, {
28668
+ message: 'raw must be a bigint-compatible string'
28669
+ }),
28670
+ decimals: zod.z.number().int().nonnegative()
28671
+ });
28672
+ /**
28673
+ * One instruction in a signed claim-rewards execution payload.
28674
+ *
28675
+ * `.passthrough()` so a backend that adds a field does not fail validation.
28676
+ *
28677
+ * @internal
28678
+ */ const claimRewardsInstructionSchema = zod.z.object({
28679
+ target: evmAddressSchema$1,
28680
+ data: hexBytesSchema$3,
28681
+ value: uint256LikeSchema$3,
28682
+ tokenIn: evmAddressSchema$1,
28683
+ amountToApprove: uint256LikeSchema$3,
28684
+ tokenOut: evmAddressSchema$1,
28685
+ minTokenOut: uint256LikeSchema$3
28686
+ }).passthrough();
28687
+ const claimRewardsTokenSchema = zod.z.object({
28688
+ token: evmAddressSchema$1,
28689
+ beneficiary: evmAddressSchema$1
28690
+ }).passthrough();
28691
+ /**
28692
+ * The signed execution payload the `earn.claimRewards` Adapter action takes.
28693
+ *
28694
+ * @internal
28695
+ */ const claimRewardsExecutionParamsSchema = zod.z.object({
28696
+ instructions: zod.z.tuple([
28697
+ claimRewardsInstructionSchema
28698
+ ]).rest(claimRewardsInstructionSchema),
28699
+ tokens: zod.z.tuple([
28700
+ claimRewardsTokenSchema
28701
+ ]).rest(claimRewardsTokenSchema),
28702
+ execId: uint256LikeSchema$3,
28703
+ deadline: uint256LikeSchema$3,
28704
+ metadata: hexBytesSchema$3
28705
+ }).passthrough();
28706
+ /**
28707
+ * A single claimable reward entry in a claim-rewards response.
28708
+ *
28709
+ * @internal
28710
+ */ const claimedAmountSchema = zod.z.object({
28711
+ token: evmAddressSchema$1,
28712
+ symbol: zod.z.string(),
28713
+ amount: amountJsonSchema$2
28714
+ });
28715
+ /**
28716
+ * The `data` envelope of a claim-rewards response.
28717
+ *
28718
+ * `executionParams` and `signature` are present exactly when rewards are
28719
+ * claimable — never one without the other, and never either when `rewards`
28720
+ * is empty.
28721
+ *
28722
+ * @internal
28723
+ */ const claimRewardsPayloadSchema = zod.z.object({
28724
+ rewards: zod.z.array(claimedAmountSchema),
28725
+ executionParams: claimRewardsExecutionParamsSchema.optional(),
28726
+ signature: evmSignatureSchema.optional()
28727
+ }).refine((data)=>{
28728
+ const signedFieldCount = (data.executionParams === undefined ? 0 : 1) + (data.signature === undefined ? 0 : 1);
28729
+ if (data.rewards.length > 0) {
28730
+ return signedFieldCount === 2;
28731
+ }
28732
+ return signedFieldCount === 0;
28733
+ }, {
28734
+ message: 'claim rewards response must include executionParams and signature only when rewards are claimable'
28735
+ });
28736
+ /**
28737
+ * Zod schema for a claim-rewards API response.
28738
+ *
28739
+ * @internal
28740
+ */ zod.z.object({
28741
+ data: claimRewardsPayloadSchema
28742
+ });
28743
+ /**
28744
+ * A single claimable reward entry in a claim-rewards quote response.
28745
+ *
28746
+ * @internal
28747
+ */ const claimRewardsQuoteRewardSchema = zod.z.object({
28748
+ token: zod.z.string(),
28749
+ symbol: zod.z.string(),
28750
+ amount: amountJsonSchema$2
28751
+ });
28752
+ /**
28753
+ * The `data` envelope of a claim-rewards quote response.
28754
+ *
28755
+ * @internal
28756
+ */ const claimRewardsQuotePayloadSchema = zod.z.object({
28757
+ rewards: zod.z.array(claimRewardsQuoteRewardSchema)
28758
+ });
28759
+ /**
28760
+ * Zod schema for a claim-rewards quote API response.
28761
+ *
28762
+ * @internal
28763
+ */ zod.z.object({
28764
+ data: claimRewardsQuotePayloadSchema
28765
+ });
28766
+
28315
28767
  /**
28316
28768
  * Zod schema for validating EVM adapter capabilities.
28317
28769
  *
@@ -28739,7 +29191,7 @@ const abiParameterSchema = zod.z.object({
28739
29191
  * const result = evmPreparedChainRequestParamsSchema.safeParse(params)
28740
29192
  * ```
28741
29193
  */ zod.z.object({
28742
- type: zod.z.literal('evm'),
29194
+ type: zod.z.literal('evm').optional(),
28743
29195
  abi: abiSchema,
28744
29196
  address: evmAddressSchema$1,
28745
29197
  functionName: zod.z.string({
@@ -28856,7 +29308,7 @@ const abiParameterSchema = zod.z.object({
28856
29308
  message: 'Private key must be a valid 64-character hex string. Please provide a valid private key with or without "0x" prefix.'
28857
29309
  }));
28858
29310
 
28859
- const hexBytesSchema$1 = zod.z.string().regex(/^0x([0-9a-fA-F]{2})*$/, {
29311
+ const hexBytesSchema$2 = zod.z.string().regex(/^0x([0-9a-fA-F]{2})*$/, {
28860
29312
  message: 'value must be a 0x-prefixed even-length hex string'
28861
29313
  }).transform((value)=>value);
28862
29314
  zod.z.object({
@@ -28868,6 +29320,53 @@ zod.z.object({
28868
29320
  amount: zod.z.bigint().refine((value)=>value >= 0n, {
28869
29321
  message: 'amount must be a non-negative bigint'
28870
29322
  }),
29323
+ permitCalldata: hexBytesSchema$2
29324
+ }))
29325
+ }).passthrough();
29326
+
29327
+ const hexBytesSchema$1 = zod.z.string().regex(/^0x([0-9a-fA-F]{2})*$/, {
29328
+ message: 'value must be a 0x-prefixed even-length hex string'
29329
+ }).transform((value)=>value);
29330
+ function isNonNegativeBigIntLike$2(value) {
29331
+ try {
29332
+ return BigInt(value) >= 0n;
29333
+ } catch {
29334
+ return false;
29335
+ }
29336
+ }
29337
+ const uint256LikeSchema$2 = zod.z.union([
29338
+ zod.z.string(),
29339
+ zod.z.bigint()
29340
+ ]).refine(isNonNegativeBigIntLike$2, {
29341
+ message: 'value must be a non-negative bigint-compatible string or bigint'
29342
+ });
29343
+ const borrowExecutionParamsSchema$1 = zod.z.object({
29344
+ instructions: zod.z.array(zod.z.object({
29345
+ target: evmAddressSchema$1,
29346
+ data: hexBytesSchema$1,
29347
+ value: uint256LikeSchema$2,
29348
+ tokenIn: evmAddressSchema$1,
29349
+ amountToApprove: uint256LikeSchema$2,
29350
+ tokenOut: evmAddressSchema$1,
29351
+ minTokenOut: uint256LikeSchema$2
29352
+ }).passthrough()).min(1),
29353
+ tokens: zod.z.array(zod.z.object({
29354
+ token: evmAddressSchema$1,
29355
+ beneficiary: evmAddressSchema$1
29356
+ }).passthrough()),
29357
+ execId: uint256LikeSchema$2,
29358
+ deadline: uint256LikeSchema$2,
29359
+ metadata: hexBytesSchema$1
29360
+ }).passthrough();
29361
+ zod.z.object({
29362
+ executeParams: borrowExecutionParamsSchema$1,
29363
+ signature: evmSignatureSchema,
29364
+ tokenInputs: zod.z.array(zod.z.object({
29365
+ permitType: zod.z.nativeEnum(BorrowPermitType),
29366
+ token: evmAddressSchema$1,
29367
+ amount: zod.z.bigint().refine((value)=>value >= 0n, {
29368
+ message: 'amount must be a non-negative bigint'
29369
+ }),
28871
29370
  permitCalldata: hexBytesSchema$1
28872
29371
  }))
28873
29372
  }).passthrough();
@@ -29215,6 +29714,16 @@ createTokenRegistry();
29215
29714
  name: 'USDC',
29216
29715
  version: '2'
29217
29716
  },
29717
+ Cronos: {
29718
+ chainId: Cronos.chainId,
29719
+ name: 'USDC',
29720
+ version: '2'
29721
+ },
29722
+ Morph: {
29723
+ chainId: Morph.chainId,
29724
+ name: 'USDC',
29725
+ version: '2'
29726
+ },
29218
29727
  Arc_Testnet: {
29219
29728
  chainId: ArcTestnet.chainId,
29220
29729
  name: 'USDC',
@@ -29260,7 +29769,7 @@ const optionalSwapChainIdentifierField = swapChainIdentifierField.optional();
29260
29769
  * Schema for validating AdapterContext.
29261
29770
  * Must always contain both adapter and chain explicitly.
29262
29771
  * Optionally includes address for developer-controlled adapters.
29263
- */ const adapterContextSchema$1 = zod.z.object({
29772
+ */ const adapterContextSchema$2 = zod.z.object({
29264
29773
  adapter: adapterSchema,
29265
29774
  chain: swapChainIdentifierField,
29266
29775
  address: zod.z.string().optional()
@@ -29303,6 +29812,7 @@ const optionalSwapChainIdentifierField = swapChainIdentifierField.optional();
29303
29812
  * - customFee: Optional fee configuration
29304
29813
  * - apiKey: Optional credential string
29305
29814
  * - kitKey: Optional credential string (deprecated alias for apiKey)
29815
+ * - batchTransactions: Optional boolean to opt out of atomic batching
29306
29816
  */ const swapConfigSchema = zod.z.object({
29307
29817
  allowanceStrategy: allowanceStrategySchema.optional(),
29308
29818
  slippageBps: zod.z.number().int().min(0).optional(),
@@ -29313,7 +29823,10 @@ const optionalSwapChainIdentifierField = swapChainIdentifierField.optional();
29313
29823
  })(zod.z.string())).optional(),
29314
29824
  customFee: swapCustomFeeSchema.optional(),
29315
29825
  apiKey: zod.z.string().optional(),
29316
- kitKey: zod.z.string().optional()
29826
+ kitKey: zod.z.string().optional(),
29827
+ batchTransactions: zod.z.boolean({
29828
+ invalid_type_error: 'batchTransactions must be a boolean'
29829
+ }).optional()
29317
29830
  });
29318
29831
  const swapDestinationSchema = zod.z.object({
29319
29832
  chain: optionalSwapChainIdentifierField,
@@ -29321,7 +29834,7 @@ const swapDestinationSchema = zod.z.object({
29321
29834
  message: 'recipientAddress must be a valid blockchain address: EVM (0x + 40 hex chars) or Solana (base58, 32-44 chars)'
29322
29835
  }).optional()
29323
29836
  }).strict();
29324
- const EVM_ADDRESS_REGEX = /^0x[a-fA-F0-9]{40}$/;
29837
+ const EVM_ADDRESS_REGEX$1 = /^0x[a-fA-F0-9]{40}$/;
29325
29838
  const SOLANA_ADDRESS_REGEX = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
29326
29839
  const BASE58_CHARS_REGEX = /^[1-9A-HJ-NP-Za-km-z]+$/;
29327
29840
  /**
@@ -29364,7 +29877,7 @@ const MAX_DISPLAY_LENGTH = 30;
29364
29877
  */ let _symbolResult;
29365
29878
  const swapTokenSchema = zod.z.string().superRefine((value, ctx)=>{
29366
29879
  if (value.startsWith('0x')) {
29367
- if (!EVM_ADDRESS_REGEX.test(value)) {
29880
+ if (!EVM_ADDRESS_REGEX$1.test(value)) {
29368
29881
  ctx.addIssue({
29369
29882
  code: zod.z.ZodIssueCode.custom,
29370
29883
  message: `Invalid EVM token address format. Expected '0x' followed by 40 hexadecimal characters, but received: '${truncate(value)}'`
@@ -29459,7 +29972,7 @@ const amountInSchema = zod.z.string().min(1, 'Required').pipe(createDecimalStrin
29459
29972
  * }
29460
29973
  * ```
29461
29974
  */ zod.z.object({
29462
- from: adapterContextSchema$1,
29975
+ from: adapterContextSchema$2,
29463
29976
  tokenIn: swapTokenSchema,
29464
29977
  tokenOut: swapTokenSchema,
29465
29978
  amountIn: amountInSchema,
@@ -29500,16 +30013,16 @@ new Set(Object.values(Blockchain));
29500
30013
 
29501
30014
  new Set(Object.values(Blockchain));
29502
30015
 
29503
- /** SDK name used in telemetry payloads. */ resolveKitSdkName(pkg$1.name);
30016
+ /** SDK name used in telemetry payloads. */ resolveKitSdkName(pkg$2.name);
29504
30017
 
29505
30018
  // Auto-register this kit for user agent tracking
29506
- registerKit(`${pkg$1.name}/${pkg$1.version}`);
30019
+ registerKit(`${pkg$2.name}/${pkg$2.version}`);
29507
30020
 
29508
- var name = "@circle-fin/earn-kit";
29509
- var version = "1.8.0";
29510
- var pkg = {
29511
- name: name,
29512
- version: version};
30021
+ var name$1 = "@circle-fin/earn-kit";
30022
+ var version$1 = "1.9.0";
30023
+ var pkg$1 = {
30024
+ name: name$1,
30025
+ version: version$1};
29513
30026
 
29514
30027
  /**
29515
30028
  * Map SDK chain identifiers to bridge API chain strings.
@@ -29581,7 +30094,7 @@ function isBigIntLike(value) {
29581
30094
  return false;
29582
30095
  }
29583
30096
  }
29584
- function isNonNegativeBigIntLike(value) {
30097
+ function isNonNegativeBigIntLike$1(value) {
29585
30098
  try {
29586
30099
  return BigInt(value) >= 0n;
29587
30100
  } catch {
@@ -29601,28 +30114,22 @@ const bridgeFeeTokenSchema = hexAddressSchema;
29601
30114
  * unchanged so signed execution params can be forwarded verbatim.
29602
30115
  *
29603
30116
  * @internal
29604
- */ const uint256LikeSchema = zod.z.union([
30117
+ */ const uint256LikeSchema$1 = zod.z.union([
29605
30118
  zod.z.string(),
29606
30119
  zod.z.bigint()
29607
- ]).refine(isNonNegativeBigIntLike, {
30120
+ ]).refine(isNonNegativeBigIntLike$1, {
29608
30121
  message: 'value must be a non-negative bigint-compatible string or bigint'
29609
30122
  });
29610
30123
  /**
29611
30124
  * Zod schema for typed amount objects emitted by the API.
29612
30125
  *
29613
30126
  * @internal
29614
- */ const amountJsonSchema = zod.z.object({
30127
+ */ const amountJsonSchema$1 = zod.z.object({
29615
30128
  raw: zod.z.string().refine(isBigIntLike, {
29616
30129
  message: 'raw must be a bigint-compatible string'
29617
30130
  }),
29618
30131
  decimals: zod.z.number().int().nonnegative()
29619
30132
  });
29620
- /** @internal */ const hexBytesSchema = zod.z.union([
29621
- hexStringSchema,
29622
- zod.z.literal('0x')
29623
- ]).refine((value)=>value.length % 2 === 0, {
29624
- message: 'value must be a 0x-prefixed even-length hex string'
29625
- });
29626
30133
  /** @internal */ const bytes32HexSchema = hexStringSchema.refine((value)=>value.length === BYTES32_HEX_LENGTH, {
29627
30134
  message: 'value must be a 0x-prefixed 32-byte hex string'
29628
30135
  });
@@ -29706,9 +30213,9 @@ const bridgeFeeTokenSchema = hexAddressSchema;
29706
30213
  *
29707
30214
  * @internal
29708
30215
  */ const liquidityProfileSchema = zod.z.object({
29709
- totalDeposits: amountJsonSchema,
29710
- available: amountJsonSchema,
29711
- totalSupply: amountJsonSchema,
30216
+ totalDeposits: amountJsonSchema$1,
30217
+ available: amountJsonSchema$1,
30218
+ totalSupply: amountJsonSchema$1,
29712
30219
  status: zod.z.enum([
29713
30220
  'active',
29714
30221
  'low_liquidity'
@@ -29745,8 +30252,8 @@ const bridgeFeeTokenSchema = hexAddressSchema;
29745
30252
  vaultFee: zod.z.number(),
29746
30253
  rewards: zod.z.array(vaultRewardSchema),
29747
30254
  collateral: zod.z.array(collateralSchema),
29748
- totalDeposits: amountJsonSchema,
29749
- liquidity: amountJsonSchema,
30255
+ totalDeposits: amountJsonSchema$1,
30256
+ liquidity: amountJsonSchema$1,
29750
30257
  status: zod.z.enum([
29751
30258
  'active',
29752
30259
  'low_liquidity'
@@ -29856,13 +30363,13 @@ const bridgeFeeTokenSchema = hexAddressSchema;
29856
30363
  */ const accruedRewardSchema = zod.z.object({
29857
30364
  token: zod.z.string(),
29858
30365
  symbol: zod.z.string(),
29859
- amount: amountJsonSchema
30366
+ amount: amountJsonSchema$1
29860
30367
  });
29861
30368
  const positionPnlSchema = zod.z.discriminatedUnion('status', [
29862
30369
  zod.z.object({
29863
30370
  status: zod.z.literal('available'),
29864
- principalDeposited: amountJsonSchema,
29865
- totalYieldEarned: amountJsonSchema
30371
+ principalDeposited: amountJsonSchema$1,
30372
+ totalYieldEarned: amountJsonSchema$1
29866
30373
  }),
29867
30374
  zod.z.object({
29868
30375
  status: zod.z.literal('pending')
@@ -29882,9 +30389,9 @@ const positionPnlSchema = zod.z.discriminatedUnion('status', [
29882
30389
  vaultAddress: zod.z.string(),
29883
30390
  vaultName: zod.z.string(),
29884
30391
  asset: zod.z.string(),
29885
- currentBalance: amountJsonSchema,
30392
+ currentBalance: amountJsonSchema$1,
29886
30393
  currentApy: zod.z.number(),
29887
- shares: amountJsonSchema,
30394
+ shares: amountJsonSchema$1,
29888
30395
  pnl: positionPnlSchema,
29889
30396
  accruedRewards: zod.z.array(accruedRewardSchema).optional(),
29890
30397
  rewardsUnavailableReason: zod.z.string().optional()
@@ -29912,7 +30419,7 @@ const positionPnlSchema = zod.z.discriminatedUnion('status', [
29912
30419
  * @internal
29913
30420
  */ const earnInstructionSchema = zod.z.object({
29914
30421
  tokenIn: hexAddressSchema,
29915
- amountToApprove: uint256LikeSchema
30422
+ amountToApprove: uint256LikeSchema$1
29916
30423
  }).passthrough();
29917
30424
  /**
29918
30425
  * Zod schema for earn execution params returned by the earn service.
@@ -29926,50 +30433,10 @@ const positionPnlSchema = zod.z.discriminatedUnion('status', [
29926
30433
  instructions: zod.z.tuple([
29927
30434
  earnInstructionSchema
29928
30435
  ]).rest(earnInstructionSchema),
29929
- deadline: uint256LikeSchema
30436
+ deadline: uint256LikeSchema$1
29930
30437
  }).passthrough();
29931
30438
  /** @internal */ const depositExecutionParamsSchema = earnExecutionParamsSchema;
29932
30439
  /** @internal */ const withdrawExecutionParamsSchema = earnExecutionParamsSchema;
29933
- /**
29934
- * Zod schema for the claim rewards instruction fields that must be safe to
29935
- * ABI-encode.
29936
- *
29937
- * Claim rewards has no approval preflight, so this validates the forwarded
29938
- * signed payload at the provider boundary. Additional instruction fields are
29939
- * accepted and preserved.
29940
- *
29941
- * @internal
29942
- */ const claimRewardsInstructionSchema = zod.z.object({
29943
- target: hexAddressSchema,
29944
- data: hexBytesSchema,
29945
- value: uint256LikeSchema,
29946
- tokenIn: hexAddressSchema,
29947
- amountToApprove: uint256LikeSchema,
29948
- tokenOut: hexAddressSchema,
29949
- minTokenOut: uint256LikeSchema
29950
- }).passthrough();
29951
- /** @internal */ const claimRewardsTokenSchema = zod.z.object({
29952
- token: hexAddressSchema,
29953
- beneficiary: hexAddressSchema
29954
- }).passthrough();
29955
- /**
29956
- * Zod schema for signed claim rewards execution params.
29957
- *
29958
- * This enforces the required envelope and preserves additive fields so the
29959
- * adapter receives the signed params verbatim.
29960
- *
29961
- * @internal
29962
- */ const claimRewardsExecutionParamsSchema = zod.z.object({
29963
- instructions: zod.z.tuple([
29964
- claimRewardsInstructionSchema
29965
- ]).rest(claimRewardsInstructionSchema),
29966
- tokens: zod.z.tuple([
29967
- claimRewardsTokenSchema
29968
- ]).rest(claimRewardsTokenSchema),
29969
- execId: uint256LikeSchema,
29970
- deadline: uint256LikeSchema,
29971
- metadata: hexBytesSchema
29972
- }).passthrough();
29973
30440
  /**
29974
30441
  * Zod schema for the deposit payload inside the API `data` envelope.
29975
30442
  *
@@ -30028,9 +30495,9 @@ const eip712DomainSchema = eip712DomainSchema$1.extend({
30028
30495
  message: zod.z.object({
30029
30496
  from: hexAddressSchema,
30030
30497
  to: hexAddressSchema,
30031
- value: uint256LikeSchema,
30032
- validAfter: uint256LikeSchema,
30033
- validBefore: uint256LikeSchema,
30498
+ value: uint256LikeSchema$1,
30499
+ validAfter: uint256LikeSchema$1,
30500
+ validBefore: uint256LikeSchema$1,
30034
30501
  nonce: bytes32HexSchema
30035
30502
  }).passthrough()
30036
30503
  }).passthrough();
@@ -30048,7 +30515,7 @@ const eip712DomainSchema = eip712DomainSchema$1.extend({
30048
30515
  feeToken: bridgeFeeTokenSchema,
30049
30516
  items: zod.z.array(zod.z.object({
30050
30517
  type: zod.z.string(),
30051
- amount: uint256LikeSchema
30518
+ amount: uint256LikeSchema$1
30052
30519
  }).passthrough())
30053
30520
  }).passthrough();
30054
30521
  /**
@@ -30064,7 +30531,7 @@ const eip712DomainSchema = eip712DomainSchema$1.extend({
30064
30531
  const bridgeDepositPrepareReviewSchema = zod.z.object({
30065
30532
  sourceChain: zod.z.string(),
30066
30533
  destinationChain: zod.z.string(),
30067
- amount: amountJsonSchema,
30534
+ amount: amountJsonSchema$1,
30068
30535
  vaultAddress: hexAddressSchema
30069
30536
  }).passthrough();
30070
30537
  /** @internal */ const bridgeQuoteExpirySchema = zod.z.discriminatedUnion('mode', [
@@ -30199,72 +30666,34 @@ const bridgeDepositPrepareReviewSchema = zod.z.object({
30199
30666
  // ---------------------------------------------------------------------------
30200
30667
  // Claim rewards response schema
30201
30668
  // ---------------------------------------------------------------------------
30669
+ // ---------------------------------------------------------------------------
30670
+ // Deposit quote response schema
30671
+ // ---------------------------------------------------------------------------
30202
30672
  /**
30203
- * Zod schema for a claimed reward amount in the API response.
30673
+ * Zod schema for a fee entry in an EarnKit API response.
30674
+ *
30675
+ * Shared across deposit and withdrawal responses (and reusable for real
30676
+ * charged fees, not just quote estimates). `type` identifies the fee category.
30677
+ * For cross-chain deposit quotes this is the kits-proxy fee-quote item type
30678
+ * (e.g. `'FORWARD'`, `'PRE_FINALITY'`). For withdrawal quotes, Circle fees use
30679
+ * `type: 'circle'`. `status` qualifies the fee (e.g. `'estimated'` for a
30680
+ * pre-sign cross-chain fee). Both are omitted on plain fees.
30204
30681
  *
30205
30682
  * @internal
30206
- */ const claimedAmountSchema = zod.z.object({
30207
- token: hexAddressSchema,
30208
- symbol: zod.z.string(),
30209
- amount: amountJsonSchema
30683
+ */ const feeSchema = zod.z.object({
30684
+ type: zod.z.string().optional(),
30685
+ status: zod.z.string().optional(),
30686
+ token: zod.z.string(),
30687
+ amount: amountJsonSchema$1
30210
30688
  });
30211
30689
  /**
30212
- * Zod schema for the claim rewards payload inside the API `data` envelope.
30690
+ * Zod schema for a native gas-fee entry in an EarnKit quote response.
30213
30691
  *
30214
- * Enforce that `executionParams` and `signature` are either both
30215
- * present (claimable rewards) or both absent (nothing to claim).
30216
- *
30217
- * @internal
30218
- */ const claimRewardsPayloadSchema = zod.z.object({
30219
- rewards: zod.z.array(claimedAmountSchema),
30220
- executionParams: claimRewardsExecutionParamsSchema.optional(),
30221
- signature: hexSignatureSchema.optional()
30222
- }).refine((data)=>{
30223
- const signedFieldCount = (data.executionParams === undefined ? 0 : 1) + (data.signature === undefined ? 0 : 1);
30224
- if (data.rewards.length > 0) {
30225
- return signedFieldCount === 2;
30226
- }
30227
- return signedFieldCount === 0;
30228
- }, {
30229
- message: 'claim rewards response must include executionParams and signature only when rewards are claimable'
30230
- });
30231
- /**
30232
- * Zod schema for the `POST /v1/earnKit/claimRewards` API response.
30233
- *
30234
- * The Earn Service API wraps the claim rewards payload in a `data` envelope.
30235
- *
30236
- * @internal
30237
- */ zod.z.object({
30238
- data: claimRewardsPayloadSchema
30239
- });
30240
- // ---------------------------------------------------------------------------
30241
- // Deposit quote response schema
30242
- // ---------------------------------------------------------------------------
30243
- /**
30244
- * Zod schema for a fee entry in an EarnKit API response.
30245
- *
30246
- * Shared across deposit and withdrawal responses (and reusable for real
30247
- * charged fees, not just quote estimates). `type` identifies the fee category.
30248
- * For cross-chain deposit quotes this is the kits-proxy fee-quote item type
30249
- * (e.g. `'FORWARD'`, `'PRE_FINALITY'`). For withdrawal quotes, Circle fees use
30250
- * `type: 'circle'`. `status` qualifies the fee (e.g. `'estimated'` for a
30251
- * pre-sign cross-chain fee). Both are omitted on plain fees.
30252
- *
30253
- * @internal
30254
- */ const feeSchema = zod.z.object({
30255
- type: zod.z.string().optional(),
30256
- status: zod.z.string().optional(),
30257
- token: zod.z.string(),
30258
- amount: amountJsonSchema
30259
- });
30260
- /**
30261
- * Zod schema for a native gas-fee entry in an EarnKit quote response.
30262
- *
30263
- * The Earn Service backend estimates gas server-side and returns one entry per
30264
- * action (`Approve`, `Deposit`, `Withdraw`). A successful estimate carries
30265
- * `fees` in the SDK `EstimatedGas` shape (`{ gas, gasPrice, fee }`), each a raw
30266
- * integer string in the chain's native base units. When the backend cannot
30267
- * estimate an action it returns `fees: null` with an `error` message instead.
30692
+ * The Earn Service backend estimates gas server-side and returns one entry per
30693
+ * action (`Approve`, `Deposit`, `Withdraw`). A successful estimate carries
30694
+ * `fees` in the SDK `EstimatedGas` shape (`{ gas, gasPrice, fee }`), each a raw
30695
+ * integer string in the chain's native base units. When the backend cannot
30696
+ * estimate an action it returns `fees: null` with an `error` message instead.
30268
30697
  *
30269
30698
  * The schema deliberately validates almost nothing beyond the envelope: `name`
30270
30699
  * is optional and `fees` is entirely unvalidated (`unknown`). ALL validation
@@ -30292,8 +30721,8 @@ const bridgeDepositPrepareReviewSchema = zod.z.object({
30292
30721
  vaultAddress: zod.z.string(),
30293
30722
  vaultName: zod.z.string(),
30294
30723
  asset: zod.z.string(),
30295
- depositAmount: amountJsonSchema,
30296
- expectedShares: amountJsonSchema,
30724
+ depositAmount: amountJsonSchema$1,
30725
+ expectedShares: amountJsonSchema$1,
30297
30726
  sharePrice: zod.z.string(),
30298
30727
  currentApy: zod.z.number(),
30299
30728
  fees: zod.z.array(feeSchema).optional(),
@@ -30319,10 +30748,10 @@ const bridgeDepositPrepareReviewSchema = zod.z.object({
30319
30748
  vaultAddress: zod.z.string(),
30320
30749
  vaultName: zod.z.string(),
30321
30750
  asset: zod.z.string(),
30322
- withdrawAmount: amountJsonSchema,
30323
- sharesToRedeem: amountJsonSchema,
30751
+ withdrawAmount: amountJsonSchema$1,
30752
+ sharesToRedeem: amountJsonSchema$1,
30324
30753
  sharePrice: zod.z.string(),
30325
- maxWithdrawable: amountJsonSchema,
30754
+ maxWithdrawable: amountJsonSchema$1,
30326
30755
  fees: zod.z.array(feeSchema),
30327
30756
  gasFees: zod.z.array(quoteGasFeeSchema).optional(),
30328
30757
  warnings: zod.z.array(zod.z.string()).optional()
@@ -30339,31 +30768,6 @@ const bridgeDepositPrepareReviewSchema = zod.z.object({
30339
30768
  // ---------------------------------------------------------------------------
30340
30769
  // Claim rewards quote response schema
30341
30770
  // ---------------------------------------------------------------------------
30342
- /**
30343
- * Zod schema for a reward in the claim rewards quote response.
30344
- *
30345
- * @internal
30346
- */ const claimRewardsQuoteRewardSchema = zod.z.object({
30347
- token: zod.z.string(),
30348
- symbol: zod.z.string(),
30349
- amount: amountJsonSchema
30350
- });
30351
- /**
30352
- * Zod schema for the inner claim rewards quote payload.
30353
- *
30354
- * @internal
30355
- */ const claimRewardsQuotePayloadSchema = zod.z.object({
30356
- rewards: zod.z.array(claimRewardsQuoteRewardSchema)
30357
- });
30358
- /**
30359
- * Zod schema for the `POST /v1/earnKit/claimRewards/quote` API response.
30360
- *
30361
- * The API wraps the payload in a `data` envelope.
30362
- *
30363
- * @internal
30364
- */ zod.z.object({
30365
- data: claimRewardsQuotePayloadSchema
30366
- });
30367
30771
  // ---------------------------------------------------------------------------
30368
30772
  // Batch vault response schema
30369
30773
  // ---------------------------------------------------------------------------
@@ -30503,7 +30907,7 @@ createTokenRegistry();
30503
30907
  * fast instead of being silently stripped.
30504
30908
  *
30505
30909
  * @internal
30506
- */ const adapterContextSchema = zod.z.object({
30910
+ */ const adapterContextSchema$1 = zod.z.object({
30507
30911
  adapter: adapterSchema,
30508
30912
  // AdapterContext accepts a wider chain field, but runtime validation must
30509
30913
  // stay constrained to EarnChain identifiers.
@@ -30735,7 +31139,7 @@ const sourceAdapterContextSchema = zod.z.object({
30735
31139
  * })
30736
31140
  * ```
30737
31141
  */ zod.z.object({
30738
- from: adapterContextSchema,
31142
+ from: adapterContextSchema$1,
30739
31143
  vaultAddress: vaultAddressSchema,
30740
31144
  config: earnConfigSchema.optional()
30741
31145
  });
@@ -30790,7 +31194,7 @@ const sourceAdapterContextSchema = zod.z.object({
30790
31194
  *
30791
31195
  * @internal Exported for branch-targeted validation in the deposit operation.
30792
31196
  */ const sameChainDepositParamsSchema = zod.z.object({
30793
- from: adapterContextSchema,
31197
+ from: adapterContextSchema$1,
30794
31198
  to: zod.z.undefined().optional(),
30795
31199
  vaultAddress: vaultAddressSchema,
30796
31200
  amount: amountSchema,
@@ -30861,7 +31265,7 @@ const crossChainDepositDestinationSchema = zod.z.object({
30861
31265
  * })
30862
31266
  * ```
30863
31267
  */ zod.z.object({
30864
- from: adapterContextSchema,
31268
+ from: adapterContextSchema$1,
30865
31269
  vaultAddress: vaultAddressSchema,
30866
31270
  amount: amountSchema,
30867
31271
  config: earnConfigSchema.optional()
@@ -30915,7 +31319,7 @@ const crossChainDepositDestinationSchema = zod.z.object({
30915
31319
  * })
30916
31320
  * ```
30917
31321
  */ zod.z.object({
30918
- from: adapterContextSchema,
31322
+ from: adapterContextSchema$1,
30919
31323
  vaultAddress: vaultAddressSchema,
30920
31324
  amount: amountSchema,
30921
31325
  config: earnConfigSchema.optional()
@@ -30935,12 +31339,12 @@ const crossChainDepositDestinationSchema = zod.z.object({
30935
31339
  *
30936
31340
  * @internal
30937
31341
  */ zod.z.object({
30938
- from: adapterContextSchema,
31342
+ from: adapterContextSchema$1,
30939
31343
  vaultAddress: vaultAddressSchema,
30940
31344
  config: earnConfigSchema.optional()
30941
31345
  });
30942
31346
  const sameChainGetDepositQuoteParamsSchema = zod.z.object({
30943
- from: adapterContextSchema,
31347
+ from: adapterContextSchema$1,
30944
31348
  vaultAddress: vaultAddressSchema,
30945
31349
  amount: amountSchema,
30946
31350
  config: earnConfigSchema.optional()
@@ -31024,7 +31428,7 @@ function hasCrossChainDepositQuoteShape(params) {
31024
31428
  * })
31025
31429
  * ```
31026
31430
  */ zod.z.object({
31027
- from: adapterContextSchema,
31431
+ from: adapterContextSchema$1,
31028
31432
  vaultAddress: vaultAddressSchema,
31029
31433
  amount: amountSchema,
31030
31434
  config: earnConfigSchema.optional()
@@ -31044,15 +31448,15 @@ function hasCrossChainDepositQuoteShape(params) {
31044
31448
  *
31045
31449
  * @internal
31046
31450
  */ zod.z.object({
31047
- from: adapterContextSchema,
31451
+ from: adapterContextSchema$1,
31048
31452
  vaultAddress: vaultAddressSchema,
31049
31453
  config: earnConfigSchema.optional()
31050
31454
  });
31051
31455
 
31052
- /** SDK name used in telemetry payloads. */ resolveKitSdkName(pkg.name);
31456
+ /** SDK name used in telemetry payloads. */ resolveKitSdkName(pkg$1.name);
31053
31457
 
31054
31458
  // Auto-register this kit for user agent tracking
31055
- registerKit(`${pkg.name}/${pkg.version}`);
31459
+ registerKit(`${pkg$1.name}/${pkg$1.version}`);
31056
31460
 
31057
31461
  /**
31058
31462
  * Register event handlers from a context actions map to a kit instance.
@@ -31118,6 +31522,1358 @@ registerKit(`${pkg.name}/${pkg.version}`);
31118
31522
  }
31119
31523
  };
31120
31524
 
31525
+ var name = "@circle-fin/borrow-kit";
31526
+ var version = "1.1.0";
31527
+ var pkg = {
31528
+ name: name,
31529
+ version: version};
31530
+
31531
+ const HUMAN_READABLE_AMOUNT_REGEX$1 = /^(?:0|[1-9]\d*)(?:\.\d+)?$/;
31532
+ const EVM_ADDRESS_REGEX = /^0x[0-9a-fA-F]{40}$/;
31533
+ const POSITIVE_USDC_AMOUNT_REGEX = /^(?:0|[1-9]\d*)(?:\.\d{1,6})?$/;
31534
+ /**
31535
+ * True when `value` parses as a URL whose scheme is exactly `https:`.
31536
+ *
31537
+ * Uses the WHATWG `URL` parser rather than `startsWith('https://')` so the
31538
+ * check is scheme-normalized (case-insensitive, e.g. `HTTPS://...` still
31539
+ * passes) instead of a literal prefix match. `value` has already passed
31540
+ * `z.string().url()` by the time this runs, but a caller could still supply
31541
+ * a string that `URL` itself rejects, so the parse is guarded.
31542
+ */ function isHttpsUrl$1(value) {
31543
+ try {
31544
+ return new URL(value).protocol === 'https:';
31545
+ } catch {
31546
+ return false;
31547
+ }
31548
+ }
31549
+ const borrowServiceConfigSchema$1 = zod.z.object({
31550
+ apiKey: zod.z.string().optional(),
31551
+ baseUrl: zod.z.string().url().optional()
31552
+ }).strict();
31553
+ /** Loan asset amounts are USDC, so they carry at most 6 decimal places. */ function positiveLoanAssetAmountSchema(field) {
31554
+ return zod.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), {
31555
+ message: `${field} must be greater than zero`
31556
+ });
31557
+ }
31558
+ /**
31559
+ * Collateral amounts denominate an arbitrary token, so the decimal count is the
31560
+ * token's rather than USDC's — a satoshi-precision collateral is valid.
31561
+ */ function positiveCollateralAmountSchema(field) {
31562
+ return zod.z.string().regex(HUMAN_READABLE_AMOUNT_REGEX$1, `${field} must be a canonical decimal`).refine((value)=>/[1-9]/.test(value), {
31563
+ message: `${field} must be greater than zero`
31564
+ });
31565
+ }
31566
+ /** @internal */ zod.z.object({
31567
+ chain: chainIdentifierSchema,
31568
+ marketId: zod.z.string().min(1),
31569
+ config: borrowServiceConfigSchema$1.optional()
31570
+ }).strict();
31571
+ /** @internal */ zod.z.object({
31572
+ walletAddress: zod.z.string().regex(EVM_ADDRESS_REGEX),
31573
+ chain: chainIdentifierSchema,
31574
+ pageSize: zod.z.number().int().positive().optional(),
31575
+ pageAfter: zod.z.string().min(1).optional(),
31576
+ config: borrowServiceConfigSchema$1.optional()
31577
+ }).strict();
31578
+ /** @internal */ const marketSortBySchema$1 = zod.z.enum([
31579
+ 'lltv',
31580
+ 'borrowApy'
31581
+ ]);
31582
+ /** @internal */ const decimalFilterSchema$1 = zod.z.string().regex(/^\d+(\.\d+)?$/, 'filter thresholds must be non-negative decimal strings');
31583
+ /**
31584
+ * Provider-level runtime schema for {@link ExploreMarketsServiceParams}.
31585
+ *
31586
+ * Mirrors the kit's `exploreMarketsParamsSchema` at the provider boundary so a
31587
+ * plain-JS caller invoking `BorrowServiceProvider.exploreMarkets` directly is
31588
+ * still validated before any filter reaches `url.searchParams` — including the
31589
+ * kit's `minLltv <= maxLltv` / `minBorrow <= maxBorrow` range check, which
31590
+ * otherwise lets an inverted range serialize into the request and return a
31591
+ * silently empty page.
31592
+ *
31593
+ * @internal
31594
+ */ zod.z.object({
31595
+ chain: chainIdentifierSchema,
31596
+ minLltv: decimalFilterSchema$1.optional(),
31597
+ maxLltv: decimalFilterSchema$1.optional(),
31598
+ minBorrow: decimalFilterSchema$1.optional(),
31599
+ maxBorrow: decimalFilterSchema$1.optional(),
31600
+ sortBy: marketSortBySchema$1.optional(),
31601
+ pageSize: zod.z.number().int().min(1).max(1000).optional(),
31602
+ pageAfter: zod.z.string().min(1).optional(),
31603
+ config: borrowServiceConfigSchema$1.optional()
31604
+ }).strict().superRefine((value, ctx)=>{
31605
+ if (value.minLltv !== undefined && value.maxLltv !== undefined && Number(value.minLltv) > Number(value.maxLltv)) {
31606
+ ctx.addIssue({
31607
+ code: zod.z.ZodIssueCode.custom,
31608
+ path: [
31609
+ 'maxLltv'
31610
+ ],
31611
+ message: 'maxLltv must be greater than or equal to minLltv'
31612
+ });
31613
+ }
31614
+ if (value.minBorrow !== undefined && value.maxBorrow !== undefined && Number(value.minBorrow) > Number(value.maxBorrow)) {
31615
+ ctx.addIssue({
31616
+ code: zod.z.ZodIssueCode.custom,
31617
+ path: [
31618
+ 'maxBorrow'
31619
+ ],
31620
+ message: 'maxBorrow must be greater than or equal to minBorrow'
31621
+ });
31622
+ }
31623
+ });
31624
+ /** @internal */ const repayQuoteServiceParamsSchema = zod.z.object({
31625
+ loanId: zod.z.string().uuid(),
31626
+ chain: chainIdentifierSchema,
31627
+ repayAmount: positiveLoanAssetAmountSchema('repayAmount'),
31628
+ config: borrowServiceConfigSchema$1.optional()
31629
+ }).strict();
31630
+ /** @internal */ repayQuoteServiceParamsSchema.omit({
31631
+ chain: true
31632
+ }).extend({
31633
+ idempotencyKey: zod.z.string().uuid().optional()
31634
+ });
31635
+ /** @internal */ const closeLoanQuoteServiceParamsSchema = zod.z.object({
31636
+ loanId: zod.z.string().uuid(),
31637
+ chain: chainIdentifierSchema,
31638
+ slippageBps: zod.z.number().int().min(0).max(10_000).optional(),
31639
+ config: borrowServiceConfigSchema$1.optional()
31640
+ }).strict();
31641
+ /** Fields a borrow carries whichever way it names its position. @internal */ const borrowServiceParamsBaseShape = {
31642
+ loanAssetAmount: positiveLoanAssetAmountSchema('loanAssetAmount'),
31643
+ slippageBps: zod.z.number().int().min(0).max(10_000).optional(),
31644
+ idempotencyKey: zod.z.string().uuid().optional(),
31645
+ config: borrowServiceConfigSchema$1.optional()
31646
+ };
31647
+ /** A first-open: owner, chain and market are required together. @internal */ const openLoanServiceParamsSchema = zod.z.object({
31648
+ ...borrowServiceParamsBaseShape,
31649
+ walletAddress: zod.z.string().regex(EVM_ADDRESS_REGEX),
31650
+ chain: chainIdentifierSchema,
31651
+ marketId: zod.z.string().min(1)
31652
+ }).strict();
31653
+ /** A borrow-more: the loan is the only addressing field. @internal */ const borrowMoreServiceParamsSchema = zod.z.object({
31654
+ ...borrowServiceParamsBaseShape,
31655
+ loanId: zod.z.string().uuid()
31656
+ }).strict();
31657
+ /**
31658
+ * The two ways a borrow names its position.
31659
+ *
31660
+ * A union of strict objects rather than one object with a refinement:
31661
+ * `.strict()` is what rejects a mixed body, and the service refuses `loanId`
31662
+ * next to any open-tuple field as well as an incomplete open tuple.
31663
+ *
31664
+ * @internal
31665
+ */ zod.z.union([
31666
+ openLoanServiceParamsSchema,
31667
+ borrowMoreServiceParamsSchema
31668
+ ]);
31669
+ /** A first-open quote carries the complete owner/chain/market tuple. @internal */ const openLoanQuoteServiceParamsSchema = zod.z.object({
31670
+ walletAddress: zod.z.string().regex(EVM_ADDRESS_REGEX),
31671
+ chain: chainIdentifierSchema,
31672
+ marketId: zod.z.string().min(1),
31673
+ loanAssetAmount: positiveLoanAssetAmountSchema('loanAssetAmount'),
31674
+ slippageBps: zod.z.number().int().min(0).max(10_000).optional(),
31675
+ config: borrowServiceConfigSchema$1.optional()
31676
+ }).strict();
31677
+ /** A borrow-more quote needs only the existing loan ID. @internal */ const borrowMoreQuoteServiceParamsSchema = zod.z.object({
31678
+ loanId: zod.z.string().uuid(),
31679
+ loanAssetAmount: positiveLoanAssetAmountSchema('loanAssetAmount'),
31680
+ slippageBps: zod.z.number().int().min(0).max(10_000).optional(),
31681
+ config: borrowServiceConfigSchema$1.optional()
31682
+ }).strict();
31683
+ /** @internal */ zod.z.union([
31684
+ openLoanQuoteServiceParamsSchema,
31685
+ borrowMoreQuoteServiceParamsSchema
31686
+ ]);
31687
+ /** @internal */ zod.z.object({
31688
+ chain: chainIdentifierSchema,
31689
+ marketId: zod.z.string().min(1),
31690
+ loanAssetAmount: positiveLoanAssetAmountSchema('loanAssetAmount'),
31691
+ // `.finite()` is load-bearing, not decoration: the value is encoded as a
31692
+ // decimal string on the wire, and there is no decimal string for Infinity.
31693
+ targetHealthFactor: zod.z.number().positive().finite(),
31694
+ config: borrowServiceConfigSchema$1.optional()
31695
+ }).strict();
31696
+ /** @internal */ zod.z.object({
31697
+ chain: chainIdentifierSchema,
31698
+ marketId: zod.z.string().min(1),
31699
+ collateralAmount: positiveCollateralAmountSchema('collateralAmount'),
31700
+ config: borrowServiceConfigSchema$1.optional()
31701
+ }).strict();
31702
+ /** @internal */ closeLoanQuoteServiceParamsSchema.omit({
31703
+ chain: true
31704
+ }).extend({
31705
+ idempotencyKey: zod.z.string().uuid().optional()
31706
+ });
31707
+ /**
31708
+ * Provider-level runtime schema for the non-adapter portion of
31709
+ * {@link ClaimRewardsServiceParams} / {@link GetRewardsServiceParams}.
31710
+ *
31711
+ * `from` (the adapter context) is intentionally excluded — like
31712
+ * `repay`/`closeLoan`, it is validated structurally by TypeScript and resolved
31713
+ * via `resolveAdapterContext` rather than by a Zod schema, since it carries a
31714
+ * live adapter instance rather than plain data. `config` is the only field
31715
+ * that reaches the wire, so it is what gets validated here, matching every
31716
+ * other provider method's independent `parseOrThrow` call.
31717
+ *
31718
+ * @internal
31719
+ */ zod.z.object({
31720
+ config: borrowServiceConfigSchema$1.optional()
31721
+ }).strict();
31722
+ /**
31723
+ * Provider-level runtime schema for {@link GetPositionServiceParams}.
31724
+ *
31725
+ * @internal
31726
+ */ zod.z.object({
31727
+ loanId: zod.z.string().uuid(),
31728
+ config: borrowServiceConfigSchema$1.optional()
31729
+ }).strict();
31730
+ /** @internal */ const withdrawCollateralQuoteServiceParamsSchema = zod.z.object({
31731
+ loanId: zod.z.string().uuid(),
31732
+ chain: chainIdentifierSchema,
31733
+ // Carries no decimal-place cap: the repayment is USDC, but the collateral
31734
+ // is the market's own token and may hold more precision than USDC's six.
31735
+ collateralAmount: positiveCollateralAmountSchema('collateralAmount'),
31736
+ slippageBps: zod.z.number().int().min(0).max(10_000).optional(),
31737
+ config: borrowServiceConfigSchema$1.optional()
31738
+ }).strict();
31739
+ /** @internal */ withdrawCollateralQuoteServiceParamsSchema.omit({
31740
+ chain: true
31741
+ }).extend({
31742
+ idempotencyKey: zod.z.string().uuid().optional()
31743
+ });
31744
+ /**
31745
+ * Adding collateral sizes no second leg, so there is no slippage bound.
31746
+ *
31747
+ * @internal
31748
+ */ const addCollateralQuoteServiceParamsSchema = zod.z.object({
31749
+ loanId: zod.z.string().uuid(),
31750
+ chain: chainIdentifierSchema,
31751
+ collateralAmount: positiveCollateralAmountSchema('collateralAmount'),
31752
+ config: borrowServiceConfigSchema$1.optional()
31753
+ }).strict();
31754
+ /** @internal */ addCollateralQuoteServiceParamsSchema.omit({
31755
+ chain: true
31756
+ }).extend({
31757
+ idempotencyKey: zod.z.string().uuid().optional()
31758
+ });
31759
+ /** @internal */ const borrowAssetAmountSchema = zod.z.object({
31760
+ token: zod.z.string().min(1),
31761
+ tokenAddress: zod.z.string().regex(EVM_ADDRESS_REGEX),
31762
+ decimals: zod.z.number().int().min(0).max(255),
31763
+ amount: zod.z.string().regex(HUMAN_READABLE_AMOUNT_REGEX$1)
31764
+ });
31765
+ /** @internal */ const healthFactorBandSchema = zod.z.enum([
31766
+ 'SAFE',
31767
+ 'WARN',
31768
+ 'URGENT',
31769
+ 'IMMINENT',
31770
+ 'LIQUIDATABLE'
31771
+ ]);
31772
+ /**
31773
+ * Whether a loan's economic projection has converged.
31774
+ *
31775
+ * `PENDING` means the loan's identity and lifecycle are known but its
31776
+ * economics are not yet materialized — every economic field is `null` until
31777
+ * the service's repair queue fills them in. See {@link loanSchema}.
31778
+ *
31779
+ * @internal
31780
+ */ const loanDataStatusSchema = zod.z.enum([
31781
+ 'READY',
31782
+ 'PENDING'
31783
+ ]);
31784
+ /**
31785
+ * Lifecycle status of a loan: still open (`active`) or fully wound down
31786
+ * (`closed`). Unlike {@link loanDataStatusSchema} this is not about whether
31787
+ * economics have converged — a `closed` loan is `READY` with every economic
31788
+ * field legitimately zeroed, not `null`. Shared by {@link loanSchema} and
31789
+ * {@link loanPayloadSchema}, which return the same field for the same entity.
31790
+ *
31791
+ * @internal
31792
+ */ const loanStatusSchema = zod.z.enum([
31793
+ 'active',
31794
+ 'closed'
31795
+ ]);
31796
+ /**
31797
+ * Zod schema for one loan in the `GET /v1/borrowKit/loans` page.
31798
+ *
31799
+ * @remarks Every economic field is nullable because the service returns a
31800
+ * `PENDING` loan — identity and lifecycle fields set, all economics `null` —
31801
+ * while materialization, principal, debt, or market APY is still converging.
31802
+ * That is the documented representation of a freshly-created loan, so
31803
+ * declaring these non-nullable made the first read after a `borrow()` fail
31804
+ * validation. `dataStatus` tells callers which of the two shapes they hold.
31805
+ *
31806
+ * `status` distinguishes a fully wound-down loan — `READY` with legitimately
31807
+ * zeroed economics — from a live one. Without it a caller cannot tell the two
31808
+ * apart, which is what left a closed loan sitting in `getLoans` output looking
31809
+ * open. It is declared **required here and on {@link loanPayloadSchema}**: a
31810
+ * live `GET /v1/borrowKit/loans` page reports it on every loan, including a
31811
+ * closed one.
31812
+ *
31813
+ * @internal
31814
+ */ const loanSchema = zod.z.object({
31815
+ loanId: zod.z.string().uuid(),
31816
+ chain: zod.z.string().min(1),
31817
+ marketId: zod.z.string().min(1),
31818
+ walletAddress: zod.z.string().regex(EVM_ADDRESS_REGEX),
31819
+ dataStatus: loanDataStatusSchema,
31820
+ collateral: borrowAssetAmountSchema.nullable(),
31821
+ borrowed: borrowAssetAmountSchema.nullable(),
31822
+ principalBorrowed: borrowAssetAmountSchema.nullable(),
31823
+ accruedInterest: borrowAssetAmountSchema.nullable(),
31824
+ borrowApy: zod.z.number().nullable(),
31825
+ ltv: zod.z.number().nullable(),
31826
+ healthFactor: zod.z.number().nullable(),
31827
+ healthFactorBand: healthFactorBandSchema.nullable(),
31828
+ liquidationPrice: borrowAssetAmountSchema.nullable(),
31829
+ status: loanStatusSchema
31830
+ });
31831
+ /**
31832
+ * Zod schema for the `GET /v1/borrowKit/loans` API response. The Borrow
31833
+ * Service wraps every response in a `data` envelope.
31834
+ *
31835
+ * @internal
31836
+ */ zod.z.object({
31837
+ data: zod.z.object({
31838
+ loans: zod.z.array(loanSchema),
31839
+ pagination: zod.z.object({
31840
+ pageAfter: zod.z.string().optional()
31841
+ })
31842
+ })
31843
+ });
31844
+ /** @internal */ const borrowFeeSchema = zod.z.object({
31845
+ type: zod.z.string(),
31846
+ token: zod.z.string(),
31847
+ amount: borrowAssetAmountSchema
31848
+ });
31849
+ /** @internal */ const borrowQuoteGasFeeSchema = zod.z.object({
31850
+ name: zod.z.string(),
31851
+ fees: zod.z.object({
31852
+ gas: zod.z.string(),
31853
+ gasPrice: borrowAssetAmountSchema,
31854
+ fee: borrowAssetAmountSchema
31855
+ })
31856
+ });
31857
+ /** @internal */ const hexBytesSchema = zod.z.string().regex(/^0x(?:[0-9a-fA-F]{2})*$/).transform((value)=>value);
31858
+ function isNonNegativeBigIntLike(value) {
31859
+ try {
31860
+ return BigInt(value) >= 0n;
31861
+ } catch {
31862
+ return false;
31863
+ }
31864
+ }
31865
+ /** @internal */ const uint256LikeSchema = zod.z.union([
31866
+ zod.z.string(),
31867
+ zod.z.bigint()
31868
+ ]).refine(isNonNegativeBigIntLike, {
31869
+ message: 'value must be a non-negative bigint-compatible string or bigint'
31870
+ });
31871
+ /**
31872
+ * A quoted decimal that has to parse finite, for the response fields the
31873
+ * service marshals from a Go `decimal.Decimal`.
31874
+ *
31875
+ * Deliberately not `z.coerce.number()`: coercion maps `''` and whitespace to
31876
+ * `0`, so a service that stopped sending a value would read as a real `0.0` —
31877
+ * a health factor of zero is a liquidated position, so that failure mode
31878
+ * silently reports the opposite of the truth. Refusing anything unparseable
31879
+ * here also makes the `decimalToNumber` mapper total, with no fallback value to
31880
+ * invent downstream.
31881
+ */ const decimalNumericString = zod.z.string().min(1).refine((value)=>value.trim() !== '' && Number.isFinite(Number(value)), {
31882
+ message: 'value must be a decimal string that parses to a finite number'
31883
+ });
31884
+ /** @internal */ const borrowExecutionParamsSchema = zod.z.object({
31885
+ instructions: zod.z.array(zod.z.object({
31886
+ target: evmAddressSchema$1,
31887
+ data: hexBytesSchema,
31888
+ value: uint256LikeSchema,
31889
+ tokenIn: evmAddressSchema$1,
31890
+ amountToApprove: uint256LikeSchema,
31891
+ tokenOut: evmAddressSchema$1,
31892
+ minTokenOut: uint256LikeSchema
31893
+ }).passthrough()).min(1),
31894
+ tokens: zod.z.array(zod.z.object({
31895
+ token: evmAddressSchema$1,
31896
+ beneficiary: evmAddressSchema$1
31897
+ }).passthrough()),
31898
+ execId: uint256LikeSchema,
31899
+ deadline: uint256LikeSchema,
31900
+ metadata: hexBytesSchema
31901
+ }).passthrough();
31902
+ /**
31903
+ * Inner payload for a Borrow Service execution response, shared by every
31904
+ * write endpoint (`borrow`, `repay`, `addCollateral`, `closeLoan`,
31905
+ * `withdrawCollateral`). Kept separate from {@link loanExecutionSchema} so
31906
+ * {@link withdrawCollateralExecutionSchema} — the one execution response that
31907
+ * omits `chain` — can build on it without re-declaring every field.
31908
+ *
31909
+ * @internal
31910
+ */ const loanExecutionPayloadSchema = zod.z.object({
31911
+ loanId: zod.z.string().uuid(),
31912
+ chain: zod.z.string().min(1),
31913
+ execId: zod.z.string().min(1),
31914
+ signature: evmSignatureSchema,
31915
+ executionParams: borrowExecutionParamsSchema
31916
+ });
31917
+ /**
31918
+ * Zod schema for a Borrow Service execution API response. The Borrow Service
31919
+ * wraps every response in a `data` envelope.
31920
+ *
31921
+ * @internal
31922
+ */ zod.z.object({
31923
+ data: loanExecutionPayloadSchema
31924
+ });
31925
+ /**
31926
+ * Zod schema for the `POST /v1/borrowKit/loans/borrow` execution API
31927
+ * response. Extends {@link loanExecutionPayloadSchema} with `fees`, the fee
31928
+ * legs the signed bundle charges at origination (empty when nothing is
31929
+ * charged) — a field only this endpoint's response carries.
31930
+ *
31931
+ * `fees` is optional here, and only here. The API spec documents it as always
31932
+ * present and `[]` when nothing is charged, but the deployed service omits the
31933
+ * key entirely; requiring it fails validation on a healthy `200` and turns
31934
+ * every borrow into a FATAL error. It is optional rather than defaulted
31935
+ * because the guard consuming this schema reports `safeParse().success` and
31936
+ * discards the parsed value, so a default would never reach a reader.
31937
+ *
31938
+ * The absence is preserved to the caller rather than normalised to `[]`:
31939
+ * `fetchBorrow` returns `response.data` unchanged and `BorrowResponse.fees` is
31940
+ * optional, so "the service named no legs" stays distinguishable from "nothing
31941
+ * was charged". `executeBorrow` is what resolves it, by reporting the fee
31942
+ * transfers the receipt paid out under `type: 'unknown'`.
31943
+ *
31944
+ * @internal
31945
+ */ zod.z.object({
31946
+ data: loanExecutionPayloadSchema.extend({
31947
+ fees: zod.z.array(borrowFeeSchema).optional()
31948
+ })
31949
+ });
31950
+ /** @internal */ const resultingHealthSchema = {
31951
+ resultingHealthFactor: zod.z.number().nullable(),
31952
+ resultingLtv: zod.z.number().nullable(),
31953
+ resultingBand: healthFactorBandSchema,
31954
+ liquidationPrice: borrowAssetAmountSchema.nullable()
31955
+ };
31956
+ /**
31957
+ * Zod schema for the `POST /v1/borrowKit/loans/:loan_id/repay/quote` API
31958
+ * response. The Borrow Service wraps every response in a `data` envelope.
31959
+ *
31960
+ * @internal
31961
+ */ zod.z.object({
31962
+ data: zod.z.object({
31963
+ chain: zod.z.string().min(1),
31964
+ repayAmount: borrowAssetAmountSchema,
31965
+ fees: zod.z.array(borrowFeeSchema),
31966
+ gasFees: zod.z.array(borrowQuoteGasFeeSchema),
31967
+ ...resultingHealthSchema
31968
+ })
31969
+ });
31970
+ // ---------------------------------------------------------------------------
31971
+ // Loan position response schema (SE-6383: getPosition)
31972
+ // ---------------------------------------------------------------------------
31973
+ /**
31974
+ * Zod schema for the inner loan position payload.
31975
+ *
31976
+ * @remarks Matches the real `GET /v1/borrowKit/loans/:loan_id` wire shape
31977
+ * (Zenith's `borrowkit.LoanResponse`). Two fields in the original placeholder
31978
+ * did not exist on that contract and made every lookup of a real loan fail
31979
+ * validation: the owner address is sent as `walletAddress`, not `wallet`, and
31980
+ * the service sends no liquidation threshold at all — liquidation LTV is a
31981
+ * market property, served by `GET /v1/borrowKit/markets/:chain/:market_id` as
31982
+ * `lltv`, not a per-loan one.
31983
+ *
31984
+ * `collateral` and `borrowed` are nullable for the same reason they are on
31985
+ * {@link loanSchema}: this route returns the same `PENDING` representation —
31986
+ * null economics — for a loan whose on-chain create has not been observed
31987
+ * yet, which is exactly the state a `borrow()` leaves behind. `dataStatus`
31988
+ * distinguishes that from a `READY` loan whose economics happen to be zero.
31989
+ *
31990
+ * Declares the same economic field set as {@link loanSchema}, because Zenith
31991
+ * serves both routes from one struct (`borrowkit.LoanResponse`): a loan read
31992
+ * one at a time reports exactly what the same loan reports inside a page, so a
31993
+ * caller never has to list a wallet to recover a field. Both declare `status`
31994
+ * as required: a live `GET /v1/borrowKit/loans` page reports it on every loan,
31995
+ * including a closed one.
31996
+ *
31997
+ * `registeredAt` is the sole field on the wire that neither schema declares.
31998
+ *
31999
+ * @internal
32000
+ */ const loanPayloadSchema = zod.z.object({
32001
+ loanId: zod.z.string(),
32002
+ walletAddress: zod.z.string(),
32003
+ chain: zod.z.string(),
32004
+ marketId: zod.z.string().min(1),
32005
+ dataStatus: loanDataStatusSchema,
32006
+ collateral: borrowAssetAmountSchema.nullable(),
32007
+ borrowed: borrowAssetAmountSchema.nullable(),
32008
+ principalBorrowed: borrowAssetAmountSchema.nullable(),
32009
+ accruedInterest: borrowAssetAmountSchema.nullable(),
32010
+ borrowApy: zod.z.number().nullable(),
32011
+ ltv: zod.z.number().nullable(),
32012
+ healthFactor: zod.z.number().nullable(),
32013
+ healthFactorBand: healthFactorBandSchema.nullable(),
32014
+ liquidationPrice: borrowAssetAmountSchema.nullable(),
32015
+ status: loanStatusSchema
32016
+ });
32017
+ /**
32018
+ * Zod schema for the `GET /v1/borrowKit/loans/:loan_id` API response.
32019
+ *
32020
+ * @internal
32021
+ */ zod.z.object({
32022
+ data: loanPayloadSchema
32023
+ });
32024
+ // ---------------------------------------------------------------------------
32025
+ // Inferred types
32026
+ // ---------------------------------------------------------------------------
32027
+ /**
32028
+ * Zod schema for the `POST /v1/borrowKit/loans/borrow/quote` API response.
32029
+ * The Borrow Service wraps every response in a `data` envelope.
32030
+ *
32031
+ * @internal
32032
+ */ zod.z.object({
32033
+ data: zod.z.object({
32034
+ chain: zod.z.string().min(1),
32035
+ collateralAmount: borrowAssetAmountSchema,
32036
+ loanAssetAmount: borrowAssetAmountSchema,
32037
+ // Nullable: the service reports null when the curated market row has no
32038
+ // cached rate yet, rather than a misleading 0%. The loans and markets
32039
+ // schemas already allow it; this one did not, so an unpriced market failed
32040
+ // validation and surfaced the whole quote as SERVICE_UNKNOWN_ERROR.
32041
+ borrowApy: zod.z.number().nullable(),
32042
+ fees: zod.z.array(borrowFeeSchema),
32043
+ gasFees: zod.z.array(borrowQuoteGasFeeSchema),
32044
+ ...resultingHealthSchema
32045
+ })
32046
+ });
32047
+ /**
32048
+ * Zod schema for the `POST /v1/borrowKit/loans/:loan_id/addCollateral/quote`
32049
+ * API response. The Borrow Service wraps every response in a `data` envelope.
32050
+ *
32051
+ * @internal
32052
+ */ zod.z.object({
32053
+ data: zod.z.object({
32054
+ chain: zod.z.string().min(1),
32055
+ collateralAmount: borrowAssetAmountSchema,
32056
+ fees: zod.z.array(borrowFeeSchema),
32057
+ gasFees: zod.z.array(borrowQuoteGasFeeSchema),
32058
+ ...resultingHealthSchema
32059
+ })
32060
+ });
32061
+ /**
32062
+ * Zod schema for the `POST /v1/borrowKit/loans/:loan_id/closeLoan/quote` API
32063
+ * response. The Borrow Service wraps every response in a `data` envelope.
32064
+ *
32065
+ * @internal
32066
+ */ zod.z.object({
32067
+ data: zod.z.object({
32068
+ chain: zod.z.string().min(1),
32069
+ repayAmount: borrowAssetAmountSchema,
32070
+ collateralAmount: borrowAssetAmountSchema,
32071
+ fees: zod.z.array(borrowFeeSchema),
32072
+ gasFees: zod.z.array(borrowQuoteGasFeeSchema),
32073
+ ...resultingHealthSchema
32074
+ })
32075
+ });
32076
+ /**
32077
+ * The collateral-withdrawal execution response is the one Borrow Service
32078
+ * execution body that omits `chain`, so it cannot reuse
32079
+ * {@link loanExecutionSchema} unchanged. Built on
32080
+ * {@link loanExecutionPayloadSchema} so the `data` envelope only needs to be
32081
+ * applied once.
32082
+ *
32083
+ * @internal
32084
+ */ zod.z.object({
32085
+ data: loanExecutionPayloadSchema.omit({
32086
+ chain: true
32087
+ })
32088
+ });
32089
+ /**
32090
+ * Zod schema for the
32091
+ * `POST /v1/borrowKit/loans/:loan_id/withdrawCollateral/quote` API response.
32092
+ * The Borrow Service wraps every response in a `data` envelope.
32093
+ *
32094
+ * @internal
32095
+ */ zod.z.object({
32096
+ data: zod.z.object({
32097
+ chain: zod.z.string().min(1),
32098
+ repayAmount: borrowAssetAmountSchema,
32099
+ collateralAmount: borrowAssetAmountSchema,
32100
+ fees: zod.z.array(borrowFeeSchema),
32101
+ gasFees: zod.z.array(borrowQuoteGasFeeSchema),
32102
+ ...resultingHealthSchema
32103
+ })
32104
+ });
32105
+ /**
32106
+ * Zod schema for the `POST /v1/borrowKit/loans/requiredCollateral/quote` API
32107
+ * response. The Borrow Service wraps every response in a `data` envelope.
32108
+ *
32109
+ * @remarks Deliberately not {@link resultingHealthSchema}. The two
32110
+ * market-scoped sizing quotes send four fields, not seven — no `resultingLtv`
32111
+ * and no `resultingBand` — so requiring the loan-scoped health block rejected
32112
+ * every well-formed response with `MALFORMED_RESPONSE`.
32113
+ *
32114
+ * `resultingHealthFactor` arrives as a decimal *string* on this route alone
32115
+ * (Zenith types it `decimal.Decimal`, which marshals quoted, where every other
32116
+ * route types it `*float64`). It is accepted as either encoding here and
32117
+ * narrowed to a `number` by the mapper — deliberately not via a Zod
32118
+ * `.transform`, because these schemas are consumed through boolean type guards
32119
+ * (`isRequiredCollateralQuote`), which discard a transform's output and would
32120
+ * leave the raw string flowing on while typed as a number.
32121
+ *
32122
+ * @internal
32123
+ */ zod.z.object({
32124
+ data: zod.z.object({
32125
+ chain: zod.z.string().min(1),
32126
+ requiredCollateral: borrowAssetAmountSchema,
32127
+ resultingHealthFactor: zod.z.union([
32128
+ zod.z.number(),
32129
+ decimalNumericString
32130
+ ]).nullable(),
32131
+ liquidationPrice: borrowAssetAmountSchema.nullable()
32132
+ })
32133
+ });
32134
+ /**
32135
+ * Zod schema for the `POST /v1/borrowKit/loans/maxBorrow/quote` API response.
32136
+ * The Borrow Service wraps every response in a `data` envelope.
32137
+ *
32138
+ * @remarks Four fields, not seven — see
32139
+ * {@link requiredCollateralQuoteSchema}. `resultingHealthFactor` is a plain
32140
+ * JSON number here (Zenith types it `*float64` on this route), so unlike its
32141
+ * inverse it needs no string coercion.
32142
+ *
32143
+ * @internal
32144
+ */ zod.z.object({
32145
+ data: zod.z.object({
32146
+ chain: zod.z.string().min(1),
32147
+ maxBorrowAmount: borrowAssetAmountSchema,
32148
+ resultingHealthFactor: zod.z.number().nullable(),
32149
+ liquidationPrice: borrowAssetAmountSchema.nullable()
32150
+ })
32151
+ });
32152
+ /**
32153
+ * Zod schema for the `GET`/`POST /v1/borrowKit/integrators/config` API
32154
+ * response. The Borrow Service wraps every response in a `data` envelope.
32155
+ *
32156
+ * @internal
32157
+ */ zod.z.object({
32158
+ data: zod.z.object({
32159
+ integratorId: zod.z.string().min(1),
32160
+ integratorFeeBps: zod.z.number().int().min(0),
32161
+ integratorFeeAddress: zod.z.string().regex(EVM_ADDRESS_REGEX).nullable(),
32162
+ createdAt: zod.z.string().datetime({
32163
+ offset: true
32164
+ }),
32165
+ updatedAt: zod.z.string().datetime({
32166
+ offset: true
32167
+ })
32168
+ })
32169
+ });
32170
+ /** @internal */ zod.z.object({
32171
+ config: borrowServiceConfigSchema$1.optional()
32172
+ }).strict();
32173
+ /**
32174
+ * Both fee fields are always required, so neither half is accepted alone.
32175
+ *
32176
+ * `integratorFeeAddress` may be `null` only when `integratorFeeBps` is 0, which
32177
+ * is how an integrator turns a fee off and clears the recipient. The reverse is
32178
+ * still allowed: a 0 rate can keep an address, which pauses the fee without
32179
+ * forgetting where it used to go. A rate above 0 with no recipient is unusable.
32180
+ *
32181
+ * The rule spans two fields, so it cannot live on either one and cannot be
32182
+ * expressed in the type. `{ integratorFeeBps: 25, integratorFeeAddress: null }`
32183
+ * type-checks and fails here.
32184
+ *
32185
+ * @internal
32186
+ */ zod.z.object({
32187
+ integratorFeeBps: zod.z.number().int().min(0).max(10_000),
32188
+ integratorFeeAddress: zod.z.string().regex(EVM_ADDRESS_REGEX).nullable(),
32189
+ config: borrowServiceConfigSchema$1.optional()
32190
+ }).strict().refine((params)=>params.integratorFeeAddress !== null || params.integratorFeeBps === 0, {
32191
+ message: 'integratorFeeAddress can only be null when integratorFeeBps is 0',
32192
+ path: [
32193
+ 'integratorFeeAddress'
32194
+ ]
32195
+ });
32196
+ /**
32197
+ * Zod schema for the webhook registration payload inside the API `data`
32198
+ * envelope, returned by
32199
+ * `POST /v1/borrowKit/loans/:loan_id/webhookRegistration`.
32200
+ *
32201
+ * @internal
32202
+ */ const webhookRegistrationPayloadSchema = zod.z.object({
32203
+ loanId: zod.z.string().uuid(),
32204
+ webhookUrl: zod.z.string().nullish()
32205
+ });
32206
+ /**
32207
+ * Zod schema validating the caller-supplied `loanId` and `webhookUrl` inputs
32208
+ * to `BorrowServiceProvider.registerWebhook` before they're signed and
32209
+ * submitted.
32210
+ *
32211
+ * `loanId` must be a UUID, consistent with every other loan-scoped schema in
32212
+ * this package (`repayQuoteServiceParamsSchema`, `loanSchema`,
32213
+ * `loanExecutionSchema`). `webhookUrl` must be `null` (an explicit "unset"
32214
+ * intent) or a syntactically valid **https** URL string — an empty string is
32215
+ * neither, so it's rejected here rather than silently reaching the `?? ''`
32216
+ * coercion that `buildWebhookRegistrationTypedData` applies to a genuine
32217
+ * `null`. The scheme is restricted to `https:` because this URL is where
32218
+ * loan-lifecycle callbacks get delivered and is cryptographically signed by
32219
+ * the loan owner: allowing `http:` would let a signed registration point at
32220
+ * a plaintext endpoint (or a link-local/loopback address reachable only from
32221
+ * the callback sender's own network), and the SDK is the natural place to
32222
+ * refuse that before it's ever signed or transmitted.
32223
+ *
32224
+ * @internal
32225
+ */ zod.z.object({
32226
+ loanId: zod.z.string().uuid(),
32227
+ webhookUrl: zod.z.string().url().refine((value)=>isHttpsUrl$1(value), 'webhookUrl must use the https scheme').nullable()
32228
+ }).strict();
32229
+ /**
32230
+ * Zod schema for the `POST /v1/borrowKit/loans/:loan_id/webhookRegistration`
32231
+ * API response.
32232
+ *
32233
+ * The Borrow Service API wraps the webhook registration payload in a `data`
32234
+ * envelope.
32235
+ *
32236
+ * @internal
32237
+ */ zod.z.object({
32238
+ data: webhookRegistrationPayloadSchema
32239
+ });
32240
+ /** @internal */ const marketAssetSchema = zod.z.object({
32241
+ symbol: zod.z.string().min(1),
32242
+ address: zod.z.string().regex(EVM_ADDRESS_REGEX),
32243
+ decimals: zod.z.number().int().nonnegative()
32244
+ });
32245
+ /**
32246
+ * Wire shape for a typed token amount: raw atoms as an integer string plus the
32247
+ * token's decimals, matching `@core/amounts` {@link AmountJSON}.
32248
+ *
32249
+ * @internal
32250
+ */ const amountJsonSchema = zod.z.object({
32251
+ raw: zod.z.string().refine(isNonNegativeBigIntLike, {
32252
+ message: 'raw must be a non-negative bigint-compatible string'
32253
+ }),
32254
+ decimals: zod.z.number().int().nonnegative()
32255
+ });
32256
+ /**
32257
+ * Wire shape for a single borrow market returned by
32258
+ * `GET /v1/borrowKit/markets/:chain/:market_id`. `chain` is the Borrow Service
32259
+ * API chain code (mapped back to the SDK chain by `toMarketInfo`); display
32260
+ * ratios arrive as numbers, token amounts as `{ raw, decimals }` objects.
32261
+ *
32262
+ * @remarks `lltv`, `borrowAssets`, `liquidity`, `borrowApy`, `utilization`,
32263
+ * and `refreshedAt` are all nullable because the service's economics are
32264
+ * cached projections, not live reads: a market that has been discovered but
32265
+ * not yet backfilled by the cache reports every economic field as `null`
32266
+ * rather than a placeholder value. An idle market keeps its last-known values
32267
+ * and old `refreshedAt` rather than reporting zeros, so `null` unambiguously
32268
+ * means "never backfilled." `borrowCap` is always `null` (Morpho Blue has no
32269
+ * per-market cap) but carried anyway so the field does not have to be added
32270
+ * later as a breaking change; it is also optional since the service may omit
32271
+ * it entirely.
32272
+ *
32273
+ * @internal
32274
+ */ const marketResponseSchema = zod.z.object({
32275
+ marketId: zod.z.string().min(1),
32276
+ protocol: zod.z.enum([
32277
+ 'morpho'
32278
+ ]),
32279
+ chain: zod.z.string().min(1),
32280
+ loanAsset: marketAssetSchema,
32281
+ collateralAsset: marketAssetSchema,
32282
+ lltv: zod.z.number().nonnegative().nullable(),
32283
+ borrowCap: amountJsonSchema.nullable().optional(),
32284
+ borrowAssets: amountJsonSchema.nullable(),
32285
+ liquidity: amountJsonSchema.nullable(),
32286
+ borrowApy: zod.z.number().nonnegative().nullable(),
32287
+ utilization: zod.z.number().nonnegative().nullable(),
32288
+ refreshedAt: zod.z.string().datetime({
32289
+ offset: true
32290
+ }).nullable()
32291
+ });
32292
+ /**
32293
+ * Zod schema for the `GET /v1/borrowKit/markets/:chain/:market_id` API
32294
+ * response. The Borrow Service wraps every response in a `data` envelope;
32295
+ * {@link marketResponseSchema} itself stays unwrapped since it is also reused
32296
+ * per-entry inside {@link listMarketsResponseSchema}.
32297
+ *
32298
+ * @internal
32299
+ */ zod.z.object({
32300
+ data: marketResponseSchema
32301
+ });
32302
+ /**
32303
+ * Wire shape for a page of borrow markets returned by
32304
+ * `GET /v1/borrowKit/markets`. Reuses {@link marketResponseSchema} per entry so
32305
+ * the per-market wire format stays defined in one place, and adds the
32306
+ * pagination envelope: an opaque `pageAfter` cursor absent on the last page.
32307
+ * The Borrow Service wraps every response in a `data` envelope.
32308
+ *
32309
+ * @internal
32310
+ */ zod.z.object({
32311
+ data: zod.z.object({
32312
+ markets: zod.z.array(marketResponseSchema),
32313
+ pagination: zod.z.object({
32314
+ pageAfter: zod.z.string().optional()
32315
+ })
32316
+ })
32317
+ });
32318
+
32319
+ /** @internal */ const BORROW_CHAIN_TO_API = {
32320
+ [Blockchain.Arc]: 'ARC',
32321
+ [Blockchain.Arc_Testnet]: 'ARC-TESTNET'
32322
+ };
32323
+
32324
+ Object.fromEntries(Object.entries(BORROW_CHAIN_TO_API).map(([sdkChain, apiChain])=>[
32325
+ apiChain,
32326
+ sdkChain
32327
+ ]));
32328
+
32329
+ /**
32330
+ * Schema for the Borrow Service configuration options.
32331
+ *
32332
+ * `apiKey` carries a Circle API key (`<ENV>_API_KEY:<keyId>:<keySecret>`) or a
32333
+ * legacy `KIT_KEY:<keyId>:<keySecret>` value. It is shape-checked only — as
32334
+ * with every other field here, the credential's format is the provider's call,
32335
+ * and `buildConfig` rejects a malformed one via `isValidApiKey`. The empty
32336
+ * string is allowed on purpose: `process.env.CIRCLE_API_KEY ?? ''` yields one
32337
+ * when the variable is unset, and the provider reads that as "no credential"
32338
+ * rather than as a malformed one.
32339
+ *
32340
+ * This schema is `.strict()`, so a field missing here is not merely unvalidated
32341
+ * — it is rejected outright, which is what made an `apiKey` config unusable
32342
+ * before it was listed.
32343
+ *
32344
+ * @internal
32345
+ */ const borrowServiceConfigSchema = zod.z.object({
32346
+ apiKey: zod.z.string().optional(),
32347
+ baseUrl: zod.z.string().url('baseUrl must be a valid URL').optional()
32348
+ }).strict();
32349
+ /**
32350
+ * Runtime schema for {@link GetLoansParams}.
32351
+ *
32352
+ * @example
32353
+ * ```typescript
32354
+ * import { getLoansParamsSchema } from '@circle-fin/borrow-kit'
32355
+ *
32356
+ * const result = getLoansParamsSchema.safeParse({
32357
+ * walletAddress: '0x1111111111111111111111111111111111111111',
32358
+ * chain: 'Arc_Testnet',
32359
+ * })
32360
+ * ```
32361
+ */ zod.z.object({
32362
+ walletAddress: evmAddressSchema$1,
32363
+ chain: chainIdentifierSchema,
32364
+ pageSize: zod.z.number().int().positive().optional(),
32365
+ pageAfter: zod.z.string().min(1).optional(),
32366
+ config: borrowServiceConfigSchema.optional()
32367
+ }).strict();
32368
+ /**
32369
+ * Runtime schema for {@link GetIntegratorConfigParams}.
32370
+ *
32371
+ * @example
32372
+ * ```typescript
32373
+ * import { getIntegratorConfigParamsSchema } from '@circle-fin/borrow-kit'
32374
+ *
32375
+ * const result = getIntegratorConfigParamsSchema.safeParse({
32376
+ * config: { apiKey: 'TEST_API_KEY:key-id:key-secret' },
32377
+ * })
32378
+ * ```
32379
+ */ zod.z.object({
32380
+ config: borrowServiceConfigSchema.optional()
32381
+ }).strict();
32382
+ /**
32383
+ * Runtime schema for {@link GetMarketParams}.
32384
+ *
32385
+ * @example
32386
+ * ```typescript
32387
+ * import { getMarketParamsSchema } from '@circle-fin/borrow-kit'
32388
+ *
32389
+ * const result = getMarketParamsSchema.safeParse({
32390
+ * chain: 'Arc_Testnet',
32391
+ * marketId: '0xabc...',
32392
+ * })
32393
+ * ```
32394
+ */ zod.z.object({
32395
+ chain: chainIdentifierSchema,
32396
+ marketId: zod.z.string().min(1),
32397
+ config: borrowServiceConfigSchema.optional()
32398
+ }).strict();
32399
+ /**
32400
+ * Runtime schema for {@link SetIntegratorConfigParams}.
32401
+ *
32402
+ * Both fee fields are required, so neither half is accepted alone.
32403
+ * `integratorFeeAddress` may be `null` only when `integratorFeeBps` is 0, which
32404
+ * turns the fee off and clears the recipient. A rate above 0 with no recipient
32405
+ * is unusable and is rejected.
32406
+ *
32407
+ * @example
32408
+ * ```typescript
32409
+ * import { setIntegratorConfigParamsSchema } from '@circle-fin/borrow-kit'
32410
+ *
32411
+ * const result = setIntegratorConfigParamsSchema.safeParse({
32412
+ * integratorFeeBps: 25,
32413
+ * integratorFeeAddress: '0x1111111111111111111111111111111111111111',
32414
+ * })
32415
+ *
32416
+ * // Turning the fee off and clearing the recipient.
32417
+ * const cleared = setIntegratorConfigParamsSchema.safeParse({
32418
+ * integratorFeeBps: 0,
32419
+ * integratorFeeAddress: null,
32420
+ * })
32421
+ * ```
32422
+ */ zod.z.object({
32423
+ integratorFeeBps: zod.z.number().int().min(0).max(10_000),
32424
+ integratorFeeAddress: evmAddressSchema$1.nullable(),
32425
+ config: borrowServiceConfigSchema.optional()
32426
+ }).strict().refine((params)=>params.integratorFeeAddress !== null || params.integratorFeeBps === 0, {
32427
+ message: 'integratorFeeAddress can only be null when integratorFeeBps is 0',
32428
+ path: [
32429
+ 'integratorFeeAddress'
32430
+ ]
32431
+ });
32432
+ /** @internal */ const HUMAN_READABLE_AMOUNT_REGEX = /^(?:0|[1-9]\d*)(?:\.\d+)?$/;
32433
+ /**
32434
+ * @param maxDecimals - Decimal places the amount may carry, or `null` for no
32435
+ * cap. USDC amounts take the default six; an amount denominated in a market's
32436
+ * own collateral token may be finer, so those pass `null`.
32437
+ * @internal
32438
+ */ function createPositiveAmountSchema(field, maxDecimals = 6) {
32439
+ const positive = zod.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`);
32440
+ if (maxDecimals === null) {
32441
+ return positive;
32442
+ }
32443
+ return positive.refine((value)=>(value.split('.')[1]?.length ?? 0) <= maxDecimals, `${field} must have at most ${maxDecimals.toString()} decimal places`);
32444
+ }
32445
+ /** @internal */ const positiveAmountSchema = createPositiveAmountSchema('repayAmount');
32446
+ const NIL_UUID = '00000000-0000-0000-0000-000000000000';
32447
+ const idempotencyKeySchema = zod.z.string().uuid().refine((value)=>value !== NIL_UUID, {
32448
+ message: 'idempotencyKey must not be the nil UUID; generate a fresh one per write'
32449
+ });
32450
+ /** @internal */ const adapterContextSchema = zod.z.object({
32451
+ // prepareAction is declared in the shape (rather than checked only via
32452
+ // .passthrough()) so its value is read off the input by property access:
32453
+ // .passthrough() only copies an input's own enumerable keys into its
32454
+ // output, which drops prototype methods like a class's prepareAction.
32455
+ adapter: adapterSchema.extend({
32456
+ prepareAction: zod.z.unknown()
32457
+ }).passthrough().refine((adapter)=>typeof adapter.prepareAction === 'function', 'adapter must expose prepareAction'),
32458
+ chain: chainIdentifierSchema,
32459
+ address: evmAddressSchema$1.optional()
32460
+ }).strict();
32461
+ /**
32462
+ * Shared `superRefine` enforcing the address/adapter-capability contract: a
32463
+ * developer-controlled adapter must supply `address` (the kit cannot resolve
32464
+ * it), and a user-controlled adapter must not (the adapter itself resolves
32465
+ * it — a caller-supplied value would silently take precedence, letting the
32466
+ * two disagree). Applied to every schema whose `from` shape carries an
32467
+ * adapter with a declared `addressContext` capability, so every Borrow Kit
32468
+ * write rejects a mismatched `from` the same way.
32469
+ *
32470
+ * @internal
32471
+ */ function refineAddressContext(from, context) {
32472
+ const addressContext = from.adapter.capabilities?.addressContext;
32473
+ if (addressContext === 'developer-controlled' && from.address === undefined) {
32474
+ context.addIssue({
32475
+ code: zod.z.ZodIssueCode.custom,
32476
+ message: 'address is required for developer-controlled adapters',
32477
+ path: [
32478
+ 'address'
32479
+ ]
32480
+ });
32481
+ }
32482
+ if (addressContext === 'user-controlled' && from.address !== undefined) {
32483
+ context.addIssue({
32484
+ code: zod.z.ZodIssueCode.custom,
32485
+ message: 'address must be resolved by user-controlled adapters',
32486
+ path: [
32487
+ 'address'
32488
+ ]
32489
+ });
32490
+ }
32491
+ }
32492
+ /** @internal */ const borrowExecutionFromSchema = zod.z.object({
32493
+ // prepareAction/validateChainSupport/capabilities are declared in the
32494
+ // shape (rather than checked only via .passthrough()) so their values are
32495
+ // read off the input by property access: .passthrough() only copies an
32496
+ // input's own enumerable keys into its output, which drops a prototype
32497
+ // method or getter like a class's prepareAction/validateChainSupport/
32498
+ // capabilities. capabilities is read below and by refineAddressContext.
32499
+ adapter: adapterSchema.extend({
32500
+ prepareAction: zod.z.unknown(),
32501
+ validateChainSupport: zod.z.unknown(),
32502
+ capabilities: zod.z.unknown()
32503
+ }).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'),
32504
+ chain: chainIdentifierSchema,
32505
+ address: evmAddressSchema$1.optional()
32506
+ }).strict().superRefine(refineAddressContext);
32507
+ /**
32508
+ * Runtime schema for {@link RepayQuoteParams}.
32509
+ */ const repayQuoteParamsSchema = zod.z.object({
32510
+ loanId: zod.z.string().uuid(),
32511
+ chain: chainIdentifierSchema,
32512
+ repayAmount: positiveAmountSchema,
32513
+ config: borrowServiceConfigSchema.optional()
32514
+ }).strict();
32515
+ /** Runtime schema for {@link RepayParams}. */ repayQuoteParamsSchema.omit({
32516
+ chain: true
32517
+ }).extend({
32518
+ idempotencyKey: idempotencyKeySchema.optional(),
32519
+ from: borrowExecutionFromSchema
32520
+ });
32521
+ /** Runtime schema for requesting a full-close quote. */ const closeLoanQuoteParamsSchema = zod.z.object({
32522
+ loanId: zod.z.string().uuid(),
32523
+ chain: chainIdentifierSchema,
32524
+ slippageBps: zod.z.number().int().min(0).max(10_000).optional(),
32525
+ config: borrowServiceConfigSchema.optional()
32526
+ }).strict();
32527
+ /** Runtime schema for atomically closing a loan. */ closeLoanQuoteParamsSchema.omit({
32528
+ chain: true
32529
+ }).extend({
32530
+ idempotencyKey: idempotencyKeySchema.optional(),
32531
+ from: borrowExecutionFromSchema
32532
+ });
32533
+ /**
32534
+ * Runtime schema for {@link GetPositionParams}.
32535
+ *
32536
+ * @example
32537
+ * ```typescript
32538
+ * import { getPositionParamsSchema } from '@circle-fin/borrow-kit'
32539
+ *
32540
+ * const result = getPositionParamsSchema.safeParse({
32541
+ * loanId: '11111111-1111-4111-8111-111111111111',
32542
+ * })
32543
+ * ```
32544
+ */ zod.z.object({
32545
+ loanId: zod.z.string().uuid(),
32546
+ config: borrowServiceConfigSchema.optional()
32547
+ }).strict();
32548
+ /**
32549
+ * Schema for the adapter context within Borrow operations.
32550
+ *
32551
+ * `.strict()` so a typo nested inside `from` fails fast instead of being
32552
+ * silently stripped. The address field is optional (resolved from the
32553
+ * adapter at runtime).
32554
+ *
32555
+ * @internal
32556
+ */ const borrowAdapterContextSchema = zod.z.object({
32557
+ // signTypedData/capabilities are declared in the shape (rather than
32558
+ // checked only via .passthrough()) so their values are read off the input
32559
+ // by property access: .passthrough() only copies an input's own
32560
+ // enumerable keys into its output, which drops a prototype method or
32561
+ // getter like a class's signTypedData/capabilities. capabilities is read
32562
+ // by refineAddressContext below.
32563
+ adapter: adapterSchema.extend({
32564
+ signTypedData: zod.z.unknown(),
32565
+ capabilities: zod.z.unknown()
32566
+ }).passthrough().refine(// supportsSignTypedData is optional on SignTypedDataAdapter (see
32567
+ // @core/adapter-evm's hasSignTypedData/canSignTypedData): only
32568
+ // signTypedData itself is required here.
32569
+ (adapter)=>typeof adapter.signTypedData === 'function', 'adapter must support signTypedData'),
32570
+ // BorrowAdapterContext accepts a wider chain field, but runtime
32571
+ // validation must stay constrained to chains where loans exist. No cast
32572
+ // needed here: inferring straight from borrowChainIdentifierSchema keeps
32573
+ // this schema's exported type accurate (only the Borrow chain set),
32574
+ // unlike the previous `as unknown as typeof chainIdentifierSchema` cast,
32575
+ // which over-widened it to the full ChainIdentifier union.
32576
+ chain: borrowChainIdentifierSchema,
32577
+ address: zod.z.string().min(1, 'address must not be empty').optional()
32578
+ }).strict()// Same address/adapter-capability contract every other Borrow Kit write
32579
+ // enforces via borrowExecutionFromSchema: a developer-controlled adapter
32580
+ // without `address` fails fast with a named message instead of falling
32581
+ // through to adapter.getAddress(); a user-controlled adapter with `address`
32582
+ // set is rejected instead of silently taking precedence.
32583
+ .superRefine(refineAddressContext);
32584
+ /** @internal */ const borrowParamsBaseSchema = zod.z.object({
32585
+ from: adapterContextSchema,
32586
+ borrowAmount: createPositiveAmountSchema('borrowAmount'),
32587
+ slippageBps: zod.z.number().int().min(0).max(10_000).optional(),
32588
+ idempotencyKey: idempotencyKeySchema.optional(),
32589
+ config: borrowServiceConfigSchema.optional()
32590
+ });
32591
+ /**
32592
+ * Runtime schema for {@link BorrowParams}.
32593
+ *
32594
+ * One strict object with a refinement, rather than a union of a market branch
32595
+ * and a loan branch: a union that fails every branch reports one `invalid_union`
32596
+ * issue, which costs the caller the field that actually failed. The refinement
32597
+ * states the rule it enforces, and every other field keeps naming itself.
32598
+ */ borrowParamsBaseSchema.extend({
32599
+ marketId: zod.z.string().min(1).optional(),
32600
+ loanId: zod.z.string().uuid().optional()
32601
+ }).strict().refine((params)=>params.marketId === undefined !== (params.loanId === undefined), {
32602
+ message: 'Provide exactly one of marketId or loanId'
32603
+ });
32604
+ /**
32605
+ * Schema for the adapter context within reward-claiming operations.
32606
+ *
32607
+ * Validate that a properly-shaped adapter instance and a valid chain
32608
+ * identifier are provided. The address field is optional at the schema
32609
+ * level (resolved from the adapter at runtime for user-controlled
32610
+ * adapters, required for developer-controlled ones — that distinction is
32611
+ * enforced by {@link BorrowAdapterContext}'s conditional `AddressField`,
32612
+ * not by this runtime schema). `.strict()` so a typo nested inside `from`
32613
+ * fails fast instead of being silently stripped.
32614
+ *
32615
+ * Reward claiming is restricted to the Borrow chain identifiers (Arc and
32616
+ * Arc Testnet), which is narrower than the general
32617
+ * {@link chainIdentifierSchema} `repay`/`borrow` accept.
32618
+ *
32619
+ * @internal
32620
+ */ const claimRewardsAdapterContextSchema = zod.z.object({
32621
+ adapter: adapterSchema,
32622
+ chain: borrowChainIdentifierSchema,
32623
+ address: evmAddressSchema$1.optional()
32624
+ }).strict()// Same address/adapter-capability contract every other Borrow Kit write
32625
+ // enforces via borrowExecutionFromSchema: a developer-controlled adapter
32626
+ // without `address` fails fast with a named message instead of falling
32627
+ // through to adapter.getAddress(); a user-controlled adapter with `address`
32628
+ // set is rejected instead of silently taking precedence.
32629
+ .superRefine(refineAddressContext);
32630
+ /** @internal */ const borrowPreviewParamsBaseSchema = zod.z.object({
32631
+ chain: chainIdentifierSchema,
32632
+ marketId: zod.z.string().min(1),
32633
+ config: borrowServiceConfigSchema.optional()
32634
+ });
32635
+ /** Runtime schema for a first-open borrow quote. @internal */ const openLoanQuoteParamsSchema = borrowPreviewParamsBaseSchema.extend({
32636
+ walletAddress: evmAddressSchema$1,
32637
+ borrowAmount: createPositiveAmountSchema('borrowAmount'),
32638
+ slippageBps: zod.z.number().int().min(0).max(10_000).optional()
32639
+ }).strict();
32640
+ /** Runtime schema for an existing-loan borrow quote. @internal */ const borrowMoreQuoteParamsSchema = zod.z.object({
32641
+ loanId: zod.z.string().uuid(),
32642
+ borrowAmount: createPositiveAmountSchema('borrowAmount'),
32643
+ slippageBps: zod.z.number().int().min(0).max(10_000).optional(),
32644
+ config: borrowServiceConfigSchema.optional()
32645
+ }).strict();
32646
+ /** Runtime schema for {@link BorrowQuoteParams}. */ zod.z.union([
32647
+ openLoanQuoteParamsSchema,
32648
+ borrowMoreQuoteParamsSchema
32649
+ ]);
32650
+ /** Runtime schema for {@link RequiredCollateralParams}. */ borrowPreviewParamsBaseSchema.extend({
32651
+ borrowAmount: createPositiveAmountSchema('borrowAmount'),
32652
+ targetHealthFactor: zod.z.number().positive().finite()
32653
+ }).strict();
32654
+ // Uncapped: repayments are USDC, but collateral is the market's own token and
32655
+ // may hold more precision than USDC's six places.
32656
+ /** @internal */ const collateralAmountSchema = createPositiveAmountSchema('collateralAmount', null);
32657
+ /**
32658
+ * Validation schema for ClaimRewardsParams.
32659
+ *
32660
+ * @example
32661
+ * ```typescript
32662
+ * import { claimRewardsParamsSchema } from '@circle-fin/borrow-kit'
32663
+ *
32664
+ * const result = claimRewardsParamsSchema.safeParse({
32665
+ * from: { adapter, chain: 'Arc_Testnet' },
32666
+ * })
32667
+ * ```
32668
+ */ zod.z.object({
32669
+ from: claimRewardsAdapterContextSchema,
32670
+ config: borrowServiceConfigSchema.optional()
32671
+ }).strict();
32672
+ /**
32673
+ * Validation schema for GetClaimRewardsQuoteParams.
32674
+ *
32675
+ * @example
32676
+ * ```typescript
32677
+ * import { getClaimRewardsQuoteParamsSchema } from '@circle-fin/borrow-kit'
32678
+ *
32679
+ * const result = getClaimRewardsQuoteParamsSchema.safeParse({
32680
+ * from: { adapter, chain: 'Arc_Testnet' },
32681
+ * })
32682
+ * ```
32683
+ */ zod.z.object({
32684
+ from: claimRewardsAdapterContextSchema,
32685
+ config: borrowServiceConfigSchema.optional()
32686
+ }).strict();
32687
+ /** @internal */ const marketSortBySchema = zod.z.enum([
32688
+ 'lltv',
32689
+ 'borrowApy'
32690
+ ]);
32691
+ /**
32692
+ * Build a schema for an optional non-negative decimal filter string.
32693
+ *
32694
+ * Accepts values like `'0'`, `'100'`, `'0.86'`; rejects negatives, leading
32695
+ * dots, and non-numeric input so a malformed filter fails fast.
32696
+ *
32697
+ * @param field - The filter field name, used in the rejection message.
32698
+ * @returns A Zod string schema constrained to a non-negative decimal form.
32699
+ * @internal
32700
+ */ function decimalFilterSchema(field) {
32701
+ return zod.z.string().regex(/^\d+(\.\d+)?$/, `${field} must be a non-negative decimal string`);
32702
+ }
32703
+ const exploreMarketsParamsBase = zod.z.object({
32704
+ chain: chainIdentifierSchema,
32705
+ minLltv: decimalFilterSchema('minLltv').optional(),
32706
+ maxLltv: decimalFilterSchema('maxLltv').optional(),
32707
+ minBorrow: decimalFilterSchema('minBorrow').optional(),
32708
+ maxBorrow: decimalFilterSchema('maxBorrow').optional(),
32709
+ sortBy: marketSortBySchema.optional(),
32710
+ // Provisional bounds: the backend has not published a pageSize range, so
32711
+ // reject only implausible values rather than plausible ones.
32712
+ pageSize: zod.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(),
32713
+ pageAfter: zod.z.string().min(1, 'pageAfter must not be empty').optional(),
32714
+ config: borrowServiceConfigSchema.optional()
32715
+ }).strict();
32716
+ /**
32717
+ * Runtime schema for {@link MaxBorrowParams}.
32718
+ *
32719
+ * The collateral amount is not held to the loan asset's six decimal places: it
32720
+ * denominates the market's collateral token, whose precision may be finer.
32721
+ */ borrowPreviewParamsBaseSchema.extend({
32722
+ collateralAmount: collateralAmountSchema
32723
+ }).strict();
32724
+ /** Runtime schema for {@link WithdrawCollateralQuoteParams}. */ const withdrawCollateralQuoteParamsSchema = zod.z.object({
32725
+ loanId: zod.z.string().uuid(),
32726
+ chain: chainIdentifierSchema,
32727
+ collateralAmount: collateralAmountSchema,
32728
+ slippageBps: zod.z.number().int().min(0).max(10_000).optional(),
32729
+ config: borrowServiceConfigSchema.optional()
32730
+ }).strict();
32731
+ /**
32732
+ * Maximum accepted signature deadline: 300 seconds from now, matching the
32733
+ * Borrow Service's own hard limit on `WebhookRegistration` deadlines. A wider
32734
+ * SDK-side window would let a caller sign a deadline the service always
32735
+ * rejects.
32736
+ *
32737
+ * @internal
32738
+ */ const MAX_DEADLINE_SKEW_SECONDS = 300;
32739
+ /**
32740
+ * True when `value` parses as a URL whose scheme is exactly `https:`.
32741
+ *
32742
+ * Uses the WHATWG `URL` parser rather than `startsWith('https://')` so the
32743
+ * check is scheme-normalized (case-insensitive, e.g. `HTTPS://...` still
32744
+ * passes) instead of a literal prefix match. `value` has already passed
32745
+ * `z.string().url()` by the time this runs, but a caller could still supply
32746
+ * a string that `URL` itself rejects, so the parse is guarded.
32747
+ *
32748
+ * @internal
32749
+ */ function isHttpsUrl(value) {
32750
+ try {
32751
+ return new URL(value).protocol === 'https:';
32752
+ } catch {
32753
+ return false;
32754
+ }
32755
+ }
32756
+ /**
32757
+ * Validation schema for RegisterWebhookParams.
32758
+ *
32759
+ * @example
32760
+ * ```typescript
32761
+ * import { registerWebhookParamsSchema } from '@circle-fin/borrow-kit'
32762
+ *
32763
+ * const result = registerWebhookParamsSchema.safeParse({
32764
+ * from: { adapter, chain: 'Arc_Testnet' },
32765
+ * loanId: '11111111-1111-4111-8111-111111111111',
32766
+ * webhookUrl: 'https://example.com/webhook',
32767
+ * })
32768
+ * ```
32769
+ */ zod.z.object({
32770
+ from: borrowAdapterContextSchema,
32771
+ // loanId must be a UUID, consistent with every other loan-scoped schema
32772
+ // in this package (repayQuoteParamsSchema, closeLoanQuoteParamsSchema,
32773
+ // borrowParamsSchema) and with the provider-level
32774
+ // registerWebhookInputSchema, which already enforces this.
32775
+ loanId: zod.z.string().uuid(),
32776
+ // Restricted to https: for the same reason as the provider-level
32777
+ // registerWebhookInputSchema — this URL receives a cryptographically
32778
+ // signed, owner-authorized registration, and http:// would let that
32779
+ // signed intent point at a plaintext or link-local/loopback endpoint.
32780
+ webhookUrl: zod.z.string().url('webhookUrl must be a valid URL').refine((value)=>isHttpsUrl(value), 'webhookUrl must use the https scheme').nullable().optional(),
32781
+ deadline: zod.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(),
32782
+ config: borrowServiceConfigSchema.optional()
32783
+ }).strict();
32784
+ /**
32785
+ * Reject a filter range whose lower bound exceeds its upper bound.
32786
+ *
32787
+ * Each bound is validated independently, so `{ minLltv: '0.9', maxLltv: '0.1' }`
32788
+ * would otherwise pass and return a silently empty page; comparing the bounds
32789
+ * numerically turns that contradiction into a clear validation error. Shared by
32790
+ * the single-page and iterator schemas so the two cannot drift.
32791
+ *
32792
+ * @param value - The parsed explore filters.
32793
+ * @param ctx - The Zod refinement context that collects any range issues.
32794
+ * @internal
32795
+ */ function refineFilterRanges(value, ctx) {
32796
+ if (value.minLltv !== undefined && value.maxLltv !== undefined && Number(value.minLltv) > Number(value.maxLltv)) {
32797
+ ctx.addIssue({
32798
+ code: zod.z.ZodIssueCode.custom,
32799
+ path: [
32800
+ 'maxLltv'
32801
+ ],
32802
+ message: 'maxLltv must be greater than or equal to minLltv'
32803
+ });
32804
+ }
32805
+ if (value.minBorrow !== undefined && value.maxBorrow !== undefined && Number(value.minBorrow) > Number(value.maxBorrow)) {
32806
+ ctx.addIssue({
32807
+ code: zod.z.ZodIssueCode.custom,
32808
+ path: [
32809
+ 'maxBorrow'
32810
+ ],
32811
+ message: 'maxBorrow must be greater than or equal to minBorrow'
32812
+ });
32813
+ }
32814
+ }
32815
+ /**
32816
+ * Runtime schema for {@link ExploreMarketsParams}.
32817
+ *
32818
+ * `chain` is required; every filter is optional. `pageAfter` is an opaque
32819
+ * non-empty cursor and `pageSize` a positive integer (provisional upper bound
32820
+ * 1000, pending backend confirmation). Unknown keys are rejected, and a range
32821
+ * whose minimum exceeds its maximum is rejected rather than returning a
32822
+ * silently empty page.
32823
+ *
32824
+ * @example
32825
+ * ```typescript
32826
+ * import { exploreMarketsParamsSchema } from '@circle-fin/borrow-kit'
32827
+ *
32828
+ * const result = exploreMarketsParamsSchema.safeParse({
32829
+ * chain: 'Arc_Testnet',
32830
+ * minLltv: '0.8',
32831
+ * sortBy: 'borrowApy',
32832
+ * })
32833
+ * ```
32834
+ */ exploreMarketsParamsBase.superRefine(refineFilterRanges);
32835
+ /**
32836
+ * Runtime schema for {@link ExploreMarketsIteratorParams}.
32837
+ *
32838
+ * Identical to {@link exploreMarketsParamsSchema} minus `pageAfter`: the
32839
+ * iterator owns the cursor and advances through pages itself.
32840
+ *
32841
+ * @example
32842
+ * ```typescript
32843
+ * import { exploreMarketsIteratorParamsSchema } from '@circle-fin/borrow-kit'
32844
+ *
32845
+ * const result = exploreMarketsIteratorParamsSchema.safeParse({
32846
+ * chain: 'Arc_Testnet',
32847
+ * sortBy: 'borrowApy',
32848
+ * })
32849
+ * ```
32850
+ */ exploreMarketsParamsBase.omit({
32851
+ pageAfter: true
32852
+ }).superRefine(refineFilterRanges);
32853
+ /** Runtime schema for {@link WithdrawCollateralParams}. */ withdrawCollateralQuoteParamsSchema.omit({
32854
+ chain: true
32855
+ }).extend({
32856
+ idempotencyKey: idempotencyKeySchema.optional(),
32857
+ from: borrowExecutionFromSchema
32858
+ });
32859
+ /** Runtime schema for {@link AddCollateralQuoteParams}. */ const addCollateralQuoteParamsSchema = zod.z.object({
32860
+ loanId: zod.z.string().uuid(),
32861
+ chain: chainIdentifierSchema,
32862
+ collateralAmount: collateralAmountSchema,
32863
+ config: borrowServiceConfigSchema.optional()
32864
+ }).strict();
32865
+ /** Runtime schema for {@link AddCollateralParams}. */ addCollateralQuoteParamsSchema.omit({
32866
+ chain: true
32867
+ }).extend({
32868
+ idempotencyKey: idempotencyKeySchema.optional(),
32869
+ from: borrowExecutionFromSchema
32870
+ });
32871
+
32872
+ /** SDK name used in telemetry payloads. */ resolveKitSdkName(pkg.name);
32873
+
32874
+ // Auto-register this kit for user agent tracking
32875
+ registerKit(`${pkg.name}/${pkg.version}`);
32876
+
31121
32877
  async function bridge(context, params) {
31122
32878
  const kit = createBridgeKit(context);
31123
32879
  registerActionHandlers(kit, context.actions.bridge, 'bridge');