@circle-fin/app-kit 1.9.0 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bridge.cjs CHANGED
@@ -18,13 +18,24 @@
18
18
 
19
19
  'use strict';
20
20
 
21
+ // Buffer polyfill setup - executes before any other code
22
+ // Ensures globalThis.Buffer is available for Solana libraries
23
+ const { Buffer } = require('buffer');
24
+ if (typeof globalThis !== 'undefined' && typeof globalThis.Buffer === 'undefined') {
25
+ globalThis.Buffer = Buffer;
26
+ }
27
+ if (typeof window !== 'undefined' && typeof window.Buffer === 'undefined') {
28
+ window.Buffer = Buffer;
29
+ }
30
+
31
+
21
32
  var zod = require('zod');
22
33
  var pino = require('pino');
23
34
  var units = require('@ethersproject/units');
24
35
  var bytes = require('@ethersproject/bytes');
36
+ require('@ethersproject/abi');
25
37
  var address = require('@ethersproject/address');
26
38
  var bs58 = require('bs58');
27
- require('@ethersproject/abi');
28
39
  var web3_js = require('@solana/web3.js');
29
40
  require('bn.js');
30
41
  require('@coral-xyz/anchor');
@@ -51,6 +62,27 @@ var bs58__default = /*#__PURE__*/_interopDefault(bs58);
51
62
  * }
52
63
  * ```
53
64
  */ const isNodeEnvironment = ()=>typeof process !== 'undefined' && typeof process.versions === 'object' && typeof process.versions.node === 'string';
65
+ /**
66
+ * Return the SDK User-Agent request header only when running in Node.js.
67
+ *
68
+ * Browsers forbid manually setting `User-Agent`, and a custom fallback header
69
+ * can trigger CORS preflight. Non-Node server runtimes also omit this optional
70
+ * attribution header because they cannot set it reliably.
71
+ *
72
+ * @returns A User-Agent header in Node.js, or an empty object otherwise.
73
+ *
74
+ * @example
75
+ * ```typescript
76
+ * import { getNodeUserAgentHeader } from '@core/utils'
77
+ *
78
+ * const headers = {
79
+ * 'Content-Type': 'application/json',
80
+ * ...getNodeUserAgentHeader(),
81
+ * }
82
+ * ```
83
+ */ const getNodeUserAgentHeader = ()=>isNodeEnvironment() ? {
84
+ 'User-Agent': getUserAgent()
85
+ } : {};
54
86
  /**
55
87
  * Detect the runtime environment and return a shortened identifier.
56
88
  *
@@ -3101,7 +3133,10 @@ var EarnChain;
3101
3133
  contracts: {
3102
3134
  v1: {
3103
3135
  wallet: GATEWAY_WALLET_EVM_TESTNET,
3104
- minter: GATEWAY_MINTER_EVM_TESTNET
3136
+ minter: GATEWAY_MINTER_EVM_TESTNET,
3137
+ // DepositForHandler the GenericExecutor calls to run a fast cross-chain
3138
+ // deposit into the GatewayWallet above.
3139
+ depositForHandler: '0xD05E7D2E7d30b92c5F17d7d0fC575fce231F1A48'
3105
3140
  }
3106
3141
  },
3107
3142
  forwarderSupported: {
@@ -6219,7 +6254,10 @@ var Chains = {
6219
6254
  minter: zod.z.string({
6220
6255
  required_error: 'Gateway minter address is required. Please provide a valid contract address.',
6221
6256
  invalid_type_error: 'Gateway minter address must be a string.'
6222
- }).min(1, 'Gateway minter address cannot be empty.')
6257
+ }).min(1, 'Gateway minter address cannot be empty.'),
6258
+ depositForHandler: zod.z.string({
6259
+ invalid_type_error: 'Gateway depositForHandler address must be a string.'
6260
+ }).min(1, 'Gateway depositForHandler address cannot be empty.').optional()
6223
6261
  }).strict() // Reject any additional properties not defined in the schema
6224
6262
  ;
6225
6263
  /**
@@ -6741,21 +6779,31 @@ const swapTokenEnumSchema = zod.z.enum([
6741
6779
  * returning the appropriate address based on the requested contract type.
6742
6780
  *
6743
6781
  * @param chain - The chain definition to resolve the contract address for
6744
- * @param contractType - The type of contract address to resolve ('tokenMessenger' or 'messageTransmitter')
6782
+ * @param contractType - The type of contract address to resolve ('tokenMessenger', 'messageTransmitter', or 'tokenMessengerWithFees')
6745
6783
  * @returns The contract address for the specified contract type
6746
6784
  * @throws Error when chain does not support CCTP v2 or has unsupported contract configuration
6785
+ * @throws Error when 'tokenMessengerWithFees' is requested but not configured on the chain
6747
6786
  */ const resolveCCTPV2ContractAddress = (chain, contractType)=>{
6748
6787
  // Handle custom bridge contract for tokenMessenger (burn transaction)
6749
- if (hasCustomContractSupport(chain, 'bridge') && chain.kitContracts?.bridge !== undefined) {
6788
+ if (contractType === 'tokenMessenger' && hasCustomContractSupport(chain, 'bridge') && chain.kitContracts?.bridge !== undefined) {
6750
6789
  return chain.kitContracts.bridge;
6751
6790
  }
6752
6791
  // At this point we know CCTP v2 is supported, so contracts exist
6753
6792
  const cctpConfig = chain.cctp;
6754
6793
  const contracts = cctpConfig.contracts.v2;
6794
+ // The `TokenMessengerWithFees` wrapper (prepaid FORWARD path) is an optional
6795
+ // deployment carried alongside both split and merged configurations.
6796
+ if (contractType === 'tokenMessengerWithFees') {
6797
+ const wrapper = contracts.tokenMessengerWithFees;
6798
+ if (wrapper === undefined || wrapper === '') {
6799
+ throw new Error(`TokenMessengerWithFees is not configured on chain ${chain.name}. The prepaid FORWARD path is unavailable on this chain.`);
6800
+ }
6801
+ return wrapper;
6802
+ }
6755
6803
  // Handle different contract types with explicit type checking
6756
6804
  switch(contracts.type){
6757
6805
  case 'split':
6758
- return contracts.tokenMessenger ;
6806
+ return contractType === 'tokenMessenger' ? contracts.tokenMessenger : contracts.messageTransmitter;
6759
6807
  case 'merged':
6760
6808
  return contracts.contract;
6761
6809
  default:
@@ -7461,13 +7509,12 @@ const swapTokenEnumSchema = zod.z.enum([
7461
7509
  headers: {
7462
7510
  ...DEFAULT_CONFIG$1.headers,
7463
7511
  ...config.headers ?? {},
7464
- // In browser environments, directly setting the 'User-Agent' or similar headers is restricted and may be ignored or cause errors.
7465
- // This is why we use the 'X-User-Agent' header instead.
7466
- ...typeof window === 'undefined' ? {
7467
- 'User-Agent': getUserAgent()
7468
- } : {
7469
- 'X-User-Agent': getUserAgent()
7470
- }
7512
+ // Browsers forbid setting a user-agent request header, and the custom
7513
+ // fallback header the SDK used instead trips CORS preflight against the
7514
+ // Circle APIs (it isn't in their `Access-Control-Allow-Headers`),
7515
+ // blocking the request. So send the SDK user agent only in Node;
7516
+ // browsers omit it entirely.
7517
+ ...getNodeUserAgentHeader()
7471
7518
  }
7472
7519
  };
7473
7520
  let lastError;
@@ -9253,6 +9300,7 @@ function resolveOptions(options) {
9253
9300
  if (payload.tokenIn !== undefined) safe['tokenIn'] = payload.tokenIn;
9254
9301
  if (payload.tokenOut !== undefined) safe['tokenOut'] = payload.tokenOut;
9255
9302
  if (payload.txHash !== undefined) safe['txHash'] = payload.txHash;
9303
+ if (payload.correlationId !== undefined) safe['correlationId'] = payload.correlationId;
9256
9304
  if (payload.errorDetails !== undefined) {
9257
9305
  const errorDetails = {
9258
9306
  ...payload.errorDetails.errorCode !== undefined && {
@@ -9323,18 +9371,15 @@ function resolveOptions(options) {
9323
9371
  timeoutHandle.unref();
9324
9372
  }
9325
9373
  try {
9326
- const isNode = isNodeEnvironment();
9327
- const userAgent = getUserAgent();
9328
9374
  await fetch(getLogsUrl(), {
9329
9375
  method: 'POST',
9330
9376
  headers: {
9331
9377
  'Content-Type': 'application/json',
9332
- // Browser restricts setting User-Agent; use X-User-Agent instead.
9333
- ...isNode ? {
9334
- 'User-Agent': userAgent
9335
- } : {
9336
- 'X-User-Agent': userAgent
9337
- }
9378
+ // Browsers forbid setting a user-agent request header, and the custom
9379
+ // fallback header the SDK used instead trips CORS preflight (it isn't
9380
+ // in the telemetry endpoint's `Access-Control-Allow-Headers`), so send
9381
+ // it only in Node; browsers omit it entirely.
9382
+ ...getNodeUserAgentHeader()
9338
9383
  },
9339
9384
  body: JSON.stringify(toSafePayload(payload)),
9340
9385
  signal: controller.signal
@@ -9465,7 +9510,7 @@ function resolveOptions(options) {
9465
9510
  // discards the stack trace, nested `cause`, and any custom Error
9466
9511
  // properties — exactly the context an on-call needs when a
9467
9512
  // resolver-closure regression triggers this path.
9468
- console.warn(`[stablecoin-kits telemetry] dropped error event '${eventType}':`, cause);
9513
+ console.warn(`[stablecoin-kits telemetry] dropped event '${eventType}':`, cause);
9469
9514
  } catch {
9470
9515
  // console.warn itself throwing is the user's environment; nothing more we
9471
9516
  // can do without risking the original operation error.
@@ -9481,7 +9526,9 @@ function resolveOptions(options) {
9481
9526
  sdkVersion: config.sdkVersion,
9482
9527
  eventType,
9483
9528
  timestamp: new Date().toISOString(),
9484
- errorDetails,
9529
+ ...errorDetails !== undefined && {
9530
+ errorDetails
9531
+ },
9485
9532
  clientContext: buildClientContext(),
9486
9533
  ...context?.sourceChain != null && {
9487
9534
  sourceChain: context.sourceChain
@@ -9497,6 +9544,9 @@ function resolveOptions(options) {
9497
9544
  },
9498
9545
  ...context?.txHash != null && {
9499
9546
  txHash: context.txHash
9547
+ },
9548
+ ...context?.correlationId != null && {
9549
+ correlationId: context.correlationId
9500
9550
  }
9501
9551
  };
9502
9552
  }
@@ -9610,7 +9660,7 @@ function resolveOptions(options) {
9610
9660
  }
9611
9661
 
9612
9662
  var name$2 = "@circle-fin/bridge-kit";
9613
- var version$3 = "1.12.0";
9663
+ var version$3 = "1.12.2";
9614
9664
  var pkg$3 = {
9615
9665
  name: name$2,
9616
9666
  version: version$3};
@@ -12114,6 +12164,150 @@ const FAST_TIER_FINALITY_THRESHOLD = 1000;
12114
12164
  return false;
12115
12165
  };
12116
12166
 
12167
+ /**
12168
+ * The zero address, denoting a native-currency fee in a signed quote.
12169
+ */ const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
12170
+ /**
12171
+ * Resolve the fee payment channel for a prepaid-FORWARD burn via `TokenMessengerWithFees`.
12172
+ *
12173
+ * Determines the native `msg.value` and the ERC-20 approvals required, honouring
12174
+ * the quote's `feeToken`:
12175
+ * - Native fee (`feeToken` is the zero address): attach exactly `feeTotalAmount`
12176
+ * as `msg.value`; approve only the burn amount.
12177
+ * - ERC-20 fee equal to the burn token (both USDC — the `isBurnTokenFee` case):
12178
+ * approve a single combined `amount + feeTotalAmount` and skip the redundant
12179
+ * second approval.
12180
+ * - ERC-20 fee different from the burn token: approve the burn amount and the fee
12181
+ * amount separately.
12182
+ *
12183
+ * This encodes only balance/allowance intent; it does not fetch balances. The
12184
+ * caller is responsible for a balance preflight against the fresh quote.
12185
+ *
12186
+ * @param params - The fee token, burn token, burn amount, and total fee amount.
12187
+ * @returns The resolved fee payment plan.
12188
+ * @throws KitError if `amount` or `feeTotalAmount` is negative.
12189
+ *
12190
+ * @example
12191
+ * ```typescript
12192
+ * // Native fee
12193
+ * resolveFeePayment({
12194
+ * feeToken: '0x0000000000000000000000000000000000000000',
12195
+ * burnToken: '0xUSDC...',
12196
+ * amount: 1_000_000n,
12197
+ * feeTotalAmount: 3_500_000n,
12198
+ * })
12199
+ * // → { isNativeFee: true, isBurnTokenFee: false, nativeValue: 3_500_000n,
12200
+ * // approvals: [{ token: '0xUSDC...', amount: 1_000_000n }] }
12201
+ * ```
12202
+ */ const resolveFeePayment = (params)=>{
12203
+ const { feeToken, burnToken, amount, feeTotalAmount } = params;
12204
+ if (typeof amount !== 'bigint' || amount < 0n) {
12205
+ throw createValidationFailedError$1('amount', amount, 'Must be a non-negative bigint');
12206
+ }
12207
+ if (typeof feeTotalAmount !== 'bigint' || feeTotalAmount < 0n) {
12208
+ throw createValidationFailedError$1('feeTotalAmount', feeTotalAmount, 'Must be a non-negative bigint');
12209
+ }
12210
+ const isNativeFee = feeToken.toLowerCase() === ZERO_ADDRESS;
12211
+ const isBurnTokenFee = !isNativeFee && feeToken.toLowerCase() === burnToken.toLowerCase();
12212
+ if (isNativeFee) {
12213
+ return {
12214
+ isNativeFee: true,
12215
+ isBurnTokenFee: false,
12216
+ nativeValue: feeTotalAmount,
12217
+ approvals: [
12218
+ {
12219
+ token: burnToken,
12220
+ amount
12221
+ }
12222
+ ]
12223
+ };
12224
+ }
12225
+ if (isBurnTokenFee) {
12226
+ // Fee and burn draw on the same token — a single combined approval covers
12227
+ // both; the redundant second approval is skipped.
12228
+ return {
12229
+ isNativeFee: false,
12230
+ isBurnTokenFee: true,
12231
+ nativeValue: 0n,
12232
+ approvals: [
12233
+ {
12234
+ token: burnToken,
12235
+ amount: amount + feeTotalAmount
12236
+ }
12237
+ ]
12238
+ };
12239
+ }
12240
+ return {
12241
+ isNativeFee: false,
12242
+ isBurnTokenFee: false,
12243
+ nativeValue: 0n,
12244
+ approvals: [
12245
+ {
12246
+ token: burnToken,
12247
+ amount
12248
+ },
12249
+ {
12250
+ token: feeToken,
12251
+ amount: feeTotalAmount
12252
+ }
12253
+ ]
12254
+ };
12255
+ };
12256
+
12257
+ /**
12258
+ * The ASCII "cctp-forward" magic, hex-encoded (no `0x`), that a forward-friendly
12259
+ * hookData must start with.
12260
+ *
12261
+ * Encoded with `TextEncoder` (a browser-safe Web API) rather than `Buffer.from`
12262
+ * so this module-level constant does not reference the Node `Buffer` global at
12263
+ * import time. App Kit inlines this provider into its bundle without a Buffer polyfill, and a
12264
+ * bare `Buffer` here crashes browser bundles (e.g. Vite) on load — even for apps
12265
+ * that never touch the prepaid FORWARD path. Mirrors `buildForwardingHookData`.
12266
+ */ const CCTP_FORWARD_MAGIC_HEX = Array.from(new TextEncoder().encode(CCTP_FORWARD_MAGIC_PREFIX)).map((byte)=>byte.toString(16).padStart(2, '0')).join('');
12267
+ /**
12268
+ * Determine whether a hookData blob begins with the `cctp-forward` envelope.
12269
+ *
12270
+ * The prepaid FORWARD path requires the GenericExecutor blob to be wrapped in a
12271
+ * `cctp-forward` frame; without it the wrapper reverts `ForwardFeeWithoutHook`.
12272
+ *
12273
+ * @param hookData - The 0x-prefixed hookData hex string.
12274
+ * @returns True when the hookData starts with the `cctp-forward` magic.
12275
+ *
12276
+ * @example
12277
+ * ```typescript
12278
+ * hasForwardHook('0x636374702d666f7277617264...') // true
12279
+ * hasForwardHook('0xdeadbeef') // false
12280
+ * ```
12281
+ */ const hasForwardHook = (hookData)=>{
12282
+ if (typeof hookData !== 'string') {
12283
+ return false;
12284
+ }
12285
+ const normalized = (hookData.startsWith('0x') ? hookData.slice(2) : hookData).toLowerCase();
12286
+ return normalized.startsWith(CCTP_FORWARD_MAGIC_HEX);
12287
+ };
12288
+ /**
12289
+ * Assert that a hookData blob is forward-friendly for the prepaid FORWARD path.
12290
+ *
12291
+ * The prepaid FORWARD path always requests a FORWARD fee item, so the wrapper
12292
+ * requires the hookData to start with a `cctp-forward` frame. Validating this
12293
+ * before the burn surfaces the guaranteed `ForwardFeeWithoutHook` revert as a
12294
+ * typed input error instead of an on-chain failure.
12295
+ *
12296
+ * @param hookData - The 0x-prefixed hookData hex string.
12297
+ * @throws KitError (`INPUT_VALIDATION_FAILED`) if the hookData is missing or lacks
12298
+ * the `cctp-forward` frame.
12299
+ *
12300
+ * @example
12301
+ * ```typescript
12302
+ * assertForwardHookData(geForwardHookData) // ok
12303
+ * assertForwardHookData('0xdeadbeef') // throws — would revert ForwardFeeWithoutHook
12304
+ * ```
12305
+ */ const assertForwardHookData = (hookData)=>{
12306
+ if (!hasForwardHook(hookData)) {
12307
+ throw createValidationFailedError$1('hookData', hookData, 'Prepaid FORWARD burns require a cctp-forward-wrapped hookData; without it the TokenMessengerWithFees wrapper reverts ForwardFeeWithoutHook');
12308
+ }
12309
+ };
12310
+
12117
12311
  /**
12118
12312
  * Type guard to validate the forwardFee object structure.
12119
12313
  *
@@ -13045,6 +13239,109 @@ const assertCCTPv2BridgeParamsSymbol = Symbol('assertCCTPv2BridgeParams');
13045
13239
  }
13046
13240
  }
13047
13241
 
13242
+ /**
13243
+ * Assert that `params` is a well-formed {@link BurnWithFeesParams} object.
13244
+ *
13245
+ * Validates the full public-boundary input before any field destructuring,
13246
+ * `BigInt()` coercion, or adapter preparation runs, so malformed JS-caller
13247
+ * inputs always produce typed `KitError` validation failures.
13248
+ *
13249
+ * Checks performed (in order):
13250
+ * - `params` must be a non-null plain object
13251
+ * - `source` — valid CCTP v2 wallet context (via `assertCCTPv2WalletContext`)
13252
+ * - `destinationChain` — present and supports CCTP v2
13253
+ * - source and destination chains must both be testnet or both mainnet
13254
+ * - source and destination chains must differ
13255
+ * - `executor` — non-empty string
13256
+ * - `amount` — bigint or non-empty string coercible to bigint
13257
+ * - `feeTotalAmount` — bigint or non-empty string coercible to bigint
13258
+ * - `feeToken` — valid EVM address (`0x` + 40 hex chars)
13259
+ * - `claim.signedQuote` — valid `0x`-prefixed hex string
13260
+ * - `claim.refundAddress` — valid EVM address
13261
+ * - `hookData` — valid `0x`-prefixed hex string when present
13262
+ *
13263
+ * @param params - The value to validate.
13264
+ * @throws {KitError} If any field is missing or invalid.
13265
+ *
13266
+ * @example
13267
+ * ```typescript
13268
+ * assertBurnWithFeesParams(params)
13269
+ * // params is now typed as BurnWithFeesParams and safe to use
13270
+ * const { source, destinationChain, amount } = params
13271
+ * ```
13272
+ */ function assertBurnWithFeesParams(params) {
13273
+ if (params === null || typeof params !== 'object' || Array.isArray(params)) {
13274
+ throw createValidationFailedError$1('params', params, 'Must be a non-null plain object');
13275
+ }
13276
+ const p = params;
13277
+ // Source wallet context
13278
+ assertCCTPv2WalletContext(p['source']);
13279
+ const source = p['source'];
13280
+ // destinationChain
13281
+ const destinationChain = p['destinationChain'];
13282
+ if (destinationChain === null || destinationChain === undefined || typeof destinationChain !== 'object') {
13283
+ throw createValidationFailedError$1('destinationChain', destinationChain, 'Must be a chain definition object');
13284
+ }
13285
+ if (!isCCTPV2Supported(destinationChain)) {
13286
+ throw createValidationFailedError$1('destinationChain', destinationChain, 'destinationChain must support CCTP v2');
13287
+ }
13288
+ const dest = destinationChain;
13289
+ // Testnet / mainnet mismatch
13290
+ if (source.chain.isTestnet !== dest.isTestnet) {
13291
+ throw createNetworkMismatchError(source.chain, dest);
13292
+ }
13293
+ // Same-chain guard
13294
+ if (source.chain.name === dest.name) {
13295
+ throw createUnsupportedRouteError(source.chain.name, dest.name);
13296
+ }
13297
+ // executor
13298
+ const executor = p['executor'];
13299
+ if (typeof executor !== 'string' || executor === '') {
13300
+ throw createValidationFailedError$1('executor', executor, 'A GenericExecutor address is required (used for both mintRecipient and destinationCaller)');
13301
+ }
13302
+ // amount
13303
+ const rawAmount = p['amount'];
13304
+ if (typeof rawAmount !== 'bigint' && typeof rawAmount !== 'string' || rawAmount === '') {
13305
+ throw createValidationFailedError$1('amount', rawAmount, 'Must be a bigint or a numeric string');
13306
+ }
13307
+ try {
13308
+ BigInt(rawAmount);
13309
+ } catch {
13310
+ throw createValidationFailedError$1('amount', rawAmount, 'Must be a valid integer value coercible to bigint');
13311
+ }
13312
+ // feeTotalAmount
13313
+ const rawFee = p['feeTotalAmount'];
13314
+ if (typeof rawFee !== 'bigint' && typeof rawFee !== 'string' || rawFee === '') {
13315
+ throw createValidationFailedError$1('feeTotalAmount', rawFee, 'Must be a bigint or a numeric string');
13316
+ }
13317
+ try {
13318
+ BigInt(rawFee);
13319
+ } catch {
13320
+ throw createValidationFailedError$1('feeTotalAmount', rawFee, 'Must be a valid integer value coercible to bigint');
13321
+ }
13322
+ // feeToken
13323
+ if (!evmAddressSchema.safeParse(p['feeToken']).success) {
13324
+ throw createValidationFailedError$1('feeToken', p['feeToken'], 'Must be a valid EVM address (use the zero address for a native fee)');
13325
+ }
13326
+ // claim
13327
+ const rawClaim = p['claim'];
13328
+ if (rawClaim === null || typeof rawClaim !== 'object' || Array.isArray(rawClaim)) {
13329
+ throw createValidationFailedError$1('claim', rawClaim, 'Must be an object with signedQuote and refundAddress');
13330
+ }
13331
+ const claim = rawClaim;
13332
+ if (!hexStringSchema.safeParse(claim['signedQuote']).success) {
13333
+ throw createValidationFailedError$1('claim.signedQuote', claim['signedQuote'], 'Must be a valid hex string starting with 0x');
13334
+ }
13335
+ if (!evmAddressSchema.safeParse(claim['refundAddress']).success) {
13336
+ throw createValidationFailedError$1('claim.refundAddress', claim['refundAddress'], 'Must be a valid EVM address');
13337
+ }
13338
+ // hookData (optional)
13339
+ const hookData = p['hookData'];
13340
+ if (hookData !== undefined && hookData !== '' && !hexStringSchema.safeParse(hookData).success) {
13341
+ throw createValidationFailedError$1('hookData', hookData, 'Must be a valid hex string starting with 0x');
13342
+ }
13343
+ }
13344
+
13048
13345
  /**
13049
13346
  * CCTP bridge step names that can occur in the bridging flow.
13050
13347
  *
@@ -14253,10 +14550,15 @@ const mockAttestationMessage = {
14253
14550
  const burnCallData = burnRequest.getCallData();
14254
14551
  // batchExecute may throw before submission (wallet declined) but never
14255
14552
  // after — post-submission errors are returned as empty receipts.
14553
+ // The sender is threaded for adapters whose execution is routed through a
14554
+ // signing strategy (which has no wallet account to read it from); the
14555
+ // wallet-client path ignores it.
14256
14556
  const batchResult = await adapter.batchExecute([
14257
14557
  approveCallData,
14258
14558
  burnCallData
14259
- ], chain);
14559
+ ], chain, {
14560
+ fromAddress: params.source.address
14561
+ });
14260
14562
  const approveReceipt = batchResult.receipts[0];
14261
14563
  const burnReceipt = batchResult.receipts[1];
14262
14564
  const approveStep = await buildBatchedStep('approve', approveReceipt, batchResult.batchId, adapter, chain, batchResult.statusCode, batchResult.error);
@@ -14399,7 +14701,7 @@ const mockAttestationMessage = {
14399
14701
  return step;
14400
14702
  }
14401
14703
 
14402
- var version$2 = "1.9.0";
14704
+ var version$2 = "1.10.1";
14403
14705
  var pkg$2 = {
14404
14706
  version: version$2};
14405
14707
 
@@ -15554,7 +15856,7 @@ function assertCCTPV2Config(config) {
15554
15856
  throw new Error(`Failed to resolve operation context: ${error instanceof Error ? error.message : String(error)}`);
15555
15857
  }
15556
15858
  // Resolve spender address with proper error handling
15557
- const spenderAddress = resolveCCTPV2ContractAddress(chain);
15859
+ const spenderAddress = resolveCCTPV2ContractAddress(chain, 'tokenMessenger');
15558
15860
  // Prepare action parameters
15559
15861
  const actionParams = {
15560
15862
  amount: BigInt(amount),
@@ -16034,6 +16336,106 @@ function assertCCTPV2Config(config) {
16034
16336
  return await source.adapter.prepareAction('cctp.v2.depositForBurn', actionParams, resolvedContext);
16035
16337
  }
16036
16338
  /**
16339
+ * Prepare a prepaid-FORWARD burn through the `TokenMessengerWithFees` wrapper.
16340
+ *
16341
+ * Builds the source-chain `depositForBurnWithHookAndFees` call for the
16342
+ * GenericExecutor FORWARD path: fees are collected up front on the source chain
16343
+ * against a signed quote, `mintRecipient` and `destinationCaller` are both set to
16344
+ * the GenericExecutor, and the GE `hookData` is passed through unchanged.
16345
+ *
16346
+ * This is the low-level on-chain primitive behind the UBK `fastCrossChainDeposit`
16347
+ * and Bridge Kit `bridge({ deposit })` flows. The `hookData` and signed-quote
16348
+ * `claim` are produced elsewhere and passed in here:
16349
+ * - `hookData`: `buildForwardingHookDataWithPayload(version,
16350
+ * buildDepositForGenericExecutorPayload(...).hookData)` from `@core/utils`.
16351
+ * - `claim.signedQuote` / `feeToken` / `feeTotalAmount`: from `fetchFeeQuote`
16352
+ * (`@circle-fin/provider-fee-v1`), whose FORWARD item must be bound to the
16353
+ * SAME `hookData` and executor `destinationCaller` used here.
16354
+ *
16355
+ * The returned approvals and burn are NOT executed — the caller executes the
16356
+ * approvals first (in order) and then the burn. The fee payment channel matches
16357
+ * the quote's `feeToken`:
16358
+ * - Native fee: exactly `feeTotalAmount` is attached as the burn's `msg.value`;
16359
+ * only the burn amount is approved.
16360
+ * - USDC fee (same token as the burn): a single combined `amount + feeTotalAmount`
16361
+ * approval covers both; the redundant second approval is skipped.
16362
+ *
16363
+ * @typeParam TFromAdapterCapabilities - The source adapter's capabilities.
16364
+ * @param params - The burn amount, executor, hookData, signed-quote claim, and fee.
16365
+ * @returns The prepared approvals, the prepared burn, and the resolved fee plan.
16366
+ * @throws {KitError} If the wallet context is invalid, `destinationChain` does not
16367
+ * support CCTP v2, the executor is missing, `amount` or `feeTotalAmount` is not
16368
+ * a bigint or a numeric string coercible to bigint, the hookData lacks a
16369
+ * `cctp-forward` frame (guaranteed `ForwardFeeWithoutHook`), or the operation
16370
+ * context cannot be resolved.
16371
+ *
16372
+ * @example
16373
+ * ```typescript
16374
+ * const { approvals, burn } = await provider.burnWithFees({
16375
+ * source,
16376
+ * destinationChain: Arc,
16377
+ * amount: 1_000_000n,
16378
+ * executor: genericExecutorAddress,
16379
+ * hookData: geForwardHookData,
16380
+ * claim: { signedQuote: '0x01...', refundAddress: userAddress },
16381
+ * feeToken: '0x0000000000000000000000000000000000000000', // native
16382
+ * feeTotalAmount: 3_500_000n,
16383
+ * })
16384
+ * for (const approval of approvals) await approval.execute()
16385
+ * const txHash = await burn.execute()
16386
+ * ```
16387
+ */ async burnWithFees(params) {
16388
+ assertBurnWithFeesParams(params);
16389
+ const { source, destinationChain, executor, hookData, claim, feeToken } = params;
16390
+ const amount = BigInt(params.amount);
16391
+ const feeTotalAmount = BigInt(params.feeTotalAmount);
16392
+ // Coupling guard: the prepaid FORWARD path always requests a FORWARD fee item,
16393
+ // so the hookData must carry a cctp-forward frame; otherwise the wrapper
16394
+ // reverts ForwardFeeWithoutHook. Surface it as a typed input error up front.
16395
+ assertForwardHookData(hookData);
16396
+ const burnToken = source.chain.usdcAddress;
16397
+ const feePayment = resolveFeePayment({
16398
+ feeToken,
16399
+ burnToken,
16400
+ amount,
16401
+ feeTotalAmount
16402
+ });
16403
+ // Resolve operation context from the source wallet context.
16404
+ const operationContext = this.extractOperationContext(source);
16405
+ let resolvedContext;
16406
+ try {
16407
+ resolvedContext = await resolveOperationContext(source.adapter, operationContext);
16408
+ } catch (error) {
16409
+ throw createValidationFailedError$1('source.adapter', undefined, `Failed to resolve operation context: ${error instanceof Error ? error.message : String(error)}`);
16410
+ }
16411
+ const context = resolvedContext;
16412
+ const wrapperAddress = resolveCCTPV2ContractAddress(source.chain, 'tokenMessengerWithFees');
16413
+ // Build the ERC-20 approvals to the wrapper (burn token, plus a distinct fee
16414
+ // token only when the fee is not paid in the burn token).
16415
+ const approvals = await Promise.all(feePayment.approvals.map(async (approval)=>source.adapter.prepareAction('token.approve', {
16416
+ tokenAddress: approval.token,
16417
+ delegate: wrapperAddress,
16418
+ amount: approval.amount
16419
+ }, context)));
16420
+ // Build the burn: mintRecipient AND destinationCaller are both the executor.
16421
+ const burn = await source.adapter.prepareAction('cctp.v2.depositForBurnWithFees', {
16422
+ fromChain: source.chain,
16423
+ toChain: destinationChain,
16424
+ amount,
16425
+ mintRecipient: executor,
16426
+ destinationCaller: executor,
16427
+ hookData,
16428
+ claim,
16429
+ feeToken,
16430
+ feeTotalAmount
16431
+ }, context);
16432
+ return {
16433
+ approvals,
16434
+ burn,
16435
+ feePayment
16436
+ };
16437
+ }
16438
+ /**
16037
16439
  * Waits for a transaction to be mined and confirmed on the blockchain.
16038
16440
  *
16039
16441
  * This method should block until the transaction is confirmed on the blockchain.
@@ -16913,7 +17315,7 @@ registerKit(`${pkg$3.name}/${pkg$3.version}`);
16913
17315
  };
16914
17316
 
16915
17317
  var name$1 = "@circle-fin/swap-kit";
16916
- var version$1 = "1.3.2";
17318
+ var version$1 = "1.5.0";
16917
17319
  var pkg$1 = {
16918
17320
  name: name$1,
16919
17321
  version: version$1};
@@ -16978,7 +17380,10 @@ const optionalChainIdentifierField = chainIdentifierField.optional();
16978
17380
  }).min(1, 'kitKey must be a non-empty string').optional(),
16979
17381
  provider: zod.z.string({
16980
17382
  invalid_type_error: 'provider must be a string'
16981
- }).min(1, 'provider must be a non-empty string').optional()
17383
+ }).min(1, 'provider must be a non-empty string').optional(),
17384
+ batchTransactions: zod.z.boolean({
17385
+ invalid_type_error: 'batchTransactions must be a boolean'
17386
+ }).optional()
16982
17387
  });
16983
17388
  /**
16984
17389
  * Zod schema for adapter context.
@@ -17417,7 +17822,7 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
17417
17822
  /**
17418
17823
  * Circle Stablecoin Service API Key.
17419
17824
  * Must be a valid API key format.
17420
- */ apiKey: apiKeySchema
17825
+ */ apiKey: apiKeySchema.optional()
17421
17826
  }).superRefine(requireCrossChainQuoteToAddress);
17422
17827
  /**
17423
17828
  * Zod schema for validating CreateSwapRequest parameters.
@@ -17475,7 +17880,7 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
17475
17880
  /**
17476
17881
  * Circle Stablecoin Service API Key.
17477
17882
  * Must be a valid API key format.
17478
- */ apiKey: apiKeySchema
17883
+ */ apiKey: apiKeySchema.optional()
17479
17884
  });
17480
17885
  /**
17481
17886
  * Zod schema for validating GetSwapStatusResponse data.
@@ -17511,7 +17916,7 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
17511
17916
  toChain: zod.z.string({
17512
17917
  invalid_type_error: 'toChain must be a string'
17513
17918
  }).min(1, 'toChain must be a non-empty string if provided').optional(),
17514
- apiKey: apiKeySchema
17919
+ apiKey: apiKeySchema.optional()
17515
17920
  });
17516
17921
  /**
17517
17922
  * Zod schema for validating CreateSwapResponse payloads.
@@ -17520,13 +17925,15 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
17520
17925
  required_error: 'fee token is required',
17521
17926
  invalid_type_error: 'fee token must be a string'
17522
17927
  }).min(1, 'fee token must be a non-empty string'),
17523
- amount: feeAmountSchema
17928
+ amount: feeAmountSchema,
17929
+ decimals: zod.z.number().int('fee token decimals must be an integer').nonnegative('fee token decimals must be a non-negative integer').optional(),
17930
+ symbol: zod.z.string({
17931
+ invalid_type_error: 'fee token symbol must be a string'
17932
+ }).min(1, 'fee token symbol must be a non-empty string').optional()
17524
17933
  });
17525
17934
  /**
17526
17935
  * Developer fee item schema with basis field.
17527
- */ const createSwapDeveloperFeeItemSchema = zod.z.object({
17528
- token: zod.z.string().min(1, 'fee token must be a non-empty string'),
17529
- amount: feeAmountSchema,
17936
+ */ const createSwapDeveloperFeeItemSchema = createSwapFeeItemSchema.extend({
17530
17937
  basis: zod.z.enum([
17531
17938
  'inputAmount',
17532
17939
  'estimatedAmount'
@@ -17618,7 +18025,7 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
17618
18025
  addresses: zod.z.array(zod.z.string({
17619
18026
  invalid_type_error: 'addresses entries must be strings'
17620
18027
  }).min(1, 'addresses entries must be non-empty strings')).min(1, 'addresses must contain at least one entry when provided').max(MAX_RATE_ADDRESSES_PER_REQUEST, `addresses supports at most ${String(MAX_RATE_ADDRESSES_PER_REQUEST)} values per request`).optional(),
17621
- apiKey: apiKeySchema
18028
+ apiKey: apiKeySchema.optional()
17622
18029
  });
17623
18030
  /**
17624
18031
  * Zod schema for validating GetTokenRatesResponse payloads.
@@ -17649,6 +18056,16 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
17649
18056
  required_error: 'estimatedAmount is required',
17650
18057
  invalid_type_error: 'estimatedAmount must be a string'
17651
18058
  }).min(1, 'estimatedAmount must be a non-empty string'),
18059
+ // Per-swap join key echoed back verbatim on success telemetry. Optional so a
18060
+ // not-yet-upgraded service (no field) still validates during rollout. A
18061
+ // malformed/non-UUID value is coerced to `undefined` (no telemetry id) rather
18062
+ // than throwing: this is a telemetry-only field (stripped from the developer
18063
+ // result, never used for control flow), so it must not be able to abort the
18064
+ // swap via `parseCreateSwapResponse().parse()`. Mirrors the best-effort,
18065
+ // never-throw contract of the rest of the telemetry stack. Implemented with
18066
+ // `preprocess` rather than Zod's `.catch()` because static analysis misreads
18067
+ // `.catch` on the schema chain as an unhandled Promise (S7785).
18068
+ correlationId: zod.z.preprocess((value)=>zod.z.string().uuid().safeParse(value).success ? value : undefined, zod.z.string().optional()),
17652
18069
  config: createSwapRequestBaseSchema.shape.config.optional(),
17653
18070
  fees: createSwapFeesSchema.optional(),
17654
18071
  transaction: createSwapTransactionSchema
@@ -18841,7 +19258,7 @@ new Set(Object.values(Blockchain));
18841
19258
  registerKit(`${pkg$1.name}/${pkg$1.version}`);
18842
19259
 
18843
19260
  var name = "@circle-fin/earn-kit";
18844
- var version = "1.2.2";
19261
+ var version = "1.4.0";
18845
19262
  var pkg = {
18846
19263
  name: name,
18847
19264
  version: version};
@@ -18971,7 +19388,11 @@ const bridgeFeeTokenSchema = hexAddressSchema;
18971
19388
  asset: zod.z.string(),
18972
19389
  assetAddress: zod.z.string(),
18973
19390
  lltv: zod.z.number(),
18974
- supplyUsd: zod.z.number()
19391
+ supplyUsd: zod.z.number(),
19392
+ // Optional during the expand/contract window (a backend that predates the
19393
+ // field omits the key), mirroring the `.optional()` facets on the base
19394
+ // schema; `null` when the product exposes no per-market allocation (V2).
19395
+ allocationPct: zod.z.number().nullable().optional()
18975
19396
  });
18976
19397
  /**
18977
19398
  * Zod schema for a Morpho vault warning in the API response.
@@ -18985,7 +19406,74 @@ const bridgeFeeTokenSchema = hexAddressSchema;
18985
19406
  ])
18986
19407
  });
18987
19408
  /**
18988
- * Zod schema for a single vault info object in the API response.
19409
+ * Zod schema for the manager (curator) facet in the API response.
19410
+ *
19411
+ * @internal
19412
+ */ const managerSchema = zod.z.object({
19413
+ name: zod.z.string(),
19414
+ address: zod.z.string().optional(),
19415
+ // Only 'curator' is emitted today (Morpho V1/V2). Additional manager roles
19416
+ // are added here as the providers that emit them land, rather than shipped
19417
+ // speculatively.
19418
+ type: zod.z.enum([
19419
+ 'curator'
19420
+ ])
19421
+ });
19422
+ /**
19423
+ * Zod schema for the APY profile facet in the API response.
19424
+ *
19425
+ * @internal
19426
+ */ const apyProfileSchema = zod.z.object({
19427
+ current: zod.z.number(),
19428
+ native: zod.z.number().nullable(),
19429
+ d7: zod.z.number().nullable(),
19430
+ d30: zod.z.number().nullable(),
19431
+ d90: zod.z.number().nullable(),
19432
+ rewardShare: zod.z.number().nullable(),
19433
+ source: zod.z.string().optional(),
19434
+ asOf: zod.z.string().optional()
19435
+ });
19436
+ /**
19437
+ * Zod schema for the fee split facet in the API response.
19438
+ *
19439
+ * @internal
19440
+ */ const feeInfoSchema = zod.z.object({
19441
+ performance: zod.z.number().nullable(),
19442
+ management: zod.z.number().nullable()
19443
+ });
19444
+ /**
19445
+ * Zod schema for the liquidity profile facet in the API response.
19446
+ *
19447
+ * `totalSupply` is the outstanding vault share tokens (ERC4626 totalSupply);
19448
+ * it is validated as a raw JSON amount, like `totalDeposits`/`available`.
19449
+ *
19450
+ * @internal
19451
+ */ const liquidityProfileSchema = zod.z.object({
19452
+ totalDeposits: amountJsonSchema,
19453
+ available: amountJsonSchema,
19454
+ totalSupply: amountJsonSchema,
19455
+ status: zod.z.enum([
19456
+ 'active',
19457
+ 'low_liquidity'
19458
+ ])
19459
+ });
19460
+ /**
19461
+ * Zod schema for the risk signals facet in the API response.
19462
+ *
19463
+ * @internal
19464
+ */ const riskSignalsSchema = zod.z.object({
19465
+ circleSentinel: zod.z.boolean(),
19466
+ warnings: zod.z.array(vaultWarningSchema).optional(),
19467
+ earnKitWarnings: zod.z.array(zod.z.string()).optional()
19468
+ });
19469
+ /**
19470
+ * Zod schema for the universal earn-opportunity base in the API response.
19471
+ *
19472
+ * Retains every existing deprecated flat field (kept validated through the
19473
+ * expand/contract window so default-strip does not drop them) and adds the
19474
+ * new nested facets. The nested facets are `.optional()` during the
19475
+ * transition so the SDK still validates against a not-yet-fully-deployed
19476
+ * backend; they become required after Expand ships.
18989
19477
  *
18990
19478
  * @internal
18991
19479
  */ const vaultInfoResponseSchema = zod.z.object({
@@ -19010,6 +19498,96 @@ const bridgeFeeTokenSchema = hexAddressSchema;
19010
19498
  warnings: zod.z.array(vaultWarningSchema).optional(),
19011
19499
  earnKitWarnings: zod.z.array(zod.z.string()).optional()
19012
19500
  });
19501
+ /**
19502
+ * Shared base schema: existing flat fields (kept) plus the new nested
19503
+ * facets and neutral identity. Facets are `.optional()` during the
19504
+ * transition; flip to required once the backend is confirmed emitting.
19505
+ *
19506
+ * @internal
19507
+ */ const earnBaseSchema = vaultInfoResponseSchema.extend({
19508
+ address: zod.z.string().optional(),
19509
+ asOf: zod.z.string().optional(),
19510
+ manager: managerSchema.nullable().optional(),
19511
+ apyProfile: apyProfileSchema.optional(),
19512
+ fee: feeInfoSchema.optional(),
19513
+ liquidityProfile: liquidityProfileSchema.optional(),
19514
+ riskSignals: riskSignalsSchema.optional()
19515
+ });
19516
+ /**
19517
+ * Zod schema for the `vault` opportunity variant.
19518
+ *
19519
+ * @internal
19520
+ */ const vaultOpportunitySchema = earnBaseSchema.extend({
19521
+ productType: zod.z.literal('vault'),
19522
+ collateral: zod.z.array(collateralSchema)
19523
+ });
19524
+ /**
19525
+ * Discriminated union over `productType`. Add union members here as new
19526
+ * product types (e.g. `lending_market`, `rwa_token`) land.
19527
+ *
19528
+ * @internal
19529
+ */ const earnOpportunityVariants = [
19530
+ vaultOpportunitySchema
19531
+ ];
19532
+ /** @internal */ const earnOpportunitySchema = zod.z.discriminatedUnion('productType', earnOpportunityVariants);
19533
+ /** Product types this SDK version knows how to parse. */ const knownProductTypes = new Set(earnOpportunityVariants.map((variant)=>variant.shape.productType.value));
19534
+ /**
19535
+ * Tolerant list parser for earn opportunities.
19536
+ *
19537
+ * `z.discriminatedUnion` throws on an unrecognized discriminant and
19538
+ * `z.array` fails the whole array if any element fails. Two migration-window
19539
+ * cases are smoothed over here so neither breaks an already-shipped SDK:
19540
+ *
19541
+ * - A backend that predates `productType` omits it entirely. `'vault'` was the
19542
+ * only opportunity type then, so default a missing discriminant to `'vault'`
19543
+ * rather than dropping every vault the backend returns.
19544
+ * - A future backend adds a *second* `productType` this SDK version does not
19545
+ * know. Drop those elements (a present-but-unrecognized discriminant) instead
19546
+ * of rejecting the whole list.
19547
+ *
19548
+ * Only the drop above is a *tolerant* case. Anything that is not a plain object
19549
+ * with a present-but-unknown string `productType` — `null`, `undefined`,
19550
+ * primitives, or an object whose `productType` is malformed — is passed through
19551
+ * untouched so `z.array(earnOpportunitySchema)` reports it as a normal
19552
+ * validation failure. It is deliberately not silently dropped (which would hide
19553
+ * malformed backend data) and never throws here (an unguarded property read on
19554
+ * a non-object would escape `safeParse` as a raw `TypeError` instead of a
19555
+ * `ZodError`).
19556
+ *
19557
+ * @internal
19558
+ */ const earnOpportunityListSchema = zod.z.preprocess((raw)=>{
19559
+ if (!Array.isArray(raw)) {
19560
+ return raw;
19561
+ }
19562
+ // Array.isArray narrows `raw` to `any[]`; view it as `unknown[]` so the
19563
+ // map/filter chain stays type-safe and no `any` leaks into the return.
19564
+ const entries = raw;
19565
+ return entries.map((entry)=>{
19566
+ // Only touch plain objects; non-objects fall through to fail validation.
19567
+ if (typeof entry !== 'object' || entry === null) {
19568
+ return entry;
19569
+ }
19570
+ const record = entry;
19571
+ // Older backend predating productType: default to the only type then.
19572
+ return record.productType === undefined ? {
19573
+ ...record,
19574
+ productType: 'vault'
19575
+ } : record;
19576
+ }).filter((entry)=>{
19577
+ // Drop ONLY a present-but-unknown string discriminant (a future
19578
+ // productType this SDK version doesn't know). Everything else —
19579
+ // non-objects, a non-string productType — flows through to
19580
+ // z.array(earnOpportunitySchema) and fails/passes validation normally.
19581
+ if (typeof entry !== 'object' || entry === null) {
19582
+ return true;
19583
+ }
19584
+ const productType = entry.productType;
19585
+ if (typeof productType !== 'string') {
19586
+ return true;
19587
+ }
19588
+ return knownProductTypes.has(productType);
19589
+ });
19590
+ }, zod.z.array(earnOpportunitySchema));
19013
19591
  // ---------------------------------------------------------------------------
19014
19592
  // Position response schema
19015
19593
  // ---------------------------------------------------------------------------
@@ -19139,6 +19717,7 @@ const positionPnlSchema = zod.z.discriminatedUnion('status', [
19139
19717
  *
19140
19718
  * @internal
19141
19719
  */ const depositPayloadSchema = zod.z.object({
19720
+ execId: bridgeDepositExecIdSchema,
19142
19721
  executionParams: depositExecutionParamsSchema,
19143
19722
  signature: hexSignatureSchema
19144
19723
  });
@@ -19230,6 +19809,21 @@ const bridgeDepositPrepareReviewSchema = zod.z.object({
19230
19809
  amount: amountJsonSchema,
19231
19810
  vaultAddress: hexAddressSchema
19232
19811
  }).passthrough();
19812
+ /** @internal */ const bridgeQuoteExpirySchema = zod.z.discriminatedUnion('mode', [
19813
+ zod.z.object({
19814
+ mode: zod.z.literal('TIMESTAMP'),
19815
+ expiresAt: zod.z.string().datetime({
19816
+ offset: true
19817
+ })
19818
+ }),
19819
+ zod.z.object({
19820
+ mode: zod.z.literal('BLOCK_NUMBER'),
19821
+ expiresAtBlock: zod.z.number().int(),
19822
+ blockEstimatedAt: zod.z.string().datetime({
19823
+ offset: true
19824
+ }).optional()
19825
+ })
19826
+ ]).optional().catch(undefined);
19233
19827
  /**
19234
19828
  * Zod schema for the bridge deposit prepare payload.
19235
19829
  *
@@ -19241,6 +19835,10 @@ const bridgeDepositPrepareReviewSchema = zod.z.object({
19241
19835
  execId: bridgeDepositExecIdSchema,
19242
19836
  erc3009TypedData: bridgeDepositPreparedBundleSchema,
19243
19837
  expiresAt: zod.z.string().datetime(),
19838
+ quoteIssuedAt: zod.z.string().datetime({
19839
+ offset: true
19840
+ }).optional().catch(undefined),
19841
+ quoteExpiry: bridgeQuoteExpirySchema,
19244
19842
  review: bridgeDepositPrepareReviewSchema
19245
19843
  });
19246
19844
  /**
@@ -19306,6 +19904,7 @@ const bridgeDepositPrepareReviewSchema = zod.z.object({
19306
19904
  *
19307
19905
  * @internal
19308
19906
  */ const withdrawPayloadSchema = zod.z.object({
19907
+ execId: bridgeDepositExecIdSchema,
19309
19908
  executionParams: withdrawExecutionParamsSchema,
19310
19909
  signature: hexSignatureSchema
19311
19910
  });
@@ -19319,6 +19918,27 @@ const bridgeDepositPrepareReviewSchema = zod.z.object({
19319
19918
  data: withdrawPayloadSchema
19320
19919
  });
19321
19920
  // ---------------------------------------------------------------------------
19921
+ // Transaction report response schema
19922
+ // ---------------------------------------------------------------------------
19923
+ /**
19924
+ * Zod schema for the transaction report payload inside the API `data` envelope.
19925
+ *
19926
+ * The Earn Service returns an empty payload (`{"data":{}}`) on success, so the
19927
+ * schema accepts any object shape and does not require specific fields.
19928
+ *
19929
+ * @internal
19930
+ */ const transactionReportPayloadSchema = zod.z.object({}).passthrough();
19931
+ /**
19932
+ * Zod schema for the `POST /v1/earnKit/transactions/report` API response.
19933
+ *
19934
+ * The Earn Service API wraps the transaction report payload in a `data`
19935
+ * envelope.
19936
+ *
19937
+ * @internal
19938
+ */ zod.z.object({
19939
+ data: transactionReportPayloadSchema
19940
+ });
19941
+ // ---------------------------------------------------------------------------
19322
19942
  // Claim rewards response schema
19323
19943
  // ---------------------------------------------------------------------------
19324
19944
  /**
@@ -19379,6 +19999,30 @@ const bridgeDepositPrepareReviewSchema = zod.z.object({
19379
19999
  token: zod.z.string(),
19380
20000
  amount: amountJsonSchema
19381
20001
  });
20002
+ /**
20003
+ * Zod schema for a native gas-fee entry in an EarnKit quote response.
20004
+ *
20005
+ * The Earn Service backend estimates gas server-side and returns one entry per
20006
+ * action (`Approve`, `Deposit`, `Withdraw`). A successful estimate carries
20007
+ * `fees` in the SDK `EstimatedGas` shape (`{ gas, gasPrice, fee }`), each a raw
20008
+ * integer string in the chain's native base units. When the backend cannot
20009
+ * estimate an action it returns `fees: null` with an `error` message instead.
20010
+ *
20011
+ * The schema deliberately validates almost nothing beyond the envelope: `name`
20012
+ * is optional and `fees` is entirely unvalidated (`unknown`). ALL validation
20013
+ * of `fees` — that it is an object at all, and that `gas`, `gasPrice`, and
20014
+ * `fee` are parseable integer strings — is deferred to {@link toQuoteGasFees},
20015
+ * which degrades a malformed entry to a `fees: null` soft failure. This is
20016
+ * intentional: gas is best-effort, so a single unparseable gas entry (a wrong
20017
+ * type such as `fees: 123` or `fees: 'bad'`, a missing field, or a non-numeric
20018
+ * `fee`) must never fail Zod validation and reject the entire quote.
20019
+ *
20020
+ * @internal
20021
+ */ const quoteGasFeeSchema = zod.z.object({
20022
+ name: zod.z.string().optional(),
20023
+ fees: zod.z.unknown(),
20024
+ error: zod.z.string().optional()
20025
+ }).passthrough();
19382
20026
  /**
19383
20027
  * Zod schema for the inner deposit quote payload.
19384
20028
  *
@@ -19394,7 +20038,8 @@ const bridgeDepositPrepareReviewSchema = zod.z.object({
19394
20038
  expectedShares: amountJsonSchema,
19395
20039
  sharePrice: zod.z.string(),
19396
20040
  currentApy: zod.z.number(),
19397
- fees: zod.z.array(feeSchema).optional()
20041
+ fees: zod.z.array(feeSchema).optional(),
20042
+ gasFees: zod.z.array(quoteGasFeeSchema).optional()
19398
20043
  });
19399
20044
  /**
19400
20045
  * Zod schema for the `POST /v1/earnKit/deposit/quote` API response.
@@ -19421,6 +20066,7 @@ const bridgeDepositPrepareReviewSchema = zod.z.object({
19421
20066
  sharePrice: zod.z.string(),
19422
20067
  maxWithdrawable: amountJsonSchema,
19423
20068
  fees: zod.z.array(feeSchema),
20069
+ gasFees: zod.z.array(quoteGasFeeSchema).optional(),
19424
20070
  warnings: zod.z.array(zod.z.string()).optional()
19425
20071
  });
19426
20072
  /**
@@ -19478,7 +20124,7 @@ const bridgeDepositPrepareReviewSchema = zod.z.object({
19478
20124
  *
19479
20125
  * @internal
19480
20126
  */ const getVaultsPayloadSchema = zod.z.object({
19481
- vaults: zod.z.array(vaultInfoResponseSchema),
20127
+ vaults: earnOpportunityListSchema,
19482
20128
  errors: zod.z.array(vaultErrorSchema)
19483
20129
  });
19484
20130
  /**
@@ -19508,7 +20154,7 @@ const bridgeDepositPrepareReviewSchema = zod.z.object({
19508
20154
  *
19509
20155
  * @internal
19510
20156
  */ const exploreVaultsPayloadSchema = zod.z.object({
19511
- vaults: zod.z.array(vaultInfoResponseSchema),
20157
+ vaults: earnOpportunityListSchema,
19512
20158
  pagination: explorePaginationSchema
19513
20159
  });
19514
20160
  /**
@@ -20076,6 +20722,8 @@ function hasCrossChainDepositQuoteShape(params) {
20076
20722
  config: earnConfigSchema.optional()
20077
20723
  });
20078
20724
 
20725
+ /** SDK name used in telemetry payloads. */ resolveKitSdkName(pkg.name);
20726
+
20079
20727
  // Auto-register this kit for user agent tracking
20080
20728
  registerKit(`${pkg.name}/${pkg.version}`);
20081
20729