@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.
@@ -16,13 +16,24 @@
16
16
  * limitations under the License.
17
17
  */
18
18
 
19
+ // Buffer polyfill setup - executes before any other code
20
+ // Ensures globalThis.Buffer is available for Solana libraries
21
+ import { Buffer } from 'buffer';
22
+ if (typeof globalThis !== 'undefined' && typeof globalThis.Buffer === 'undefined') {
23
+ globalThis.Buffer = Buffer;
24
+ }
25
+ if (typeof window !== 'undefined' && typeof window.Buffer === 'undefined') {
26
+ window.Buffer = Buffer;
27
+ }
28
+
29
+
19
30
  import { z } from 'zod';
20
31
  import pino from 'pino';
21
32
  import { formatUnits as formatUnits$1, parseUnits as parseUnits$1 } from '@ethersproject/units';
22
33
  import { hexlify, hexZeroPad } from '@ethersproject/bytes';
34
+ import '@ethersproject/abi';
23
35
  import { getAddress } from '@ethersproject/address';
24
36
  import bs58 from 'bs58';
25
- import '@ethersproject/abi';
26
37
  import { PublicKey } from '@solana/web3.js';
27
38
  import 'bn.js';
28
39
  import '@coral-xyz/anchor';
@@ -44,6 +55,27 @@ import { keccak256 } from '@ethersproject/keccak256';
44
55
  * }
45
56
  * ```
46
57
  */ const isNodeEnvironment = ()=>typeof process !== 'undefined' && typeof process.versions === 'object' && typeof process.versions.node === 'string';
58
+ /**
59
+ * Return the SDK User-Agent request header only when running in Node.js.
60
+ *
61
+ * Browsers forbid manually setting `User-Agent`, and a custom fallback header
62
+ * can trigger CORS preflight. Non-Node server runtimes also omit this optional
63
+ * attribution header because they cannot set it reliably.
64
+ *
65
+ * @returns A User-Agent header in Node.js, or an empty object otherwise.
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * import { getNodeUserAgentHeader } from '@core/utils'
70
+ *
71
+ * const headers = {
72
+ * 'Content-Type': 'application/json',
73
+ * ...getNodeUserAgentHeader(),
74
+ * }
75
+ * ```
76
+ */ const getNodeUserAgentHeader = ()=>isNodeEnvironment() ? {
77
+ 'User-Agent': getUserAgent()
78
+ } : {};
47
79
  /**
48
80
  * Detect the runtime environment and return a shortened identifier.
49
81
  *
@@ -3094,7 +3126,10 @@ var EarnChain;
3094
3126
  contracts: {
3095
3127
  v1: {
3096
3128
  wallet: GATEWAY_WALLET_EVM_TESTNET,
3097
- minter: GATEWAY_MINTER_EVM_TESTNET
3129
+ minter: GATEWAY_MINTER_EVM_TESTNET,
3130
+ // DepositForHandler the GenericExecutor calls to run a fast cross-chain
3131
+ // deposit into the GatewayWallet above.
3132
+ depositForHandler: '0xD05E7D2E7d30b92c5F17d7d0fC575fce231F1A48'
3098
3133
  }
3099
3134
  },
3100
3135
  forwarderSupported: {
@@ -6212,7 +6247,10 @@ var Chains = /*#__PURE__*/Object.freeze({
6212
6247
  minter: z.string({
6213
6248
  required_error: 'Gateway minter address is required. Please provide a valid contract address.',
6214
6249
  invalid_type_error: 'Gateway minter address must be a string.'
6215
- }).min(1, 'Gateway minter address cannot be empty.')
6250
+ }).min(1, 'Gateway minter address cannot be empty.'),
6251
+ depositForHandler: z.string({
6252
+ invalid_type_error: 'Gateway depositForHandler address must be a string.'
6253
+ }).min(1, 'Gateway depositForHandler address cannot be empty.').optional()
6216
6254
  }).strict() // Reject any additional properties not defined in the schema
6217
6255
  ;
6218
6256
  /**
@@ -6734,21 +6772,31 @@ const swapTokenEnumSchema = z.enum([
6734
6772
  * returning the appropriate address based on the requested contract type.
6735
6773
  *
6736
6774
  * @param chain - The chain definition to resolve the contract address for
6737
- * @param contractType - The type of contract address to resolve ('tokenMessenger' or 'messageTransmitter')
6775
+ * @param contractType - The type of contract address to resolve ('tokenMessenger', 'messageTransmitter', or 'tokenMessengerWithFees')
6738
6776
  * @returns The contract address for the specified contract type
6739
6777
  * @throws Error when chain does not support CCTP v2 or has unsupported contract configuration
6778
+ * @throws Error when 'tokenMessengerWithFees' is requested but not configured on the chain
6740
6779
  */ const resolveCCTPV2ContractAddress = (chain, contractType)=>{
6741
6780
  // Handle custom bridge contract for tokenMessenger (burn transaction)
6742
- if (hasCustomContractSupport(chain, 'bridge') && chain.kitContracts?.bridge !== undefined) {
6781
+ if (contractType === 'tokenMessenger' && hasCustomContractSupport(chain, 'bridge') && chain.kitContracts?.bridge !== undefined) {
6743
6782
  return chain.kitContracts.bridge;
6744
6783
  }
6745
6784
  // At this point we know CCTP v2 is supported, so contracts exist
6746
6785
  const cctpConfig = chain.cctp;
6747
6786
  const contracts = cctpConfig.contracts.v2;
6787
+ // The `TokenMessengerWithFees` wrapper (prepaid FORWARD path) is an optional
6788
+ // deployment carried alongside both split and merged configurations.
6789
+ if (contractType === 'tokenMessengerWithFees') {
6790
+ const wrapper = contracts.tokenMessengerWithFees;
6791
+ if (wrapper === undefined || wrapper === '') {
6792
+ throw new Error(`TokenMessengerWithFees is not configured on chain ${chain.name}. The prepaid FORWARD path is unavailable on this chain.`);
6793
+ }
6794
+ return wrapper;
6795
+ }
6748
6796
  // Handle different contract types with explicit type checking
6749
6797
  switch(contracts.type){
6750
6798
  case 'split':
6751
- return contracts.tokenMessenger ;
6799
+ return contractType === 'tokenMessenger' ? contracts.tokenMessenger : contracts.messageTransmitter;
6752
6800
  case 'merged':
6753
6801
  return contracts.contract;
6754
6802
  default:
@@ -7454,13 +7502,12 @@ const swapTokenEnumSchema = z.enum([
7454
7502
  headers: {
7455
7503
  ...DEFAULT_CONFIG$1.headers,
7456
7504
  ...config.headers ?? {},
7457
- // In browser environments, directly setting the 'User-Agent' or similar headers is restricted and may be ignored or cause errors.
7458
- // This is why we use the 'X-User-Agent' header instead.
7459
- ...typeof window === 'undefined' ? {
7460
- 'User-Agent': getUserAgent()
7461
- } : {
7462
- 'X-User-Agent': getUserAgent()
7463
- }
7505
+ // Browsers forbid setting a user-agent request header, and the custom
7506
+ // fallback header the SDK used instead trips CORS preflight against the
7507
+ // Circle APIs (it isn't in their `Access-Control-Allow-Headers`),
7508
+ // blocking the request. So send the SDK user agent only in Node;
7509
+ // browsers omit it entirely.
7510
+ ...getNodeUserAgentHeader()
7464
7511
  }
7465
7512
  };
7466
7513
  let lastError;
@@ -9246,6 +9293,7 @@ function resolveOptions(options) {
9246
9293
  if (payload.tokenIn !== undefined) safe['tokenIn'] = payload.tokenIn;
9247
9294
  if (payload.tokenOut !== undefined) safe['tokenOut'] = payload.tokenOut;
9248
9295
  if (payload.txHash !== undefined) safe['txHash'] = payload.txHash;
9296
+ if (payload.correlationId !== undefined) safe['correlationId'] = payload.correlationId;
9249
9297
  if (payload.errorDetails !== undefined) {
9250
9298
  const errorDetails = {
9251
9299
  ...payload.errorDetails.errorCode !== undefined && {
@@ -9316,18 +9364,15 @@ function resolveOptions(options) {
9316
9364
  timeoutHandle.unref();
9317
9365
  }
9318
9366
  try {
9319
- const isNode = isNodeEnvironment();
9320
- const userAgent = getUserAgent();
9321
9367
  await fetch(getLogsUrl(), {
9322
9368
  method: 'POST',
9323
9369
  headers: {
9324
9370
  'Content-Type': 'application/json',
9325
- // Browser restricts setting User-Agent; use X-User-Agent instead.
9326
- ...isNode ? {
9327
- 'User-Agent': userAgent
9328
- } : {
9329
- 'X-User-Agent': userAgent
9330
- }
9371
+ // Browsers forbid setting a user-agent request header, and the custom
9372
+ // fallback header the SDK used instead trips CORS preflight (it isn't
9373
+ // in the telemetry endpoint's `Access-Control-Allow-Headers`), so send
9374
+ // it only in Node; browsers omit it entirely.
9375
+ ...getNodeUserAgentHeader()
9331
9376
  },
9332
9377
  body: JSON.stringify(toSafePayload(payload)),
9333
9378
  signal: controller.signal
@@ -9458,7 +9503,7 @@ function resolveOptions(options) {
9458
9503
  // discards the stack trace, nested `cause`, and any custom Error
9459
9504
  // properties — exactly the context an on-call needs when a
9460
9505
  // resolver-closure regression triggers this path.
9461
- console.warn(`[stablecoin-kits telemetry] dropped error event '${eventType}':`, cause);
9506
+ console.warn(`[stablecoin-kits telemetry] dropped event '${eventType}':`, cause);
9462
9507
  } catch {
9463
9508
  // console.warn itself throwing is the user's environment; nothing more we
9464
9509
  // can do without risking the original operation error.
@@ -9474,7 +9519,9 @@ function resolveOptions(options) {
9474
9519
  sdkVersion: config.sdkVersion,
9475
9520
  eventType,
9476
9521
  timestamp: new Date().toISOString(),
9477
- errorDetails,
9522
+ ...errorDetails !== undefined && {
9523
+ errorDetails
9524
+ },
9478
9525
  clientContext: buildClientContext(),
9479
9526
  ...context?.sourceChain != null && {
9480
9527
  sourceChain: context.sourceChain
@@ -9490,6 +9537,9 @@ function resolveOptions(options) {
9490
9537
  },
9491
9538
  ...context?.txHash != null && {
9492
9539
  txHash: context.txHash
9540
+ },
9541
+ ...context?.correlationId != null && {
9542
+ correlationId: context.correlationId
9493
9543
  }
9494
9544
  };
9495
9545
  }
@@ -9603,7 +9653,7 @@ function resolveOptions(options) {
9603
9653
  }
9604
9654
 
9605
9655
  var name$2 = "@circle-fin/bridge-kit";
9606
- var version$3 = "1.12.0";
9656
+ var version$3 = "1.12.2";
9607
9657
  var pkg$3 = {
9608
9658
  name: name$2,
9609
9659
  version: version$3};
@@ -12107,6 +12157,150 @@ const FAST_TIER_FINALITY_THRESHOLD = 1000;
12107
12157
  return false;
12108
12158
  };
12109
12159
 
12160
+ /**
12161
+ * The zero address, denoting a native-currency fee in a signed quote.
12162
+ */ const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
12163
+ /**
12164
+ * Resolve the fee payment channel for a prepaid-FORWARD burn via `TokenMessengerWithFees`.
12165
+ *
12166
+ * Determines the native `msg.value` and the ERC-20 approvals required, honouring
12167
+ * the quote's `feeToken`:
12168
+ * - Native fee (`feeToken` is the zero address): attach exactly `feeTotalAmount`
12169
+ * as `msg.value`; approve only the burn amount.
12170
+ * - ERC-20 fee equal to the burn token (both USDC — the `isBurnTokenFee` case):
12171
+ * approve a single combined `amount + feeTotalAmount` and skip the redundant
12172
+ * second approval.
12173
+ * - ERC-20 fee different from the burn token: approve the burn amount and the fee
12174
+ * amount separately.
12175
+ *
12176
+ * This encodes only balance/allowance intent; it does not fetch balances. The
12177
+ * caller is responsible for a balance preflight against the fresh quote.
12178
+ *
12179
+ * @param params - The fee token, burn token, burn amount, and total fee amount.
12180
+ * @returns The resolved fee payment plan.
12181
+ * @throws KitError if `amount` or `feeTotalAmount` is negative.
12182
+ *
12183
+ * @example
12184
+ * ```typescript
12185
+ * // Native fee
12186
+ * resolveFeePayment({
12187
+ * feeToken: '0x0000000000000000000000000000000000000000',
12188
+ * burnToken: '0xUSDC...',
12189
+ * amount: 1_000_000n,
12190
+ * feeTotalAmount: 3_500_000n,
12191
+ * })
12192
+ * // → { isNativeFee: true, isBurnTokenFee: false, nativeValue: 3_500_000n,
12193
+ * // approvals: [{ token: '0xUSDC...', amount: 1_000_000n }] }
12194
+ * ```
12195
+ */ const resolveFeePayment = (params)=>{
12196
+ const { feeToken, burnToken, amount, feeTotalAmount } = params;
12197
+ if (typeof amount !== 'bigint' || amount < 0n) {
12198
+ throw createValidationFailedError$1('amount', amount, 'Must be a non-negative bigint');
12199
+ }
12200
+ if (typeof feeTotalAmount !== 'bigint' || feeTotalAmount < 0n) {
12201
+ throw createValidationFailedError$1('feeTotalAmount', feeTotalAmount, 'Must be a non-negative bigint');
12202
+ }
12203
+ const isNativeFee = feeToken.toLowerCase() === ZERO_ADDRESS;
12204
+ const isBurnTokenFee = !isNativeFee && feeToken.toLowerCase() === burnToken.toLowerCase();
12205
+ if (isNativeFee) {
12206
+ return {
12207
+ isNativeFee: true,
12208
+ isBurnTokenFee: false,
12209
+ nativeValue: feeTotalAmount,
12210
+ approvals: [
12211
+ {
12212
+ token: burnToken,
12213
+ amount
12214
+ }
12215
+ ]
12216
+ };
12217
+ }
12218
+ if (isBurnTokenFee) {
12219
+ // Fee and burn draw on the same token — a single combined approval covers
12220
+ // both; the redundant second approval is skipped.
12221
+ return {
12222
+ isNativeFee: false,
12223
+ isBurnTokenFee: true,
12224
+ nativeValue: 0n,
12225
+ approvals: [
12226
+ {
12227
+ token: burnToken,
12228
+ amount: amount + feeTotalAmount
12229
+ }
12230
+ ]
12231
+ };
12232
+ }
12233
+ return {
12234
+ isNativeFee: false,
12235
+ isBurnTokenFee: false,
12236
+ nativeValue: 0n,
12237
+ approvals: [
12238
+ {
12239
+ token: burnToken,
12240
+ amount
12241
+ },
12242
+ {
12243
+ token: feeToken,
12244
+ amount: feeTotalAmount
12245
+ }
12246
+ ]
12247
+ };
12248
+ };
12249
+
12250
+ /**
12251
+ * The ASCII "cctp-forward" magic, hex-encoded (no `0x`), that a forward-friendly
12252
+ * hookData must start with.
12253
+ *
12254
+ * Encoded with `TextEncoder` (a browser-safe Web API) rather than `Buffer.from`
12255
+ * so this module-level constant does not reference the Node `Buffer` global at
12256
+ * import time. App Kit inlines this provider into its bundle without a Buffer polyfill, and a
12257
+ * bare `Buffer` here crashes browser bundles (e.g. Vite) on load — even for apps
12258
+ * that never touch the prepaid FORWARD path. Mirrors `buildForwardingHookData`.
12259
+ */ const CCTP_FORWARD_MAGIC_HEX = Array.from(new TextEncoder().encode(CCTP_FORWARD_MAGIC_PREFIX)).map((byte)=>byte.toString(16).padStart(2, '0')).join('');
12260
+ /**
12261
+ * Determine whether a hookData blob begins with the `cctp-forward` envelope.
12262
+ *
12263
+ * The prepaid FORWARD path requires the GenericExecutor blob to be wrapped in a
12264
+ * `cctp-forward` frame; without it the wrapper reverts `ForwardFeeWithoutHook`.
12265
+ *
12266
+ * @param hookData - The 0x-prefixed hookData hex string.
12267
+ * @returns True when the hookData starts with the `cctp-forward` magic.
12268
+ *
12269
+ * @example
12270
+ * ```typescript
12271
+ * hasForwardHook('0x636374702d666f7277617264...') // true
12272
+ * hasForwardHook('0xdeadbeef') // false
12273
+ * ```
12274
+ */ const hasForwardHook = (hookData)=>{
12275
+ if (typeof hookData !== 'string') {
12276
+ return false;
12277
+ }
12278
+ const normalized = (hookData.startsWith('0x') ? hookData.slice(2) : hookData).toLowerCase();
12279
+ return normalized.startsWith(CCTP_FORWARD_MAGIC_HEX);
12280
+ };
12281
+ /**
12282
+ * Assert that a hookData blob is forward-friendly for the prepaid FORWARD path.
12283
+ *
12284
+ * The prepaid FORWARD path always requests a FORWARD fee item, so the wrapper
12285
+ * requires the hookData to start with a `cctp-forward` frame. Validating this
12286
+ * before the burn surfaces the guaranteed `ForwardFeeWithoutHook` revert as a
12287
+ * typed input error instead of an on-chain failure.
12288
+ *
12289
+ * @param hookData - The 0x-prefixed hookData hex string.
12290
+ * @throws KitError (`INPUT_VALIDATION_FAILED`) if the hookData is missing or lacks
12291
+ * the `cctp-forward` frame.
12292
+ *
12293
+ * @example
12294
+ * ```typescript
12295
+ * assertForwardHookData(geForwardHookData) // ok
12296
+ * assertForwardHookData('0xdeadbeef') // throws — would revert ForwardFeeWithoutHook
12297
+ * ```
12298
+ */ const assertForwardHookData = (hookData)=>{
12299
+ if (!hasForwardHook(hookData)) {
12300
+ throw createValidationFailedError$1('hookData', hookData, 'Prepaid FORWARD burns require a cctp-forward-wrapped hookData; without it the TokenMessengerWithFees wrapper reverts ForwardFeeWithoutHook');
12301
+ }
12302
+ };
12303
+
12110
12304
  /**
12111
12305
  * Type guard to validate the forwardFee object structure.
12112
12306
  *
@@ -13038,6 +13232,109 @@ const assertCCTPv2BridgeParamsSymbol = Symbol('assertCCTPv2BridgeParams');
13038
13232
  }
13039
13233
  }
13040
13234
 
13235
+ /**
13236
+ * Assert that `params` is a well-formed {@link BurnWithFeesParams} object.
13237
+ *
13238
+ * Validates the full public-boundary input before any field destructuring,
13239
+ * `BigInt()` coercion, or adapter preparation runs, so malformed JS-caller
13240
+ * inputs always produce typed `KitError` validation failures.
13241
+ *
13242
+ * Checks performed (in order):
13243
+ * - `params` must be a non-null plain object
13244
+ * - `source` — valid CCTP v2 wallet context (via `assertCCTPv2WalletContext`)
13245
+ * - `destinationChain` — present and supports CCTP v2
13246
+ * - source and destination chains must both be testnet or both mainnet
13247
+ * - source and destination chains must differ
13248
+ * - `executor` — non-empty string
13249
+ * - `amount` — bigint or non-empty string coercible to bigint
13250
+ * - `feeTotalAmount` — bigint or non-empty string coercible to bigint
13251
+ * - `feeToken` — valid EVM address (`0x` + 40 hex chars)
13252
+ * - `claim.signedQuote` — valid `0x`-prefixed hex string
13253
+ * - `claim.refundAddress` — valid EVM address
13254
+ * - `hookData` — valid `0x`-prefixed hex string when present
13255
+ *
13256
+ * @param params - The value to validate.
13257
+ * @throws {KitError} If any field is missing or invalid.
13258
+ *
13259
+ * @example
13260
+ * ```typescript
13261
+ * assertBurnWithFeesParams(params)
13262
+ * // params is now typed as BurnWithFeesParams and safe to use
13263
+ * const { source, destinationChain, amount } = params
13264
+ * ```
13265
+ */ function assertBurnWithFeesParams(params) {
13266
+ if (params === null || typeof params !== 'object' || Array.isArray(params)) {
13267
+ throw createValidationFailedError$1('params', params, 'Must be a non-null plain object');
13268
+ }
13269
+ const p = params;
13270
+ // Source wallet context
13271
+ assertCCTPv2WalletContext(p['source']);
13272
+ const source = p['source'];
13273
+ // destinationChain
13274
+ const destinationChain = p['destinationChain'];
13275
+ if (destinationChain === null || destinationChain === undefined || typeof destinationChain !== 'object') {
13276
+ throw createValidationFailedError$1('destinationChain', destinationChain, 'Must be a chain definition object');
13277
+ }
13278
+ if (!isCCTPV2Supported(destinationChain)) {
13279
+ throw createValidationFailedError$1('destinationChain', destinationChain, 'destinationChain must support CCTP v2');
13280
+ }
13281
+ const dest = destinationChain;
13282
+ // Testnet / mainnet mismatch
13283
+ if (source.chain.isTestnet !== dest.isTestnet) {
13284
+ throw createNetworkMismatchError(source.chain, dest);
13285
+ }
13286
+ // Same-chain guard
13287
+ if (source.chain.name === dest.name) {
13288
+ throw createUnsupportedRouteError(source.chain.name, dest.name);
13289
+ }
13290
+ // executor
13291
+ const executor = p['executor'];
13292
+ if (typeof executor !== 'string' || executor === '') {
13293
+ throw createValidationFailedError$1('executor', executor, 'A GenericExecutor address is required (used for both mintRecipient and destinationCaller)');
13294
+ }
13295
+ // amount
13296
+ const rawAmount = p['amount'];
13297
+ if (typeof rawAmount !== 'bigint' && typeof rawAmount !== 'string' || rawAmount === '') {
13298
+ throw createValidationFailedError$1('amount', rawAmount, 'Must be a bigint or a numeric string');
13299
+ }
13300
+ try {
13301
+ BigInt(rawAmount);
13302
+ } catch {
13303
+ throw createValidationFailedError$1('amount', rawAmount, 'Must be a valid integer value coercible to bigint');
13304
+ }
13305
+ // feeTotalAmount
13306
+ const rawFee = p['feeTotalAmount'];
13307
+ if (typeof rawFee !== 'bigint' && typeof rawFee !== 'string' || rawFee === '') {
13308
+ throw createValidationFailedError$1('feeTotalAmount', rawFee, 'Must be a bigint or a numeric string');
13309
+ }
13310
+ try {
13311
+ BigInt(rawFee);
13312
+ } catch {
13313
+ throw createValidationFailedError$1('feeTotalAmount', rawFee, 'Must be a valid integer value coercible to bigint');
13314
+ }
13315
+ // feeToken
13316
+ if (!evmAddressSchema.safeParse(p['feeToken']).success) {
13317
+ throw createValidationFailedError$1('feeToken', p['feeToken'], 'Must be a valid EVM address (use the zero address for a native fee)');
13318
+ }
13319
+ // claim
13320
+ const rawClaim = p['claim'];
13321
+ if (rawClaim === null || typeof rawClaim !== 'object' || Array.isArray(rawClaim)) {
13322
+ throw createValidationFailedError$1('claim', rawClaim, 'Must be an object with signedQuote and refundAddress');
13323
+ }
13324
+ const claim = rawClaim;
13325
+ if (!hexStringSchema.safeParse(claim['signedQuote']).success) {
13326
+ throw createValidationFailedError$1('claim.signedQuote', claim['signedQuote'], 'Must be a valid hex string starting with 0x');
13327
+ }
13328
+ if (!evmAddressSchema.safeParse(claim['refundAddress']).success) {
13329
+ throw createValidationFailedError$1('claim.refundAddress', claim['refundAddress'], 'Must be a valid EVM address');
13330
+ }
13331
+ // hookData (optional)
13332
+ const hookData = p['hookData'];
13333
+ if (hookData !== undefined && hookData !== '' && !hexStringSchema.safeParse(hookData).success) {
13334
+ throw createValidationFailedError$1('hookData', hookData, 'Must be a valid hex string starting with 0x');
13335
+ }
13336
+ }
13337
+
13041
13338
  /**
13042
13339
  * CCTP bridge step names that can occur in the bridging flow.
13043
13340
  *
@@ -14246,10 +14543,15 @@ const mockAttestationMessage = {
14246
14543
  const burnCallData = burnRequest.getCallData();
14247
14544
  // batchExecute may throw before submission (wallet declined) but never
14248
14545
  // after — post-submission errors are returned as empty receipts.
14546
+ // The sender is threaded for adapters whose execution is routed through a
14547
+ // signing strategy (which has no wallet account to read it from); the
14548
+ // wallet-client path ignores it.
14249
14549
  const batchResult = await adapter.batchExecute([
14250
14550
  approveCallData,
14251
14551
  burnCallData
14252
- ], chain);
14552
+ ], chain, {
14553
+ fromAddress: params.source.address
14554
+ });
14253
14555
  const approveReceipt = batchResult.receipts[0];
14254
14556
  const burnReceipt = batchResult.receipts[1];
14255
14557
  const approveStep = await buildBatchedStep('approve', approveReceipt, batchResult.batchId, adapter, chain, batchResult.statusCode, batchResult.error);
@@ -14392,7 +14694,7 @@ const mockAttestationMessage = {
14392
14694
  return step;
14393
14695
  }
14394
14696
 
14395
- var version$2 = "1.9.0";
14697
+ var version$2 = "1.10.1";
14396
14698
  var pkg$2 = {
14397
14699
  version: version$2};
14398
14700
 
@@ -15547,7 +15849,7 @@ function assertCCTPV2Config(config) {
15547
15849
  throw new Error(`Failed to resolve operation context: ${error instanceof Error ? error.message : String(error)}`);
15548
15850
  }
15549
15851
  // Resolve spender address with proper error handling
15550
- const spenderAddress = resolveCCTPV2ContractAddress(chain);
15852
+ const spenderAddress = resolveCCTPV2ContractAddress(chain, 'tokenMessenger');
15551
15853
  // Prepare action parameters
15552
15854
  const actionParams = {
15553
15855
  amount: BigInt(amount),
@@ -16027,6 +16329,106 @@ function assertCCTPV2Config(config) {
16027
16329
  return await source.adapter.prepareAction('cctp.v2.depositForBurn', actionParams, resolvedContext);
16028
16330
  }
16029
16331
  /**
16332
+ * Prepare a prepaid-FORWARD burn through the `TokenMessengerWithFees` wrapper.
16333
+ *
16334
+ * Builds the source-chain `depositForBurnWithHookAndFees` call for the
16335
+ * GenericExecutor FORWARD path: fees are collected up front on the source chain
16336
+ * against a signed quote, `mintRecipient` and `destinationCaller` are both set to
16337
+ * the GenericExecutor, and the GE `hookData` is passed through unchanged.
16338
+ *
16339
+ * This is the low-level on-chain primitive behind the UBK `fastCrossChainDeposit`
16340
+ * and Bridge Kit `bridge({ deposit })` flows. The `hookData` and signed-quote
16341
+ * `claim` are produced elsewhere and passed in here:
16342
+ * - `hookData`: `buildForwardingHookDataWithPayload(version,
16343
+ * buildDepositForGenericExecutorPayload(...).hookData)` from `@core/utils`.
16344
+ * - `claim.signedQuote` / `feeToken` / `feeTotalAmount`: from `fetchFeeQuote`
16345
+ * (`@circle-fin/provider-fee-v1`), whose FORWARD item must be bound to the
16346
+ * SAME `hookData` and executor `destinationCaller` used here.
16347
+ *
16348
+ * The returned approvals and burn are NOT executed — the caller executes the
16349
+ * approvals first (in order) and then the burn. The fee payment channel matches
16350
+ * the quote's `feeToken`:
16351
+ * - Native fee: exactly `feeTotalAmount` is attached as the burn's `msg.value`;
16352
+ * only the burn amount is approved.
16353
+ * - USDC fee (same token as the burn): a single combined `amount + feeTotalAmount`
16354
+ * approval covers both; the redundant second approval is skipped.
16355
+ *
16356
+ * @typeParam TFromAdapterCapabilities - The source adapter's capabilities.
16357
+ * @param params - The burn amount, executor, hookData, signed-quote claim, and fee.
16358
+ * @returns The prepared approvals, the prepared burn, and the resolved fee plan.
16359
+ * @throws {KitError} If the wallet context is invalid, `destinationChain` does not
16360
+ * support CCTP v2, the executor is missing, `amount` or `feeTotalAmount` is not
16361
+ * a bigint or a numeric string coercible to bigint, the hookData lacks a
16362
+ * `cctp-forward` frame (guaranteed `ForwardFeeWithoutHook`), or the operation
16363
+ * context cannot be resolved.
16364
+ *
16365
+ * @example
16366
+ * ```typescript
16367
+ * const { approvals, burn } = await provider.burnWithFees({
16368
+ * source,
16369
+ * destinationChain: Arc,
16370
+ * amount: 1_000_000n,
16371
+ * executor: genericExecutorAddress,
16372
+ * hookData: geForwardHookData,
16373
+ * claim: { signedQuote: '0x01...', refundAddress: userAddress },
16374
+ * feeToken: '0x0000000000000000000000000000000000000000', // native
16375
+ * feeTotalAmount: 3_500_000n,
16376
+ * })
16377
+ * for (const approval of approvals) await approval.execute()
16378
+ * const txHash = await burn.execute()
16379
+ * ```
16380
+ */ async burnWithFees(params) {
16381
+ assertBurnWithFeesParams(params);
16382
+ const { source, destinationChain, executor, hookData, claim, feeToken } = params;
16383
+ const amount = BigInt(params.amount);
16384
+ const feeTotalAmount = BigInt(params.feeTotalAmount);
16385
+ // Coupling guard: the prepaid FORWARD path always requests a FORWARD fee item,
16386
+ // so the hookData must carry a cctp-forward frame; otherwise the wrapper
16387
+ // reverts ForwardFeeWithoutHook. Surface it as a typed input error up front.
16388
+ assertForwardHookData(hookData);
16389
+ const burnToken = source.chain.usdcAddress;
16390
+ const feePayment = resolveFeePayment({
16391
+ feeToken,
16392
+ burnToken,
16393
+ amount,
16394
+ feeTotalAmount
16395
+ });
16396
+ // Resolve operation context from the source wallet context.
16397
+ const operationContext = this.extractOperationContext(source);
16398
+ let resolvedContext;
16399
+ try {
16400
+ resolvedContext = await resolveOperationContext(source.adapter, operationContext);
16401
+ } catch (error) {
16402
+ throw createValidationFailedError$1('source.adapter', undefined, `Failed to resolve operation context: ${error instanceof Error ? error.message : String(error)}`);
16403
+ }
16404
+ const context = resolvedContext;
16405
+ const wrapperAddress = resolveCCTPV2ContractAddress(source.chain, 'tokenMessengerWithFees');
16406
+ // Build the ERC-20 approvals to the wrapper (burn token, plus a distinct fee
16407
+ // token only when the fee is not paid in the burn token).
16408
+ const approvals = await Promise.all(feePayment.approvals.map(async (approval)=>source.adapter.prepareAction('token.approve', {
16409
+ tokenAddress: approval.token,
16410
+ delegate: wrapperAddress,
16411
+ amount: approval.amount
16412
+ }, context)));
16413
+ // Build the burn: mintRecipient AND destinationCaller are both the executor.
16414
+ const burn = await source.adapter.prepareAction('cctp.v2.depositForBurnWithFees', {
16415
+ fromChain: source.chain,
16416
+ toChain: destinationChain,
16417
+ amount,
16418
+ mintRecipient: executor,
16419
+ destinationCaller: executor,
16420
+ hookData,
16421
+ claim,
16422
+ feeToken,
16423
+ feeTotalAmount
16424
+ }, context);
16425
+ return {
16426
+ approvals,
16427
+ burn,
16428
+ feePayment
16429
+ };
16430
+ }
16431
+ /**
16030
16432
  * Waits for a transaction to be mined and confirmed on the blockchain.
16031
16433
  *
16032
16434
  * This method should block until the transaction is confirmed on the blockchain.
@@ -16906,7 +17308,7 @@ registerKit(`${pkg$3.name}/${pkg$3.version}`);
16906
17308
  };
16907
17309
 
16908
17310
  var name$1 = "@circle-fin/swap-kit";
16909
- var version$1 = "1.3.2";
17311
+ var version$1 = "1.5.0";
16910
17312
  var pkg$1 = {
16911
17313
  name: name$1,
16912
17314
  version: version$1};
@@ -16971,7 +17373,10 @@ const optionalChainIdentifierField = chainIdentifierField.optional();
16971
17373
  }).min(1, 'kitKey must be a non-empty string').optional(),
16972
17374
  provider: z.string({
16973
17375
  invalid_type_error: 'provider must be a string'
16974
- }).min(1, 'provider must be a non-empty string').optional()
17376
+ }).min(1, 'provider must be a non-empty string').optional(),
17377
+ batchTransactions: z.boolean({
17378
+ invalid_type_error: 'batchTransactions must be a boolean'
17379
+ }).optional()
16975
17380
  });
16976
17381
  /**
16977
17382
  * Zod schema for adapter context.
@@ -17410,7 +17815,7 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
17410
17815
  /**
17411
17816
  * Circle Stablecoin Service API Key.
17412
17817
  * Must be a valid API key format.
17413
- */ apiKey: apiKeySchema
17818
+ */ apiKey: apiKeySchema.optional()
17414
17819
  }).superRefine(requireCrossChainQuoteToAddress);
17415
17820
  /**
17416
17821
  * Zod schema for validating CreateSwapRequest parameters.
@@ -17468,7 +17873,7 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
17468
17873
  /**
17469
17874
  * Circle Stablecoin Service API Key.
17470
17875
  * Must be a valid API key format.
17471
- */ apiKey: apiKeySchema
17876
+ */ apiKey: apiKeySchema.optional()
17472
17877
  });
17473
17878
  /**
17474
17879
  * Zod schema for validating GetSwapStatusResponse data.
@@ -17504,7 +17909,7 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
17504
17909
  toChain: z.string({
17505
17910
  invalid_type_error: 'toChain must be a string'
17506
17911
  }).min(1, 'toChain must be a non-empty string if provided').optional(),
17507
- apiKey: apiKeySchema
17912
+ apiKey: apiKeySchema.optional()
17508
17913
  });
17509
17914
  /**
17510
17915
  * Zod schema for validating CreateSwapResponse payloads.
@@ -17513,13 +17918,15 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
17513
17918
  required_error: 'fee token is required',
17514
17919
  invalid_type_error: 'fee token must be a string'
17515
17920
  }).min(1, 'fee token must be a non-empty string'),
17516
- amount: feeAmountSchema
17921
+ amount: feeAmountSchema,
17922
+ decimals: z.number().int('fee token decimals must be an integer').nonnegative('fee token decimals must be a non-negative integer').optional(),
17923
+ symbol: z.string({
17924
+ invalid_type_error: 'fee token symbol must be a string'
17925
+ }).min(1, 'fee token symbol must be a non-empty string').optional()
17517
17926
  });
17518
17927
  /**
17519
17928
  * Developer fee item schema with basis field.
17520
- */ const createSwapDeveloperFeeItemSchema = z.object({
17521
- token: z.string().min(1, 'fee token must be a non-empty string'),
17522
- amount: feeAmountSchema,
17929
+ */ const createSwapDeveloperFeeItemSchema = createSwapFeeItemSchema.extend({
17523
17930
  basis: z.enum([
17524
17931
  'inputAmount',
17525
17932
  'estimatedAmount'
@@ -17611,7 +18018,7 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
17611
18018
  addresses: z.array(z.string({
17612
18019
  invalid_type_error: 'addresses entries must be strings'
17613
18020
  }).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(),
17614
- apiKey: apiKeySchema
18021
+ apiKey: apiKeySchema.optional()
17615
18022
  });
17616
18023
  /**
17617
18024
  * Zod schema for validating GetTokenRatesResponse payloads.
@@ -17642,6 +18049,16 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
17642
18049
  required_error: 'estimatedAmount is required',
17643
18050
  invalid_type_error: 'estimatedAmount must be a string'
17644
18051
  }).min(1, 'estimatedAmount must be a non-empty string'),
18052
+ // Per-swap join key echoed back verbatim on success telemetry. Optional so a
18053
+ // not-yet-upgraded service (no field) still validates during rollout. A
18054
+ // malformed/non-UUID value is coerced to `undefined` (no telemetry id) rather
18055
+ // than throwing: this is a telemetry-only field (stripped from the developer
18056
+ // result, never used for control flow), so it must not be able to abort the
18057
+ // swap via `parseCreateSwapResponse().parse()`. Mirrors the best-effort,
18058
+ // never-throw contract of the rest of the telemetry stack. Implemented with
18059
+ // `preprocess` rather than Zod's `.catch()` because static analysis misreads
18060
+ // `.catch` on the schema chain as an unhandled Promise (S7785).
18061
+ correlationId: z.preprocess((value)=>z.string().uuid().safeParse(value).success ? value : undefined, z.string().optional()),
17645
18062
  config: createSwapRequestBaseSchema.shape.config.optional(),
17646
18063
  fees: createSwapFeesSchema.optional(),
17647
18064
  transaction: createSwapTransactionSchema
@@ -18834,7 +19251,7 @@ new Set(Object.values(Blockchain));
18834
19251
  registerKit(`${pkg$1.name}/${pkg$1.version}`);
18835
19252
 
18836
19253
  var name = "@circle-fin/earn-kit";
18837
- var version = "1.2.2";
19254
+ var version = "1.4.0";
18838
19255
  var pkg = {
18839
19256
  name: name,
18840
19257
  version: version};
@@ -18964,7 +19381,11 @@ const bridgeFeeTokenSchema = hexAddressSchema;
18964
19381
  asset: z.string(),
18965
19382
  assetAddress: z.string(),
18966
19383
  lltv: z.number(),
18967
- supplyUsd: z.number()
19384
+ supplyUsd: z.number(),
19385
+ // Optional during the expand/contract window (a backend that predates the
19386
+ // field omits the key), mirroring the `.optional()` facets on the base
19387
+ // schema; `null` when the product exposes no per-market allocation (V2).
19388
+ allocationPct: z.number().nullable().optional()
18968
19389
  });
18969
19390
  /**
18970
19391
  * Zod schema for a Morpho vault warning in the API response.
@@ -18978,7 +19399,74 @@ const bridgeFeeTokenSchema = hexAddressSchema;
18978
19399
  ])
18979
19400
  });
18980
19401
  /**
18981
- * Zod schema for a single vault info object in the API response.
19402
+ * Zod schema for the manager (curator) facet in the API response.
19403
+ *
19404
+ * @internal
19405
+ */ const managerSchema = z.object({
19406
+ name: z.string(),
19407
+ address: z.string().optional(),
19408
+ // Only 'curator' is emitted today (Morpho V1/V2). Additional manager roles
19409
+ // are added here as the providers that emit them land, rather than shipped
19410
+ // speculatively.
19411
+ type: z.enum([
19412
+ 'curator'
19413
+ ])
19414
+ });
19415
+ /**
19416
+ * Zod schema for the APY profile facet in the API response.
19417
+ *
19418
+ * @internal
19419
+ */ const apyProfileSchema = z.object({
19420
+ current: z.number(),
19421
+ native: z.number().nullable(),
19422
+ d7: z.number().nullable(),
19423
+ d30: z.number().nullable(),
19424
+ d90: z.number().nullable(),
19425
+ rewardShare: z.number().nullable(),
19426
+ source: z.string().optional(),
19427
+ asOf: z.string().optional()
19428
+ });
19429
+ /**
19430
+ * Zod schema for the fee split facet in the API response.
19431
+ *
19432
+ * @internal
19433
+ */ const feeInfoSchema = z.object({
19434
+ performance: z.number().nullable(),
19435
+ management: z.number().nullable()
19436
+ });
19437
+ /**
19438
+ * Zod schema for the liquidity profile facet in the API response.
19439
+ *
19440
+ * `totalSupply` is the outstanding vault share tokens (ERC4626 totalSupply);
19441
+ * it is validated as a raw JSON amount, like `totalDeposits`/`available`.
19442
+ *
19443
+ * @internal
19444
+ */ const liquidityProfileSchema = z.object({
19445
+ totalDeposits: amountJsonSchema,
19446
+ available: amountJsonSchema,
19447
+ totalSupply: amountJsonSchema,
19448
+ status: z.enum([
19449
+ 'active',
19450
+ 'low_liquidity'
19451
+ ])
19452
+ });
19453
+ /**
19454
+ * Zod schema for the risk signals facet in the API response.
19455
+ *
19456
+ * @internal
19457
+ */ const riskSignalsSchema = z.object({
19458
+ circleSentinel: z.boolean(),
19459
+ warnings: z.array(vaultWarningSchema).optional(),
19460
+ earnKitWarnings: z.array(z.string()).optional()
19461
+ });
19462
+ /**
19463
+ * Zod schema for the universal earn-opportunity base in the API response.
19464
+ *
19465
+ * Retains every existing deprecated flat field (kept validated through the
19466
+ * expand/contract window so default-strip does not drop them) and adds the
19467
+ * new nested facets. The nested facets are `.optional()` during the
19468
+ * transition so the SDK still validates against a not-yet-fully-deployed
19469
+ * backend; they become required after Expand ships.
18982
19470
  *
18983
19471
  * @internal
18984
19472
  */ const vaultInfoResponseSchema = z.object({
@@ -19003,6 +19491,96 @@ const bridgeFeeTokenSchema = hexAddressSchema;
19003
19491
  warnings: z.array(vaultWarningSchema).optional(),
19004
19492
  earnKitWarnings: z.array(z.string()).optional()
19005
19493
  });
19494
+ /**
19495
+ * Shared base schema: existing flat fields (kept) plus the new nested
19496
+ * facets and neutral identity. Facets are `.optional()` during the
19497
+ * transition; flip to required once the backend is confirmed emitting.
19498
+ *
19499
+ * @internal
19500
+ */ const earnBaseSchema = vaultInfoResponseSchema.extend({
19501
+ address: z.string().optional(),
19502
+ asOf: z.string().optional(),
19503
+ manager: managerSchema.nullable().optional(),
19504
+ apyProfile: apyProfileSchema.optional(),
19505
+ fee: feeInfoSchema.optional(),
19506
+ liquidityProfile: liquidityProfileSchema.optional(),
19507
+ riskSignals: riskSignalsSchema.optional()
19508
+ });
19509
+ /**
19510
+ * Zod schema for the `vault` opportunity variant.
19511
+ *
19512
+ * @internal
19513
+ */ const vaultOpportunitySchema = earnBaseSchema.extend({
19514
+ productType: z.literal('vault'),
19515
+ collateral: z.array(collateralSchema)
19516
+ });
19517
+ /**
19518
+ * Discriminated union over `productType`. Add union members here as new
19519
+ * product types (e.g. `lending_market`, `rwa_token`) land.
19520
+ *
19521
+ * @internal
19522
+ */ const earnOpportunityVariants = [
19523
+ vaultOpportunitySchema
19524
+ ];
19525
+ /** @internal */ const earnOpportunitySchema = z.discriminatedUnion('productType', earnOpportunityVariants);
19526
+ /** Product types this SDK version knows how to parse. */ const knownProductTypes = new Set(earnOpportunityVariants.map((variant)=>variant.shape.productType.value));
19527
+ /**
19528
+ * Tolerant list parser for earn opportunities.
19529
+ *
19530
+ * `z.discriminatedUnion` throws on an unrecognized discriminant and
19531
+ * `z.array` fails the whole array if any element fails. Two migration-window
19532
+ * cases are smoothed over here so neither breaks an already-shipped SDK:
19533
+ *
19534
+ * - A backend that predates `productType` omits it entirely. `'vault'` was the
19535
+ * only opportunity type then, so default a missing discriminant to `'vault'`
19536
+ * rather than dropping every vault the backend returns.
19537
+ * - A future backend adds a *second* `productType` this SDK version does not
19538
+ * know. Drop those elements (a present-but-unrecognized discriminant) instead
19539
+ * of rejecting the whole list.
19540
+ *
19541
+ * Only the drop above is a *tolerant* case. Anything that is not a plain object
19542
+ * with a present-but-unknown string `productType` — `null`, `undefined`,
19543
+ * primitives, or an object whose `productType` is malformed — is passed through
19544
+ * untouched so `z.array(earnOpportunitySchema)` reports it as a normal
19545
+ * validation failure. It is deliberately not silently dropped (which would hide
19546
+ * malformed backend data) and never throws here (an unguarded property read on
19547
+ * a non-object would escape `safeParse` as a raw `TypeError` instead of a
19548
+ * `ZodError`).
19549
+ *
19550
+ * @internal
19551
+ */ const earnOpportunityListSchema = z.preprocess((raw)=>{
19552
+ if (!Array.isArray(raw)) {
19553
+ return raw;
19554
+ }
19555
+ // Array.isArray narrows `raw` to `any[]`; view it as `unknown[]` so the
19556
+ // map/filter chain stays type-safe and no `any` leaks into the return.
19557
+ const entries = raw;
19558
+ return entries.map((entry)=>{
19559
+ // Only touch plain objects; non-objects fall through to fail validation.
19560
+ if (typeof entry !== 'object' || entry === null) {
19561
+ return entry;
19562
+ }
19563
+ const record = entry;
19564
+ // Older backend predating productType: default to the only type then.
19565
+ return record.productType === undefined ? {
19566
+ ...record,
19567
+ productType: 'vault'
19568
+ } : record;
19569
+ }).filter((entry)=>{
19570
+ // Drop ONLY a present-but-unknown string discriminant (a future
19571
+ // productType this SDK version doesn't know). Everything else —
19572
+ // non-objects, a non-string productType — flows through to
19573
+ // z.array(earnOpportunitySchema) and fails/passes validation normally.
19574
+ if (typeof entry !== 'object' || entry === null) {
19575
+ return true;
19576
+ }
19577
+ const productType = entry.productType;
19578
+ if (typeof productType !== 'string') {
19579
+ return true;
19580
+ }
19581
+ return knownProductTypes.has(productType);
19582
+ });
19583
+ }, z.array(earnOpportunitySchema));
19006
19584
  // ---------------------------------------------------------------------------
19007
19585
  // Position response schema
19008
19586
  // ---------------------------------------------------------------------------
@@ -19132,6 +19710,7 @@ const positionPnlSchema = z.discriminatedUnion('status', [
19132
19710
  *
19133
19711
  * @internal
19134
19712
  */ const depositPayloadSchema = z.object({
19713
+ execId: bridgeDepositExecIdSchema,
19135
19714
  executionParams: depositExecutionParamsSchema,
19136
19715
  signature: hexSignatureSchema
19137
19716
  });
@@ -19223,6 +19802,21 @@ const bridgeDepositPrepareReviewSchema = z.object({
19223
19802
  amount: amountJsonSchema,
19224
19803
  vaultAddress: hexAddressSchema
19225
19804
  }).passthrough();
19805
+ /** @internal */ const bridgeQuoteExpirySchema = z.discriminatedUnion('mode', [
19806
+ z.object({
19807
+ mode: z.literal('TIMESTAMP'),
19808
+ expiresAt: z.string().datetime({
19809
+ offset: true
19810
+ })
19811
+ }),
19812
+ z.object({
19813
+ mode: z.literal('BLOCK_NUMBER'),
19814
+ expiresAtBlock: z.number().int(),
19815
+ blockEstimatedAt: z.string().datetime({
19816
+ offset: true
19817
+ }).optional()
19818
+ })
19819
+ ]).optional().catch(undefined);
19226
19820
  /**
19227
19821
  * Zod schema for the bridge deposit prepare payload.
19228
19822
  *
@@ -19234,6 +19828,10 @@ const bridgeDepositPrepareReviewSchema = z.object({
19234
19828
  execId: bridgeDepositExecIdSchema,
19235
19829
  erc3009TypedData: bridgeDepositPreparedBundleSchema,
19236
19830
  expiresAt: z.string().datetime(),
19831
+ quoteIssuedAt: z.string().datetime({
19832
+ offset: true
19833
+ }).optional().catch(undefined),
19834
+ quoteExpiry: bridgeQuoteExpirySchema,
19237
19835
  review: bridgeDepositPrepareReviewSchema
19238
19836
  });
19239
19837
  /**
@@ -19299,6 +19897,7 @@ const bridgeDepositPrepareReviewSchema = z.object({
19299
19897
  *
19300
19898
  * @internal
19301
19899
  */ const withdrawPayloadSchema = z.object({
19900
+ execId: bridgeDepositExecIdSchema,
19302
19901
  executionParams: withdrawExecutionParamsSchema,
19303
19902
  signature: hexSignatureSchema
19304
19903
  });
@@ -19312,6 +19911,27 @@ const bridgeDepositPrepareReviewSchema = z.object({
19312
19911
  data: withdrawPayloadSchema
19313
19912
  });
19314
19913
  // ---------------------------------------------------------------------------
19914
+ // Transaction report response schema
19915
+ // ---------------------------------------------------------------------------
19916
+ /**
19917
+ * Zod schema for the transaction report payload inside the API `data` envelope.
19918
+ *
19919
+ * The Earn Service returns an empty payload (`{"data":{}}`) on success, so the
19920
+ * schema accepts any object shape and does not require specific fields.
19921
+ *
19922
+ * @internal
19923
+ */ const transactionReportPayloadSchema = z.object({}).passthrough();
19924
+ /**
19925
+ * Zod schema for the `POST /v1/earnKit/transactions/report` API response.
19926
+ *
19927
+ * The Earn Service API wraps the transaction report payload in a `data`
19928
+ * envelope.
19929
+ *
19930
+ * @internal
19931
+ */ z.object({
19932
+ data: transactionReportPayloadSchema
19933
+ });
19934
+ // ---------------------------------------------------------------------------
19315
19935
  // Claim rewards response schema
19316
19936
  // ---------------------------------------------------------------------------
19317
19937
  /**
@@ -19372,6 +19992,30 @@ const bridgeDepositPrepareReviewSchema = z.object({
19372
19992
  token: z.string(),
19373
19993
  amount: amountJsonSchema
19374
19994
  });
19995
+ /**
19996
+ * Zod schema for a native gas-fee entry in an EarnKit quote response.
19997
+ *
19998
+ * The Earn Service backend estimates gas server-side and returns one entry per
19999
+ * action (`Approve`, `Deposit`, `Withdraw`). A successful estimate carries
20000
+ * `fees` in the SDK `EstimatedGas` shape (`{ gas, gasPrice, fee }`), each a raw
20001
+ * integer string in the chain's native base units. When the backend cannot
20002
+ * estimate an action it returns `fees: null` with an `error` message instead.
20003
+ *
20004
+ * The schema deliberately validates almost nothing beyond the envelope: `name`
20005
+ * is optional and `fees` is entirely unvalidated (`unknown`). ALL validation
20006
+ * of `fees` — that it is an object at all, and that `gas`, `gasPrice`, and
20007
+ * `fee` are parseable integer strings — is deferred to {@link toQuoteGasFees},
20008
+ * which degrades a malformed entry to a `fees: null` soft failure. This is
20009
+ * intentional: gas is best-effort, so a single unparseable gas entry (a wrong
20010
+ * type such as `fees: 123` or `fees: 'bad'`, a missing field, or a non-numeric
20011
+ * `fee`) must never fail Zod validation and reject the entire quote.
20012
+ *
20013
+ * @internal
20014
+ */ const quoteGasFeeSchema = z.object({
20015
+ name: z.string().optional(),
20016
+ fees: z.unknown(),
20017
+ error: z.string().optional()
20018
+ }).passthrough();
19375
20019
  /**
19376
20020
  * Zod schema for the inner deposit quote payload.
19377
20021
  *
@@ -19387,7 +20031,8 @@ const bridgeDepositPrepareReviewSchema = z.object({
19387
20031
  expectedShares: amountJsonSchema,
19388
20032
  sharePrice: z.string(),
19389
20033
  currentApy: z.number(),
19390
- fees: z.array(feeSchema).optional()
20034
+ fees: z.array(feeSchema).optional(),
20035
+ gasFees: z.array(quoteGasFeeSchema).optional()
19391
20036
  });
19392
20037
  /**
19393
20038
  * Zod schema for the `POST /v1/earnKit/deposit/quote` API response.
@@ -19414,6 +20059,7 @@ const bridgeDepositPrepareReviewSchema = z.object({
19414
20059
  sharePrice: z.string(),
19415
20060
  maxWithdrawable: amountJsonSchema,
19416
20061
  fees: z.array(feeSchema),
20062
+ gasFees: z.array(quoteGasFeeSchema).optional(),
19417
20063
  warnings: z.array(z.string()).optional()
19418
20064
  });
19419
20065
  /**
@@ -19471,7 +20117,7 @@ const bridgeDepositPrepareReviewSchema = z.object({
19471
20117
  *
19472
20118
  * @internal
19473
20119
  */ const getVaultsPayloadSchema = z.object({
19474
- vaults: z.array(vaultInfoResponseSchema),
20120
+ vaults: earnOpportunityListSchema,
19475
20121
  errors: z.array(vaultErrorSchema)
19476
20122
  });
19477
20123
  /**
@@ -19501,7 +20147,7 @@ const bridgeDepositPrepareReviewSchema = z.object({
19501
20147
  *
19502
20148
  * @internal
19503
20149
  */ const exploreVaultsPayloadSchema = z.object({
19504
- vaults: z.array(vaultInfoResponseSchema),
20150
+ vaults: earnOpportunityListSchema,
19505
20151
  pagination: explorePaginationSchema
19506
20152
  });
19507
20153
  /**
@@ -20069,6 +20715,8 @@ function hasCrossChainDepositQuoteShape(params) {
20069
20715
  config: earnConfigSchema.optional()
20070
20716
  });
20071
20717
 
20718
+ /** SDK name used in telemetry payloads. */ resolveKitSdkName(pkg.name);
20719
+
20072
20720
  // Auto-register this kit for user agent tracking
20073
20721
  registerKit(`${pkg.name}/${pkg.version}`);
20074
20722