@circle-fin/app-kit 1.10.0 → 1.12.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.
@@ -18,6 +18,17 @@
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');
@@ -29,6 +40,7 @@ var web3_js = require('@solana/web3.js');
29
40
  require('bn.js');
30
41
  require('@coral-xyz/anchor');
31
42
  require('@noble/curves/ed25519');
43
+ require('viem');
32
44
  var keccak256 = require('@ethersproject/keccak256');
33
45
 
34
46
  function _interopDefault (e) { return e && e.__esModule ? e.default : e; }
@@ -51,6 +63,27 @@ var bs58__default = /*#__PURE__*/_interopDefault(bs58);
51
63
  * }
52
64
  * ```
53
65
  */ const isNodeEnvironment = ()=>typeof process !== 'undefined' && typeof process.versions === 'object' && typeof process.versions.node === 'string';
66
+ /**
67
+ * Return the SDK User-Agent request header only when running in Node.js.
68
+ *
69
+ * Browsers forbid manually setting `User-Agent`, and a custom fallback header
70
+ * can trigger CORS preflight. Non-Node server runtimes also omit this optional
71
+ * attribution header because they cannot set it reliably.
72
+ *
73
+ * @returns A User-Agent header in Node.js, or an empty object otherwise.
74
+ *
75
+ * @example
76
+ * ```typescript
77
+ * import { getNodeUserAgentHeader } from '@core/utils'
78
+ *
79
+ * const headers = {
80
+ * 'Content-Type': 'application/json',
81
+ * ...getNodeUserAgentHeader(),
82
+ * }
83
+ * ```
84
+ */ const getNodeUserAgentHeader = ()=>isNodeEnvironment() ? {
85
+ 'User-Agent': getUserAgent()
86
+ } : {};
54
87
  /**
55
88
  * Detect the runtime environment and return a shortened identifier.
56
89
  *
@@ -2530,6 +2563,8 @@ class KitError extends Error {
2530
2563
  Blockchain["World_Chain_Sepolia"] = "World_Chain_Sepolia";
2531
2564
  Blockchain["XDC"] = "XDC";
2532
2565
  Blockchain["XDC_Apothem"] = "XDC_Apothem";
2566
+ Blockchain["X_Layer"] = "X_Layer";
2567
+ Blockchain["X_Layer_Testnet"] = "X_Layer_Testnet";
2533
2568
  Blockchain["ZKSync_Era"] = "ZKSync_Era";
2534
2569
  Blockchain["ZKSync_Sepolia"] = "ZKSync_Sepolia";
2535
2570
  })(Blockchain || (Blockchain = {}));
@@ -2583,6 +2618,7 @@ var BridgeChain;
2583
2618
  BridgeChain["Unichain"] = "Unichain";
2584
2619
  BridgeChain["World_Chain"] = "World_Chain";
2585
2620
  BridgeChain["XDC"] = "XDC";
2621
+ BridgeChain["X_Layer"] = "X_Layer";
2586
2622
  // Testnet chains with CCTPv2 support
2587
2623
  BridgeChain["Arc_Testnet"] = "Arc_Testnet";
2588
2624
  BridgeChain["Arbitrum_Sepolia"] = "Arbitrum_Sepolia";
@@ -2608,6 +2644,7 @@ var BridgeChain;
2608
2644
  BridgeChain["Unichain_Sepolia"] = "Unichain_Sepolia";
2609
2645
  BridgeChain["World_Chain_Sepolia"] = "World_Chain_Sepolia";
2610
2646
  BridgeChain["XDC_Apothem"] = "XDC_Apothem";
2647
+ BridgeChain["X_Layer_Testnet"] = "X_Layer_Testnet";
2611
2648
  })(BridgeChain || (BridgeChain = {}));
2612
2649
  var UnifiedBalanceChain;
2613
2650
  (function(UnifiedBalanceChain) {
@@ -5145,7 +5182,8 @@ var EarnChain;
5145
5182
  isTestnet: true,
5146
5183
  explorerUrl: 'https://amoy.polygonscan.com/tx/{hash}',
5147
5184
  rpcEndpoints: [
5148
- 'https://rpc-amoy.polygon.technology'
5185
+ 'https://polygon-amoy-bor-rpc.publicnode.com',
5186
+ 'https://polygon-amoy.drpc.org'
5149
5187
  ],
5150
5188
  eurcAddress: null,
5151
5189
  usdcAddress: '0x41e94eb019c0762f9bfcf9fb1e58725bfb0e7582',
@@ -6010,6 +6048,104 @@ var EarnChain;
6010
6048
  }
6011
6049
  });
6012
6050
 
6051
+ /**
6052
+ * X Layer Mainnet chain definition
6053
+ * @remarks
6054
+ * This represents the official production network for the X Layer blockchain.
6055
+ * X Layer is an EVM-compatible OP Stack Layer-2 blockchain built by OKX,
6056
+ * using OKB as its native gas token. (Migrated from Polygon zkEVM/CDK to the
6057
+ * OP Stack on 2025-10-27; older docs describing it as zkEVM are obsolete.)
6058
+ */ const XLayer = defineChain({
6059
+ type: 'evm',
6060
+ chain: Blockchain.X_Layer,
6061
+ name: 'X Layer',
6062
+ title: 'X Layer Mainnet',
6063
+ nativeCurrency: {
6064
+ name: 'OKB',
6065
+ symbol: 'OKB',
6066
+ decimals: 18
6067
+ },
6068
+ chainId: 196,
6069
+ isTestnet: false,
6070
+ explorerUrl: 'https://www.oklink.com/xlayer/tx/{hash}',
6071
+ rpcEndpoints: [
6072
+ 'https://xlayerrpc.okx.com'
6073
+ ],
6074
+ eurcAddress: null,
6075
+ usdcAddress: '0xB6CEceAB302E2E4948951eE7843FC24E92933061',
6076
+ usdtAddress: null,
6077
+ cctp: {
6078
+ domain: 37,
6079
+ contracts: {
6080
+ v2: {
6081
+ type: 'split',
6082
+ tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
6083
+ messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
6084
+ confirmations: 65,
6085
+ fastConfirmations: 1
6086
+ }
6087
+ },
6088
+ forwarderSupported: {
6089
+ source: false,
6090
+ destination: false
6091
+ }
6092
+ },
6093
+ kitContracts: {
6094
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET
6095
+ }
6096
+ });
6097
+
6098
+ /**
6099
+ * X Layer Testnet chain definition
6100
+ * @remarks
6101
+ * This represents the official test network for the X Layer blockchain.
6102
+ * X Layer is an EVM-compatible OP Stack Layer-2 blockchain built by OKX,
6103
+ * using OKB as its native gas token. (Migrated from Polygon zkEVM/CDK to the
6104
+ * OP Stack on 2025-10-27; older docs describing it as zkEVM are obsolete.)
6105
+ */ const XLayerTestnet = defineChain({
6106
+ type: 'evm',
6107
+ chain: Blockchain.X_Layer_Testnet,
6108
+ name: 'X Layer Testnet',
6109
+ title: 'X Layer Testnet',
6110
+ nativeCurrency: {
6111
+ name: 'OKB',
6112
+ symbol: 'OKB',
6113
+ decimals: 18
6114
+ },
6115
+ chainId: 1952,
6116
+ isTestnet: true,
6117
+ // Deliberately not oklink.com (used for mainnet): viem's bundled OKLink
6118
+ // testnet URL targets the deprecated pre-rebrand chain ID 195, not this
6119
+ // chain's ID (1952). Verified against the internal chain-expansion-scripts
6120
+ // config (`v2config.sandbox.yml`) — do not "normalize" this to match mainnet.
6121
+ explorerUrl: 'https://web3.okx.com/explorer/x-layer-testnet/tx/{hash}',
6122
+ rpcEndpoints: [
6123
+ 'https://testrpc.xlayer.tech'
6124
+ ],
6125
+ eurcAddress: null,
6126
+ usdcAddress: '0xDec90b78111Ba2fc6FC6d84d8B9ec159A2d4b9B3',
6127
+ usdtAddress: null,
6128
+ cctp: {
6129
+ domain: 37,
6130
+ contracts: {
6131
+ v2: {
6132
+ type: 'split',
6133
+ tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
6134
+ messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
6135
+ confirmations: 65,
6136
+ fastConfirmations: 1
6137
+ }
6138
+ },
6139
+ forwarderSupported: {
6140
+ source: false,
6141
+ destination: false
6142
+ }
6143
+ },
6144
+ kitContracts: {
6145
+ bridge: BRIDGE_CONTRACT_EVM_TESTNET
6146
+ }
6147
+ });
6148
+
6013
6149
  /**
6014
6150
  * ZKSync Era Mainnet chain definition
6015
6151
  * @remarks
@@ -6129,6 +6265,8 @@ var Chains = {
6129
6265
  WorldChainSepolia: WorldChainSepolia,
6130
6266
  XDC: XDC,
6131
6267
  XDCApothem: XDCApothem,
6268
+ XLayer: XLayer,
6269
+ XLayerTestnet: XLayerTestnet,
6132
6270
  ZKSyncEra: ZKSyncEra,
6133
6271
  ZKSyncEraSepolia: ZKSyncEraSepolia
6134
6272
  };
@@ -7477,13 +7615,12 @@ const swapTokenEnumSchema = zod.z.enum([
7477
7615
  headers: {
7478
7616
  ...DEFAULT_CONFIG$1.headers,
7479
7617
  ...config.headers ?? {},
7480
- // In browser environments, directly setting the 'User-Agent' or similar headers is restricted and may be ignored or cause errors.
7481
- // This is why we use the 'X-User-Agent' header instead.
7482
- ...typeof window === 'undefined' ? {
7483
- 'User-Agent': getUserAgent()
7484
- } : {
7485
- 'X-User-Agent': getUserAgent()
7486
- }
7618
+ // Browsers forbid setting a user-agent request header, and the custom
7619
+ // fallback header the SDK used instead trips CORS preflight against the
7620
+ // Circle APIs (it isn't in their `Access-Control-Allow-Headers`),
7621
+ // blocking the request. So send the SDK user agent only in Node;
7622
+ // browsers omit it entirely.
7623
+ ...getNodeUserAgentHeader()
7487
7624
  }
7488
7625
  };
7489
7626
  let lastError;
@@ -8184,6 +8321,7 @@ const swapTokenEnumSchema = zod.z.enum([
8184
8321
  [Blockchain.Unichain]: '0x078D782b760474a361dDA0AF3839290b0EF57AD6',
8185
8322
  [Blockchain.World_Chain]: '0x79A02482A880bCE3F13e09Da970dC34db4CD24d1',
8186
8323
  [Blockchain.XDC]: '0xfA2958CB79b0491CC627c1557F441eF849Ca8eb1',
8324
+ [Blockchain.X_Layer]: '0xB6CEceAB302E2E4948951eE7843FC24E92933061',
8187
8325
  [Blockchain.ZKSync_Era]: '0x1d17CBcF0D6D143135aE902365D2E5e2A16538D4',
8188
8326
  // =========================================================================
8189
8327
  // Testnets (alphabetically sorted)
@@ -8218,6 +8356,7 @@ const swapTokenEnumSchema = zod.z.enum([
8218
8356
  [Blockchain.Unichain_Sepolia]: '0x31d0220469e10c4E71834a79b1f276d740d3768F',
8219
8357
  [Blockchain.World_Chain_Sepolia]: '0x66145f38cBAC35Ca6F1Dfb4914dF98F1614aeA88',
8220
8358
  [Blockchain.XDC_Apothem]: '0xb5AB69F7bBada22B28e79C8FFAECe55eF1c771D4',
8359
+ [Blockchain.X_Layer_Testnet]: '0xDec90b78111Ba2fc6FC6d84d8B9ec159A2d4b9B3',
8221
8360
  [Blockchain.ZKSync_Sepolia]: '0xAe045DE5638162fa134807Cb558E15A3F5A7F853'
8222
8361
  }
8223
8362
  };
@@ -9269,6 +9408,7 @@ function resolveOptions(options) {
9269
9408
  if (payload.tokenIn !== undefined) safe['tokenIn'] = payload.tokenIn;
9270
9409
  if (payload.tokenOut !== undefined) safe['tokenOut'] = payload.tokenOut;
9271
9410
  if (payload.txHash !== undefined) safe['txHash'] = payload.txHash;
9411
+ if (payload.correlationId !== undefined) safe['correlationId'] = payload.correlationId;
9272
9412
  if (payload.errorDetails !== undefined) {
9273
9413
  const errorDetails = {
9274
9414
  ...payload.errorDetails.errorCode !== undefined && {
@@ -9339,18 +9479,15 @@ function resolveOptions(options) {
9339
9479
  timeoutHandle.unref();
9340
9480
  }
9341
9481
  try {
9342
- const isNode = isNodeEnvironment();
9343
- const userAgent = getUserAgent();
9344
9482
  await fetch(getLogsUrl(), {
9345
9483
  method: 'POST',
9346
9484
  headers: {
9347
9485
  'Content-Type': 'application/json',
9348
- // Browser restricts setting User-Agent; use X-User-Agent instead.
9349
- ...isNode ? {
9350
- 'User-Agent': userAgent
9351
- } : {
9352
- 'X-User-Agent': userAgent
9353
- }
9486
+ // Browsers forbid setting a user-agent request header, and the custom
9487
+ // fallback header the SDK used instead trips CORS preflight (it isn't
9488
+ // in the telemetry endpoint's `Access-Control-Allow-Headers`), so send
9489
+ // it only in Node; browsers omit it entirely.
9490
+ ...getNodeUserAgentHeader()
9354
9491
  },
9355
9492
  body: JSON.stringify(toSafePayload(payload)),
9356
9493
  signal: controller.signal
@@ -9481,7 +9618,7 @@ function resolveOptions(options) {
9481
9618
  // discards the stack trace, nested `cause`, and any custom Error
9482
9619
  // properties — exactly the context an on-call needs when a
9483
9620
  // resolver-closure regression triggers this path.
9484
- console.warn(`[stablecoin-kits telemetry] dropped error event '${eventType}':`, cause);
9621
+ console.warn(`[stablecoin-kits telemetry] dropped event '${eventType}':`, cause);
9485
9622
  } catch {
9486
9623
  // console.warn itself throwing is the user's environment; nothing more we
9487
9624
  // can do without risking the original operation error.
@@ -9497,7 +9634,9 @@ function resolveOptions(options) {
9497
9634
  sdkVersion: config.sdkVersion,
9498
9635
  eventType,
9499
9636
  timestamp: new Date().toISOString(),
9500
- errorDetails,
9637
+ ...errorDetails !== undefined && {
9638
+ errorDetails
9639
+ },
9501
9640
  clientContext: buildClientContext(),
9502
9641
  ...context?.sourceChain != null && {
9503
9642
  sourceChain: context.sourceChain
@@ -9513,6 +9652,9 @@ function resolveOptions(options) {
9513
9652
  },
9514
9653
  ...context?.txHash != null && {
9515
9654
  txHash: context.txHash
9655
+ },
9656
+ ...context?.correlationId != null && {
9657
+ correlationId: context.correlationId
9516
9658
  }
9517
9659
  };
9518
9660
  }
@@ -9626,7 +9768,7 @@ function resolveOptions(options) {
9626
9768
  }
9627
9769
 
9628
9770
  var name$2 = "@circle-fin/bridge-kit";
9629
- var version$3 = "1.12.1";
9771
+ var version$3 = "1.13.0";
9630
9772
  var pkg$3 = {
9631
9773
  name: name$2,
9632
9774
  version: version$3};
@@ -9663,13 +9805,21 @@ const assertCustomFeePolicySymbol = Symbol('assertCustomFeePolicy');
9663
9805
  computeFee: zod.z.function().returns(zod.z.string().or(zod.z.promise(zod.z.string()))).optional(),
9664
9806
  calculateFee: zod.z.function().returns(zod.z.string().or(zod.z.promise(zod.z.string()))).optional(),
9665
9807
  resolveFeeRecipientAddress: zod.z.function().returns(zod.z.string().or(zod.z.promise(zod.z.string())))
9666
- }).strict().refine((data)=>{
9808
+ }).strict().superRefine((data, ctx)=>{
9667
9809
  const hasComputeFee = data.computeFee !== undefined;
9668
9810
  const hasCalculateFee = data.calculateFee !== undefined;
9669
- // XOR: exactly one must be provided
9670
- return hasComputeFee !== hasCalculateFee;
9671
- }, {
9672
- message: 'Provide either computeFee or calculateFee, not both. Use computeFee (recommended) for human-readable amounts.'
9811
+ if (hasComputeFee && hasCalculateFee) {
9812
+ ctx.addIssue({
9813
+ code: zod.z.ZodIssueCode.custom,
9814
+ message: 'Provide either computeFee or calculateFee, not both. Use computeFee (recommended) for human-readable amounts.'
9815
+ });
9816
+ }
9817
+ if (!hasComputeFee && !hasCalculateFee) {
9818
+ ctx.addIssue({
9819
+ code: zod.z.ZodIssueCode.custom,
9820
+ message: 'Provide either computeFee or calculateFee. Use computeFee (recommended) for human-readable amounts.'
9821
+ });
9822
+ }
9673
9823
  });
9674
9824
  /**
9675
9825
  * Assert that the provided value conforms to {@link CustomFeePolicy}.
@@ -12223,7 +12373,13 @@ const FAST_TIER_FINALITY_THRESHOLD = 1000;
12223
12373
  /**
12224
12374
  * The ASCII "cctp-forward" magic, hex-encoded (no `0x`), that a forward-friendly
12225
12375
  * hookData must start with.
12226
- */ const CCTP_FORWARD_MAGIC_HEX = Buffer.from(CCTP_FORWARD_MAGIC_PREFIX, 'ascii').toString('hex');
12376
+ *
12377
+ * Encoded with `TextEncoder` (a browser-safe Web API) rather than `Buffer.from`
12378
+ * so this module-level constant does not reference the Node `Buffer` global at
12379
+ * import time. App Kit inlines this provider into its bundle without a Buffer polyfill, and a
12380
+ * bare `Buffer` here crashes browser bundles (e.g. Vite) on load — even for apps
12381
+ * that never touch the prepaid FORWARD path. Mirrors `buildForwardingHookData`.
12382
+ */ const CCTP_FORWARD_MAGIC_HEX = Array.from(new TextEncoder().encode(CCTP_FORWARD_MAGIC_PREFIX)).map((byte)=>byte.toString(16).padStart(2, '0')).join('');
12227
12383
  /**
12228
12384
  * Determine whether a hookData blob begins with the `cctp-forward` envelope.
12229
12385
  *
@@ -12424,14 +12580,32 @@ const CUSTOM_BURN_GAS_ESTIMATE_EVM = 201_525n // p99 and max are same here: 201_
12424
12580
  ;
12425
12581
  const RECEIVE_MESSAGE_GAS_ESTIMATE_EVM = 237_401n // (99p: 163_963n + max: 310_839n) / 2 = 237_401n
12426
12582
  ;
12427
- // Hard execution caps: observed max + ~30% buffer, used as gasLimit overrides on
12428
- // chains whose eth_estimateGas under-reports (e.g. Cronos EIP-7623 calldata floor).
12429
- // Kept separate from the fee-estimate averages above.
12430
- const APPROVE_GAS_LIMIT_EVM = 100_000n // ERC-20 approve observed max ~46k
12583
+ // Gas FLOORS, not ceilings kept separate from the fee-estimate averages
12584
+ // above. `executePreparedChainRequest` submits
12585
+ // max(estimate * buffer, floor), so a chain whose real cost exceeds the floor
12586
+ // is covered by its own estimate, and a chain whose estimator under-reports
12587
+ // (Cronos: returns 30_600 where the EIP-7623 calldata floor is 45_000) is
12588
+ // covered by the floor.
12589
+ //
12590
+ // Two distinct chain surcharges drive these numbers, both measured live:
12591
+ // Sei — ~+51_500 per NEWLY CREATED storage slot (73_595 vs vanilla 22_100);
12592
+ // no flat per-tx surcharge (31_535, identical to Base).
12593
+ // Edge — ~+53_200 flat on EVERY tx (84_751 vs Base 31_535); storage priced
12594
+ // normally. Edge therefore fails warm as well as cold.
12595
+ // A floor must clear the worst COLD cost, since a slot that exists at estimate
12596
+ // time can be consumed before inclusion and cost a full step more on execution.
12597
+ // Each floor is therefore derived from the worst observed estimate *after* the
12598
+ // 1.25x buffer, plus headroom — sizing it below the buffered value would leave
12599
+ // the estimate governing and defeat the point of the floor.
12600
+ //
12601
+ // The `*_GAS_LIMIT_EVM` names are kept despite these being floors: they are
12602
+ // exported, so renaming to `*_GAS_FLOOR_EVM` would be a breaking change for
12603
+ // consumers. Read "LIMIT" here as "the limit we submit", never as a ceiling.
12604
+ const APPROVE_GAS_LIMIT_EVM = 150_000n // buffered worst cold 149_355 (Edge Testnet 119_484 x 1.25) + drift headroom
12431
12605
  ;
12432
- const DEPOSIT_FOR_BURN_GAS_LIMIT_EVM = 300_000n // observed max 226_506 + ~30%
12606
+ const DEPOSIT_FOR_BURN_GAS_LIMIT_EVM = 500_000n // buffered worst 474_078 (Sei 379_263 x 1.25) + ~26k headroom
12433
12607
  ;
12434
- const RECEIVE_MESSAGE_GAS_LIMIT_EVM = 400_000n // observed max 310_839 + ~30%
12608
+ const RECEIVE_MESSAGE_GAS_LIMIT_EVM = 400_000n // observed max 310_839; clears Cronos' calldata floor ~10x
12435
12609
  ;
12436
12610
  /**
12437
12611
  * The minimum finality threshold for CCTPv2 transfers.
@@ -13896,6 +14070,63 @@ function hasPendingState(analysis, result) {
13896
14070
  return waitForPendingTransaction(pendingStep, adapter, chain);
13897
14071
  }
13898
14072
 
14073
+ /**
14074
+ * Multiplier applied to a successful gas estimate before it is submitted.
14075
+ *
14076
+ * Estimates are exact, not padded: Sei returns 109_739 for an approve that
14077
+ * consumes 107_717 (1.9% headroom). Chains that price storage in large steps
14078
+ * can exceed the estimate if state changes between estimation and inclusion,
14079
+ * so the estimate is padded before use.
14080
+ *
14081
+ * @remarks
14082
+ * This buffer alone does NOT cover Sei's ~51_500 per-new-slot step at approve
14083
+ * scale (25% of ~110_000 is only ~27_500). For approve, the FLOOR is what
14084
+ * covers a slot that exists at estimation time and is consumed before
14085
+ * inclusion — so do not lower `APPROVE_GAS_LIMIT_EVM` on the reasoning that
14086
+ * the estimate covers it. For burn the buffer does cover a step (25% of
14087
+ * ~300_000 exceeds 51_500).
14088
+ */ const GAS_ESTIMATE_BUFFER_PERCENT = 125n;
14089
+ /**
14090
+ * Resolve the gas limit for an EVM request as `max(estimate * buffer, floor)`.
14091
+ *
14092
+ * Estimates first so chains whose real cost exceeds the floor are covered by
14093
+ * their own measurement, and falls back to the floor whenever estimation is
14094
+ * unavailable or under-reports. Estimation failure is never fatal here: before
14095
+ * floors existed these requests were submitted with a pinned limit and no
14096
+ * estimate at all, so degrading to the floor is never worse than the previous
14097
+ * behaviour.
14098
+ *
14099
+ * @param request - The prepared EVM request to size a gas limit for
14100
+ * @param gasFloor - The minimum gas limit to submit, in gas units
14101
+ * @returns The gas limit to submit, in gas units
14102
+ * @throws Never — estimation failures degrade to `gasFloor`
14103
+ *
14104
+ * @example
14105
+ * ```typescript
14106
+ * const gasLimit = await resolveGasLimit(request, 150_000)
14107
+ * ```
14108
+ */ const resolveGasLimit = async (request, gasFloor)=>{
14109
+ try {
14110
+ // Deliberately called without a `fallback`: both the viem and ethers
14111
+ // adapters *return* the supplied fallback object when estimation reverts
14112
+ // rather than throwing, which would set the estimate to the floor and then
14113
+ // multiply it by the buffer below. Omitting it routes reverts through the
14114
+ // catch, so a failed estimate degrades to exactly the floor.
14115
+ const estimate = await request.estimate();
14116
+ // The arithmetic stays inside the try on purpose. `EstimatedGas.gas` is
14117
+ // typed `bigint`, but adapters are a public extension point and may be
14118
+ // implemented in plain JS, so a non-bigint `gas` would throw here
14119
+ // ("Cannot mix BigInt and other types"). Guarding it keeps the documented
14120
+ // contract — estimation never aborts a step, it degrades to the floor.
14121
+ const buffered = estimate.gas * GAS_ESTIMATE_BUFFER_PERCENT / 100n;
14122
+ // Convert before comparing: Math.max throws on BigInt operands, and gas
14123
+ // units are far below Number.MAX_SAFE_INTEGER so the narrowing is lossless.
14124
+ return Math.max(Number(buffered), gasFloor);
14125
+ } catch {
14126
+ // Estimation is best-effort; the floor is the known-safe value.
14127
+ return gasFloor;
14128
+ }
14129
+ };
13899
14130
  /**
13900
14131
  * Executes a prepared chain request and returns the result as a bridge step.
13901
14132
  *
@@ -13909,8 +14140,8 @@ function hasPendingState(analysis, result) {
13909
14140
  * - `adapter`: The adapter that will execute the transaction
13910
14141
  * - `confirmations`: The number of confirmations to wait for (defaults to 1)
13911
14142
  * - `timeout`: The timeout for the request in milliseconds
13912
- * - `gasLimit`: Optional explicit gas limit (number) forwarded to EVM execute,
13913
- * bypassing `eth_estimateGas`; ignored for non-EVM requests
14143
+ * - `gasFloor`: Optional minimum gas limit (number); the request is submitted
14144
+ * with `max(estimate * 1.25, gasFloor)`. Ignored for non-EVM requests
13914
14145
  * @returns The bridge step with the transaction details and explorer URL
13915
14146
  * @throws If the transaction execution fails
13916
14147
  *
@@ -13925,7 +14156,7 @@ function hasPendingState(analysis, result) {
13925
14156
  * })
13926
14157
  * console.log('Transaction hash:', step.txHash)
13927
14158
  * ```
13928
- */ async function executePreparedChainRequest({ name, request, adapter, chain, confirmations = 1, timeout, gasLimit }) {
14159
+ */ async function executePreparedChainRequest({ name, request, adapter, chain, confirmations = 1, timeout, gasFloor }) {
13929
14160
  const step = {
13930
14161
  name,
13931
14162
  state: 'pending'
@@ -13938,8 +14169,8 @@ function hasPendingState(analysis, result) {
13938
14169
  step.state = 'noop';
13939
14170
  return step;
13940
14171
  }
13941
- const txHash = request.type === 'evm' && gasLimit !== undefined ? await request.execute({
13942
- gasLimit
14172
+ const txHash = request.type === 'evm' && gasFloor !== undefined ? await request.execute({
14173
+ gasLimit: await resolveGasLimit(request, gasFloor)
13943
14174
  }) : await request.execute();
13944
14175
  step.txHash = txHash;
13945
14176
  const retryOptions = {
@@ -14013,7 +14244,7 @@ function hasPendingState(analysis, result) {
14013
14244
  adapter: params.source.adapter,
14014
14245
  chain: params.source.chain,
14015
14246
  request: await provider.approve(params.source, approvalAmount),
14016
- gasLimit: Number(APPROVE_GAS_LIMIT_EVM)
14247
+ gasFloor: Number(APPROVE_GAS_LIMIT_EVM)
14017
14248
  });
14018
14249
  }
14019
14250
 
@@ -14041,7 +14272,7 @@ function hasPendingState(analysis, result) {
14041
14272
  adapter: params.source.adapter,
14042
14273
  chain: params.source.chain,
14043
14274
  request: await provider.burn(params),
14044
- gasLimit: Number(DEPOSIT_FOR_BURN_GAS_LIMIT_EVM)
14275
+ gasFloor: Number(DEPOSIT_FOR_BURN_GAS_LIMIT_EVM)
14045
14276
  });
14046
14277
  }
14047
14278
 
@@ -14135,10 +14366,9 @@ function hasPendingState(analysis, result) {
14135
14366
  request: mintRequest,
14136
14367
  // Some chains (e.g. Cronos) enforce an EIP-7623 calldata gas floor that
14137
14368
  // eth_estimateGas does not account for, returning a below-floor value
14138
- // without reverting. Pinning to a value above the observed execution max
14139
- // (310_839) bypasses re-estimation and guarantees we clear both the floor
14140
- // and the actual execution cost.
14141
- gasLimit: Number(RECEIVE_MESSAGE_GAS_LIMIT_EVM)
14369
+ // without reverting. The floor covers those; chains that cost more than the
14370
+ // floor are covered by their own estimate.
14371
+ gasFloor: Number(RECEIVE_MESSAGE_GAS_LIMIT_EVM)
14142
14372
  });
14143
14373
  // Add forwarded: false for non-relayer mints
14144
14374
  return {
@@ -14661,7 +14891,7 @@ const mockAttestationMessage = {
14661
14891
  return step;
14662
14892
  }
14663
14893
 
14664
- var version$2 = "1.10.0";
14894
+ var version$2 = "1.10.2";
14665
14895
  var pkg$2 = {
14666
14896
  version: version$2};
14667
14897
 
@@ -17259,7 +17489,9 @@ registerKit(`${pkg$3.name}/${pkg$3.version}`);
17259
17489
  headers: context.headers
17260
17490
  }
17261
17491
  });
17262
- if (hasBoth) {
17492
+ if (context.customFeePolicy?.bridge != null) {
17493
+ kit.setCustomFeePolicy(context.customFeePolicy.bridge);
17494
+ } else if (hasBoth) {
17263
17495
  kit.setCustomFeePolicy({
17264
17496
  calculateFee: async (params)=>{
17265
17497
  const feeStr = await getFee('bridge', params);
@@ -17275,7 +17507,7 @@ registerKit(`${pkg$3.name}/${pkg$3.version}`);
17275
17507
  };
17276
17508
 
17277
17509
  var name$1 = "@circle-fin/swap-kit";
17278
- var version$1 = "1.4.0";
17510
+ var version$1 = "1.5.1";
17279
17511
  var pkg$1 = {
17280
17512
  name: name$1,
17281
17513
  version: version$1};
@@ -18016,6 +18248,16 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
18016
18248
  required_error: 'estimatedAmount is required',
18017
18249
  invalid_type_error: 'estimatedAmount must be a string'
18018
18250
  }).min(1, 'estimatedAmount must be a non-empty string'),
18251
+ // Per-swap join key echoed back verbatim on success telemetry. Optional so a
18252
+ // not-yet-upgraded service (no field) still validates during rollout. A
18253
+ // malformed/non-UUID value is coerced to `undefined` (no telemetry id) rather
18254
+ // than throwing: this is a telemetry-only field (stripped from the developer
18255
+ // result, never used for control flow), so it must not be able to abort the
18256
+ // swap via `parseCreateSwapResponse().parse()`. Mirrors the best-effort,
18257
+ // never-throw contract of the rest of the telemetry stack. Implemented with
18258
+ // `preprocess` rather than Zod's `.catch()` because static analysis misreads
18259
+ // `.catch` on the schema chain as an unhandled Promise (S7785).
18260
+ correlationId: zod.z.preprocess((value)=>zod.z.string().uuid().safeParse(value).success ? value : undefined, zod.z.string().optional()),
18019
18261
  config: createSwapRequestBaseSchema.shape.config.optional(),
18020
18262
  fees: createSwapFeesSchema.optional(),
18021
18263
  transaction: createSwapTransactionSchema
@@ -19208,7 +19450,7 @@ new Set(Object.values(Blockchain));
19208
19450
  registerKit(`${pkg$1.name}/${pkg$1.version}`);
19209
19451
 
19210
19452
  var name = "@circle-fin/earn-kit";
19211
- var version = "1.3.0";
19453
+ var version = "1.5.0";
19212
19454
  var pkg = {
19213
19455
  name: name,
19214
19456
  version: version};
@@ -20219,11 +20461,16 @@ const sourceAdapterContextSchema = zod.z.object({
20219
20461
  *
20220
20462
  * Validate the optional Kit Key field using the standard `apiKeySchema`
20221
20463
  * format (`KIT_KEY:<keyId>:<keySecret>`). When omitted, the SDK
20222
- * operates in permissionless mode.
20464
+ * operates in permissionless mode. `baseUrl` overrides the Earn Service
20465
+ * endpoint (e.g. staging); `batchTransactions: false` opts out of atomic
20466
+ * batched execution. Both are forwarded to the provider, so this `.strict()`
20467
+ * schema must accept them or a valid config object is rejected.
20223
20468
  *
20224
20469
  * @internal
20225
20470
  */ const earnConfigSchema = zod.z.object({
20226
- kitKey: apiKeySchema.optional()
20471
+ kitKey: apiKeySchema.optional(),
20472
+ baseUrl: zod.z.string().optional(),
20473
+ batchTransactions: zod.z.boolean().optional()
20227
20474
  }).strict();
20228
20475
  /**
20229
20476
  * Canonical decimal form: a leading digit with no leading zeros (a single
@@ -20672,6 +20919,8 @@ function hasCrossChainDepositQuoteShape(params) {
20672
20919
  config: earnConfigSchema.optional()
20673
20920
  });
20674
20921
 
20922
+ /** SDK name used in telemetry payloads. */ resolveKitSdkName(pkg.name);
20923
+
20675
20924
  // Auto-register this kit for user agent tracking
20676
20925
  registerKit(`${pkg.name}/${pkg.version}`);
20677
20926