@stablechain/sdk 1.0.7 → 1.0.8

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/README.md CHANGED
@@ -455,44 +455,57 @@ Nearly all of the wait is **source-chain finality**: the DVNs securing the pathw
455
455
  not attest until the send transaction is buried under `confirmations` source blocks.
456
456
  Destination execution is a few blocks on top.
457
457
 
458
- The SDK resolves this from two independent inputs and reports both:
458
+ `estimateBridgeTime` reports that breakdown:
459
459
 
460
460
  | Field | Meaning |
461
461
  |-------------------|--------------------------------------------------------------------------------------------------|
462
- | `seconds` | Best available estimate — display this |
463
- | `source` | `"observed"` or `"formula"`, whichever produced `seconds` |
462
+ | `seconds` | The estimate — display this |
463
+ | `source` | `"formula"`, `"floor"`, or `"observed"` — which input produced `seconds` |
464
464
  | `observedSeconds` | Median delivery time of recent messages on this exact pathway, from LayerZero Scan |
465
465
  | `formulaSeconds` | LayerZero's documented estimate: `srcBlockTime × confirmations + dstBlockTime × (2 + dvnCount)` |
466
466
  | `confirmations` | Source blocks the DVNs wait for, read live from the pathway's ULN config |
467
467
  | `dvnCount` | DVNs verifying the pathway |
468
468
 
469
- `observed` wins when available. The formula assumes block confirmations are the whole
470
- wait, which holds for L1 sources but understates rollups badly — Arbitrum→Stable computes
471
- to ~19 s (60 blocks × 0.25 s) against a real median of ~18 min, because the DVNs wait for
472
- the L2 batch to finalize on Ethereum.
469
+ `seconds` comes from the formula — the pathway's live ULN config is what the DVNs are
470
+ actually bound by, so a config change is reflected the moment it lands. On every pathway
471
+ that is purely confirmation-bound it agrees with both usdt0.to's own estimate and the
472
+ delivery times LayerZero Scan reports to well under a percent.
473
+
474
+ Some source chains carry a floor. DVN attestation and destination execution sit outside the
475
+ formula, and on Ethereum and Arbitrum the DVNs additionally hold until the send finalizes on
476
+ the beacon chain. Where confirmations are high that overhead vanishes into the rounding;
477
+ where they are low it dominates — Arbitrum's 60 confirmations compute to ~19 s against a real
478
+ ~18 min. Those chains are floored at a measured minimum, and `source` reports `"floor"`.
479
+
480
+ `observedSeconds` is fetched alongside and reported for comparison, but does not drive
481
+ `seconds`: a 50-message window can reach back months, so on a pathway that has been
482
+ reconfigured or has gone quiet it answers from a config that no longer exists.
473
483
 
474
484
  Pathway configs and observed medians are cached for 5 minutes; call
475
485
  `clearBridgeEstimateCache()` to force a refetch.
476
486
 
477
- Roughly what to expect on mainnet, measured 2026-07-30:
487
+ Roughly what to expect on mainnet, from configs read 2026-08-27:
478
488
 
479
489
  | Pathway | Confirmations | Typical delivery |
480
490
  |---------------------|---------------|------------------|
481
- | Polygon → Stable | 32 | ~1 min |
482
- | Bera → Stable | 60 | ~2 min |
483
- | Unichain → Stable | 1600 | ~8 min |
484
- | Flare → Stable | 500 | ~11 min |
485
- | Optimism → Stable | 1000 | ~15 min |
486
- | Ethereum → Stable | 65 | ~17 min |
491
+ | Polygon → Stable | 32 | ~1 min ᶠ |
492
+ | Bera → Stable | 60 | ~2 min ᶠ |
493
+ | Flare → Stable | 500 | ~10 min ᶠ |
487
494
  | Sei → Stable | 2000 | ~17 min |
488
- | Arbitrum → Stable | 60 | ~18 min |
495
+ | Ethereum → Stable | 65 | ~19.5 min ᶠ |
496
+ | Arbitrum → Stable | 60 | ~22.5 min ᶠ |
497
+ | Unichain → Stable | 1600 | ~27 min |
489
498
  | Plasma → Stable | 1800 | ~30 min |
499
+ | Optimism → Stable | 1000 | ~33 min |
490
500
  | Stable → any | 3600 | ~42 min |
491
501
  | Rootstock → Stable | 120 | ~62 min |
492
502
  | MegaETH → Stable | 5400 | ~90 min |
493
503
  | X Layer → Stable | 9000 | ~2.5 h |
494
504
  | HyperEVM → Stable | 43200 | ~12 h |
495
505
 
506
+ ᶠ Floored at the source chain's measured minimum, because its confirmation count is not the
507
+ whole wait — Ethereum and Arbitrum would otherwise compute to ~13 min and ~19 s.
508
+
496
509
  ---
497
510
 
498
511
  ### `trackBridge(srcTxHash)` → `BridgeStatus`
package/dist/index.cjs CHANGED
@@ -1751,7 +1751,7 @@ var XAUT0_DEPLOYMENTS = {
1751
1751
  ["bsc" /* BSC */]: { address: "0x21cAef8A43163Eea865baeE23b9C2E327696A3bf", decimals: 6, oftAdapter: "0x53C3A64c8942288e12813C1F8457Db45980BcfC2", approvalRequired: false }
1752
1752
  };
1753
1753
  var USDT0_LOCKBOX_CHAINS = /* @__PURE__ */ new Set(["ethereum" /* Ethereum */, "sepolia" /* Sepolia */]);
1754
- function bridgeChain(chain, lzEid, blockTimeSec, usdt0Adapter) {
1754
+ function bridgeChain(chain, lzEid, blockTimeSec, usdt0Adapter, minDeliverySec) {
1755
1755
  const base = C[chain];
1756
1756
  const tokens = {};
1757
1757
  if (usdt0Adapter) {
@@ -1764,7 +1764,7 @@ function bridgeChain(chain, lzEid, blockTimeSec, usdt0Adapter) {
1764
1764
  }
1765
1765
  const xaut0 = XAUT0_DEPLOYMENTS[chain];
1766
1766
  if (xaut0) tokens.xaut0 = xaut0;
1767
- return { ...base, lzEid, blockTimeSec, tokens, oftAdapter: usdt0Adapter };
1767
+ return { ...base, lzEid, blockTimeSec, minDeliverySec, tokens, oftAdapter: usdt0Adapter };
1768
1768
  }
1769
1769
  var BRIDGE_CHAIN_CONFIGS = {
1770
1770
  // ─── Testnet ───────────────────────────────────────────────────────────────
@@ -1772,18 +1772,18 @@ var BRIDGE_CHAIN_CONFIGS = {
1772
1772
  ["stable-testnet" /* StableTestnet */]: bridgeChain("stable-testnet" /* StableTestnet */, 40374, 0.7, "0x779Ded0c9e1022225f8E0630b35a9b54bE713736"),
1773
1773
  // ─── Mainnet ──────────────────────────────────────────────────────────────
1774
1774
  ["stable" /* Stable */]: bridgeChain("stable" /* Stable */, 30396, 0.7, "0xedaba024be4d87974d5aB11C6Dd586963CcCB027"),
1775
- ["ethereum" /* Ethereum */]: bridgeChain("ethereum" /* Ethereum */, 30101, 12, "0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee"),
1776
- ["arbitrum" /* Arbitrum */]: bridgeChain("arbitrum" /* Arbitrum */, 30110, 0.25, "0x14E4A1B13bf7F943c8ff7C51fb60FA964A298D92"),
1775
+ ["ethereum" /* Ethereum */]: bridgeChain("ethereum" /* Ethereum */, 30101, 12, "0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee", 1170),
1776
+ ["arbitrum" /* Arbitrum */]: bridgeChain("arbitrum" /* Arbitrum */, 30110, 0.25, "0x14E4A1B13bf7F943c8ff7C51fb60FA964A298D92", 1350),
1777
1777
  ["ink" /* Ink */]: bridgeChain("ink" /* Ink */, 30339, 1, "0x1cB6De532588fCA4a21B7209DE7C456AF8434A65"),
1778
- ["bera" /* Bera */]: bridgeChain("bera" /* Bera */, 30362, 2, "0x3Dc96399109df5ceb2C226664A086140bD0379cB"),
1778
+ ["bera" /* Bera */]: bridgeChain("bera" /* Bera */, 30362, 2, "0x3Dc96399109df5ceb2C226664A086140bD0379cB", 135),
1779
1779
  ["megaeth" /* MegaETH */]: bridgeChain("megaeth" /* MegaETH */, 30398, 1, "0x9151434b16b9763660705744891fa906f660ecc5"),
1780
1780
  ["hyperevm" /* HyperEVM */]: bridgeChain("hyperevm" /* HyperEVM */, 30367, 1, "0x904861a24F30EC96ea7CFC3bE9EA4B476d237e98"),
1781
- ["polygon" /* Polygon */]: bridgeChain("polygon" /* Polygon */, 30109, 1.5, "0x6BA10300f0DC58B7a1e4c0e41f5daBb7D7829e13"),
1781
+ ["polygon" /* Polygon */]: bridgeChain("polygon" /* Polygon */, 30109, 1.5, "0x6BA10300f0DC58B7a1e4c0e41f5daBb7D7829e13", 70),
1782
1782
  ["plasma" /* Plasma */]: bridgeChain("plasma" /* Plasma */, 30383, 1, "0x02ca37966753bDdDf11216B73B16C1dE756A7CF9"),
1783
1783
  ["optimism" /* Optimism */]: bridgeChain("optimism" /* Optimism */, 30111, 2, "0xF03b4d9AC1D5d1E7c4cEf54C2A313b9fe051A0aD"),
1784
1784
  ["unichain" /* Unichain */]: bridgeChain("unichain" /* Unichain */, 30320, 1, "0xc07bE8994D035631c36fb4a89C918CeFB2f03EC3"),
1785
1785
  ["sei" /* Sei */]: bridgeChain("sei" /* Sei */, 30280, 0.5, "0x56Fe74A2e3b484b921c447357203431a3485CC60"),
1786
- ["flare" /* Flare */]: bridgeChain("flare" /* Flare */, 30295, 1.1, "0x567287d2A9829215a37e3B88843d32f9221E7588"),
1786
+ ["flare" /* Flare */]: bridgeChain("flare" /* Flare */, 30295, 1.1, "0x567287d2A9829215a37e3B88843d32f9221E7588", 600),
1787
1787
  ["xlayer" /* XLayer */]: bridgeChain("xlayer" /* XLayer */, 30274, 1, "0x94BCCa6bdfd6A61817Ab0E960bFedE4984505554"),
1788
1788
  ["rootstock" /* Rootstock */]: bridgeChain("rootstock" /* Rootstock */, 30333, 31, "0x1a594d5d5d1c426281C1064B07f23F57B2716B61"),
1789
1789
  // XAUt0 only — BSC has no USDT0 deployment. Block time measured 2026-08-20.
@@ -1964,6 +1964,14 @@ function median(values) {
1964
1964
  const mid = Math.floor(sorted.length / 2);
1965
1965
  return sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
1966
1966
  }
1967
+ function resolveDeliverySeconds(formulaSeconds, observedSeconds, minDeliverySec) {
1968
+ const base = _nullishCoalesce(formulaSeconds, () => ( observedSeconds));
1969
+ if (base === void 0) return void 0;
1970
+ if (minDeliverySec !== void 0 && minDeliverySec > base) {
1971
+ return { seconds: minDeliverySec, source: "floor" };
1972
+ }
1973
+ return { seconds: base, source: formulaSeconds !== void 0 ? "formula" : "observed" };
1974
+ }
1967
1975
  var WORKER_EXECUTOR = 1;
1968
1976
  var OPTION_LZRECEIVE = 1;
1969
1977
  var OPTION_NATIVE_DROP = 2;
@@ -2383,16 +2391,22 @@ var LayerZeroService = class {
2383
2391
  /**
2384
2392
  * Estimate how long a USDT0 bridge will take, end to end.
2385
2393
  *
2386
- * Two independent inputs, both cheap:
2387
- * - **observed** — median delivery time of recent messages LayerZero Scan has
2388
- * already settled on this exact pathway.
2389
- * - **formula** — LayerZero's documented estimate from the on-chain ULN config:
2390
- * `srcBlockTime × confirmations + dstBlockTime × (2 + dvnCount)`.
2394
+ * The number comes from the pathway's live ULN config:
2395
+ * `srcBlockTime × confirmations + dstBlockTime × (2 + dvnCount)`, floored at the
2396
+ * source chain's {@link BridgeChainConfig.minDeliverySec} where the confirmation
2397
+ * count is not the whole wait. This is what the DVNs are actually bound by, so it
2398
+ * tracks a config change the moment it lands — and on every pathway that is purely
2399
+ * confirmation-bound it agrees with both usdt0.to's own estimate and the delivery
2400
+ * times Scan reports to well under a percent.
2391
2401
  *
2392
- * `observed` wins when available. The formula assumes `confirmations` is the whole
2393
- * wait, which holds for L1 sources but badly understates rollups: Arbitrum→Stable
2394
- * computes to ~15s (60 blocks × 0.25s) while real deliveries take ~18min, because
2395
- * the DVNs wait for the L2 batch to finalize on L1.
2402
+ * The observed median of recent deliveries on this exact pathway is fetched too
2403
+ * and reported as {@link BridgeTimeEstimate.observedSeconds}, but it no longer
2404
+ * drives `seconds`. History lags the config: after Optimism→Stable was raised to
2405
+ * 1000 confirmations, a 50-message window still reaching back 261 days put the
2406
+ * median at ~15min against a real ~33min, and pathways quiet for months (Unichain,
2407
+ * XAUt0 from Arbitrum) were answering from configs that no longer exist. It stays
2408
+ * the fallback for the case the formula cannot be computed at all — an RPC that
2409
+ * will not serve the ULN read.
2396
2410
  */
2397
2411
  async estimateBridgeTime({
2398
2412
  fromChain,
@@ -2423,16 +2437,15 @@ var LayerZeroService = class {
2423
2437
  );
2424
2438
  formulaSeconds = Number.isFinite(computed) ? computed : void 0;
2425
2439
  }
2426
- const seconds = _nullishCoalesce(observedSeconds, () => ( formulaSeconds));
2427
- if (seconds === void 0) {
2440
+ const resolved = resolveDeliverySeconds(formulaSeconds, observedSeconds, src.minDeliverySec);
2441
+ if (!resolved) {
2428
2442
  throw new (0, _chunkQQ26UI3Fcjs.StableQuoteError)({
2429
2443
  provider: "layerzero",
2430
2444
  shortMessage: `estimateBridgeTime: no pathway data for ${fromChain}\u2192${toChain}`
2431
2445
  });
2432
2446
  }
2433
2447
  return {
2434
- seconds,
2435
- source: observedSeconds !== void 0 ? "observed" : "formula",
2448
+ ...resolved,
2436
2449
  observedSeconds,
2437
2450
  formulaSeconds,
2438
2451
  confirmations: Number.isFinite(_optionalChain([uln, 'optionalAccess', _57 => _57.confirmations])) ? _optionalChain([uln, 'optionalAccess', _58 => _58.confirmations]) : void 0,
package/dist/index.d.cts CHANGED
@@ -222,17 +222,23 @@ interface BridgeGasEstimate {
222
222
  interface BridgeTimeEstimate {
223
223
  /** Best available estimate, in seconds — prefer this for display */
224
224
  seconds: number;
225
- /** Which input produced `seconds` — see {@link BridgeTimeEstimate.observedSeconds} */
226
- source: "observed" | "formula";
227
225
  /**
228
- * Median delivery time of recent delivered messages on this exact pathway,
229
- * from LayerZero Scan. More trustworthy than `formulaSeconds` — notably on
230
- * rollup sources, where DVNs wait for L1 finality rather than raw block count.
226
+ * Which input produced `seconds`. `"formula"` normally; `"floor"` when the
227
+ * source chain's measured minimum exceeded it (Ethereum, Arbitrum, Polygon,
228
+ * Bera, Flare); `"observed"` only when the pathway's ULN config could not be read.
229
+ */
230
+ source: "formula" | "floor" | "observed";
231
+ /**
232
+ * Median delivery time of recent delivered messages on this exact pathway, from
233
+ * LayerZero Scan. Reported for comparison but no longer preferred: a 50-message
234
+ * window can reach back months, so it answers from a ULN config the pathway has
235
+ * since moved off.
231
236
  */
232
237
  observedSeconds?: number;
233
238
  /**
234
239
  * LayerZero's documented estimate:
235
- * `srcBlockTime × confirmations + dstBlockTime × (2 + dvnCount)`.
240
+ * `srcBlockTime × confirmations + dstBlockTime × (2 + dvnCount)`. What `seconds`
241
+ * reports unless the source chain's measured floor is higher.
236
242
  */
237
243
  formulaSeconds?: number;
238
244
  /** Source-chain block confirmations the DVNs wait for before attesting */
@@ -743,6 +749,26 @@ interface BridgeChainConfig extends ChainConfig {
743
749
  * requirement into a wall-clock estimate. Measured from mainnet, 2026-07-30.
744
750
  */
745
751
  blockTimeSec?: number;
752
+ /**
753
+ * Floor on delivery time, in seconds, for sends *from* this chain — set only
754
+ * where the confirmation count is demonstrably not the whole wait.
755
+ *
756
+ * Two things sit outside the formula: DVN attestation and destination execution,
757
+ * a roughly fixed overhead, and on Ethereum and Arbitrum a hold until the send
758
+ * finalizes on the beacon chain. Where confirmations are high the overhead is lost
759
+ * in the rounding — Optimism's 1000 and X Layer's 9000 both land within 0.5% of
760
+ * observed. Where they are low it dominates: Arbitrum's 60 confirmations compute
761
+ * to ~19s against a real ~18min, Ethereum's 65 to ~13min against ~17min, Polygon's
762
+ * 32 to ~52s against a real ~70s, Flare's 500 to ~9min against a real ~10min.
763
+ *
764
+ * Each figure is taken from deliveries LayerZero Scan held on 2026-08-27, no older
765
+ * than 45 days — the ceiling of that spread for the two finality-bound chains, so
766
+ * the estimate errs long, and the median for the rest. Flare's is a median across
767
+ * its destinations rather than one pathway's, since the hub pathway alone had only
768
+ * six recent sends to go on; those settled at ~9m30s and the busier pathways to
769
+ * Ethereum, Arbitrum, Plasma, Polygon and Optimism between ~9m20s and ~10m10s.
770
+ */
771
+ minDeliverySec?: number;
746
772
  }
747
773
  declare const BRIDGE_CHAIN_CONFIGS: Partial<Record<Chain, BridgeChainConfig>>;
748
774
  /**
package/dist/index.d.ts CHANGED
@@ -222,17 +222,23 @@ interface BridgeGasEstimate {
222
222
  interface BridgeTimeEstimate {
223
223
  /** Best available estimate, in seconds — prefer this for display */
224
224
  seconds: number;
225
- /** Which input produced `seconds` — see {@link BridgeTimeEstimate.observedSeconds} */
226
- source: "observed" | "formula";
227
225
  /**
228
- * Median delivery time of recent delivered messages on this exact pathway,
229
- * from LayerZero Scan. More trustworthy than `formulaSeconds` — notably on
230
- * rollup sources, where DVNs wait for L1 finality rather than raw block count.
226
+ * Which input produced `seconds`. `"formula"` normally; `"floor"` when the
227
+ * source chain's measured minimum exceeded it (Ethereum, Arbitrum, Polygon,
228
+ * Bera, Flare); `"observed"` only when the pathway's ULN config could not be read.
229
+ */
230
+ source: "formula" | "floor" | "observed";
231
+ /**
232
+ * Median delivery time of recent delivered messages on this exact pathway, from
233
+ * LayerZero Scan. Reported for comparison but no longer preferred: a 50-message
234
+ * window can reach back months, so it answers from a ULN config the pathway has
235
+ * since moved off.
231
236
  */
232
237
  observedSeconds?: number;
233
238
  /**
234
239
  * LayerZero's documented estimate:
235
- * `srcBlockTime × confirmations + dstBlockTime × (2 + dvnCount)`.
240
+ * `srcBlockTime × confirmations + dstBlockTime × (2 + dvnCount)`. What `seconds`
241
+ * reports unless the source chain's measured floor is higher.
236
242
  */
237
243
  formulaSeconds?: number;
238
244
  /** Source-chain block confirmations the DVNs wait for before attesting */
@@ -743,6 +749,26 @@ interface BridgeChainConfig extends ChainConfig {
743
749
  * requirement into a wall-clock estimate. Measured from mainnet, 2026-07-30.
744
750
  */
745
751
  blockTimeSec?: number;
752
+ /**
753
+ * Floor on delivery time, in seconds, for sends *from* this chain — set only
754
+ * where the confirmation count is demonstrably not the whole wait.
755
+ *
756
+ * Two things sit outside the formula: DVN attestation and destination execution,
757
+ * a roughly fixed overhead, and on Ethereum and Arbitrum a hold until the send
758
+ * finalizes on the beacon chain. Where confirmations are high the overhead is lost
759
+ * in the rounding — Optimism's 1000 and X Layer's 9000 both land within 0.5% of
760
+ * observed. Where they are low it dominates: Arbitrum's 60 confirmations compute
761
+ * to ~19s against a real ~18min, Ethereum's 65 to ~13min against ~17min, Polygon's
762
+ * 32 to ~52s against a real ~70s, Flare's 500 to ~9min against a real ~10min.
763
+ *
764
+ * Each figure is taken from deliveries LayerZero Scan held on 2026-08-27, no older
765
+ * than 45 days — the ceiling of that spread for the two finality-bound chains, so
766
+ * the estimate errs long, and the median for the rest. Flare's is a median across
767
+ * its destinations rather than one pathway's, since the hub pathway alone had only
768
+ * six recent sends to go on; those settled at ~9m30s and the busier pathways to
769
+ * Ethereum, Arbitrum, Plasma, Polygon and Optimism between ~9m20s and ~10m10s.
770
+ */
771
+ minDeliverySec?: number;
746
772
  }
747
773
  declare const BRIDGE_CHAIN_CONFIGS: Partial<Record<Chain, BridgeChainConfig>>;
748
774
  /**
package/dist/index.js CHANGED
@@ -1751,7 +1751,7 @@ var XAUT0_DEPLOYMENTS = {
1751
1751
  ["bsc" /* BSC */]: { address: "0x21cAef8A43163Eea865baeE23b9C2E327696A3bf", decimals: 6, oftAdapter: "0x53C3A64c8942288e12813C1F8457Db45980BcfC2", approvalRequired: false }
1752
1752
  };
1753
1753
  var USDT0_LOCKBOX_CHAINS = /* @__PURE__ */ new Set(["ethereum" /* Ethereum */, "sepolia" /* Sepolia */]);
1754
- function bridgeChain(chain, lzEid, blockTimeSec, usdt0Adapter) {
1754
+ function bridgeChain(chain, lzEid, blockTimeSec, usdt0Adapter, minDeliverySec) {
1755
1755
  const base = C[chain];
1756
1756
  const tokens = {};
1757
1757
  if (usdt0Adapter) {
@@ -1764,7 +1764,7 @@ function bridgeChain(chain, lzEid, blockTimeSec, usdt0Adapter) {
1764
1764
  }
1765
1765
  const xaut0 = XAUT0_DEPLOYMENTS[chain];
1766
1766
  if (xaut0) tokens.xaut0 = xaut0;
1767
- return { ...base, lzEid, blockTimeSec, tokens, oftAdapter: usdt0Adapter };
1767
+ return { ...base, lzEid, blockTimeSec, minDeliverySec, tokens, oftAdapter: usdt0Adapter };
1768
1768
  }
1769
1769
  var BRIDGE_CHAIN_CONFIGS = {
1770
1770
  // ─── Testnet ───────────────────────────────────────────────────────────────
@@ -1772,18 +1772,18 @@ var BRIDGE_CHAIN_CONFIGS = {
1772
1772
  ["stable-testnet" /* StableTestnet */]: bridgeChain("stable-testnet" /* StableTestnet */, 40374, 0.7, "0x779Ded0c9e1022225f8E0630b35a9b54bE713736"),
1773
1773
  // ─── Mainnet ──────────────────────────────────────────────────────────────
1774
1774
  ["stable" /* Stable */]: bridgeChain("stable" /* Stable */, 30396, 0.7, "0xedaba024be4d87974d5aB11C6Dd586963CcCB027"),
1775
- ["ethereum" /* Ethereum */]: bridgeChain("ethereum" /* Ethereum */, 30101, 12, "0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee"),
1776
- ["arbitrum" /* Arbitrum */]: bridgeChain("arbitrum" /* Arbitrum */, 30110, 0.25, "0x14E4A1B13bf7F943c8ff7C51fb60FA964A298D92"),
1775
+ ["ethereum" /* Ethereum */]: bridgeChain("ethereum" /* Ethereum */, 30101, 12, "0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee", 1170),
1776
+ ["arbitrum" /* Arbitrum */]: bridgeChain("arbitrum" /* Arbitrum */, 30110, 0.25, "0x14E4A1B13bf7F943c8ff7C51fb60FA964A298D92", 1350),
1777
1777
  ["ink" /* Ink */]: bridgeChain("ink" /* Ink */, 30339, 1, "0x1cB6De532588fCA4a21B7209DE7C456AF8434A65"),
1778
- ["bera" /* Bera */]: bridgeChain("bera" /* Bera */, 30362, 2, "0x3Dc96399109df5ceb2C226664A086140bD0379cB"),
1778
+ ["bera" /* Bera */]: bridgeChain("bera" /* Bera */, 30362, 2, "0x3Dc96399109df5ceb2C226664A086140bD0379cB", 135),
1779
1779
  ["megaeth" /* MegaETH */]: bridgeChain("megaeth" /* MegaETH */, 30398, 1, "0x9151434b16b9763660705744891fa906f660ecc5"),
1780
1780
  ["hyperevm" /* HyperEVM */]: bridgeChain("hyperevm" /* HyperEVM */, 30367, 1, "0x904861a24F30EC96ea7CFC3bE9EA4B476d237e98"),
1781
- ["polygon" /* Polygon */]: bridgeChain("polygon" /* Polygon */, 30109, 1.5, "0x6BA10300f0DC58B7a1e4c0e41f5daBb7D7829e13"),
1781
+ ["polygon" /* Polygon */]: bridgeChain("polygon" /* Polygon */, 30109, 1.5, "0x6BA10300f0DC58B7a1e4c0e41f5daBb7D7829e13", 70),
1782
1782
  ["plasma" /* Plasma */]: bridgeChain("plasma" /* Plasma */, 30383, 1, "0x02ca37966753bDdDf11216B73B16C1dE756A7CF9"),
1783
1783
  ["optimism" /* Optimism */]: bridgeChain("optimism" /* Optimism */, 30111, 2, "0xF03b4d9AC1D5d1E7c4cEf54C2A313b9fe051A0aD"),
1784
1784
  ["unichain" /* Unichain */]: bridgeChain("unichain" /* Unichain */, 30320, 1, "0xc07bE8994D035631c36fb4a89C918CeFB2f03EC3"),
1785
1785
  ["sei" /* Sei */]: bridgeChain("sei" /* Sei */, 30280, 0.5, "0x56Fe74A2e3b484b921c447357203431a3485CC60"),
1786
- ["flare" /* Flare */]: bridgeChain("flare" /* Flare */, 30295, 1.1, "0x567287d2A9829215a37e3B88843d32f9221E7588"),
1786
+ ["flare" /* Flare */]: bridgeChain("flare" /* Flare */, 30295, 1.1, "0x567287d2A9829215a37e3B88843d32f9221E7588", 600),
1787
1787
  ["xlayer" /* XLayer */]: bridgeChain("xlayer" /* XLayer */, 30274, 1, "0x94BCCa6bdfd6A61817Ab0E960bFedE4984505554"),
1788
1788
  ["rootstock" /* Rootstock */]: bridgeChain("rootstock" /* Rootstock */, 30333, 31, "0x1a594d5d5d1c426281C1064B07f23F57B2716B61"),
1789
1789
  // XAUt0 only — BSC has no USDT0 deployment. Block time measured 2026-08-20.
@@ -1964,6 +1964,14 @@ function median(values) {
1964
1964
  const mid = Math.floor(sorted.length / 2);
1965
1965
  return sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
1966
1966
  }
1967
+ function resolveDeliverySeconds(formulaSeconds, observedSeconds, minDeliverySec) {
1968
+ const base = formulaSeconds ?? observedSeconds;
1969
+ if (base === void 0) return void 0;
1970
+ if (minDeliverySec !== void 0 && minDeliverySec > base) {
1971
+ return { seconds: minDeliverySec, source: "floor" };
1972
+ }
1973
+ return { seconds: base, source: formulaSeconds !== void 0 ? "formula" : "observed" };
1974
+ }
1967
1975
  var WORKER_EXECUTOR = 1;
1968
1976
  var OPTION_LZRECEIVE = 1;
1969
1977
  var OPTION_NATIVE_DROP = 2;
@@ -2383,16 +2391,22 @@ var LayerZeroService = class {
2383
2391
  /**
2384
2392
  * Estimate how long a USDT0 bridge will take, end to end.
2385
2393
  *
2386
- * Two independent inputs, both cheap:
2387
- * - **observed** — median delivery time of recent messages LayerZero Scan has
2388
- * already settled on this exact pathway.
2389
- * - **formula** — LayerZero's documented estimate from the on-chain ULN config:
2390
- * `srcBlockTime × confirmations + dstBlockTime × (2 + dvnCount)`.
2394
+ * The number comes from the pathway's live ULN config:
2395
+ * `srcBlockTime × confirmations + dstBlockTime × (2 + dvnCount)`, floored at the
2396
+ * source chain's {@link BridgeChainConfig.minDeliverySec} where the confirmation
2397
+ * count is not the whole wait. This is what the DVNs are actually bound by, so it
2398
+ * tracks a config change the moment it lands — and on every pathway that is purely
2399
+ * confirmation-bound it agrees with both usdt0.to's own estimate and the delivery
2400
+ * times Scan reports to well under a percent.
2391
2401
  *
2392
- * `observed` wins when available. The formula assumes `confirmations` is the whole
2393
- * wait, which holds for L1 sources but badly understates rollups: Arbitrum→Stable
2394
- * computes to ~15s (60 blocks × 0.25s) while real deliveries take ~18min, because
2395
- * the DVNs wait for the L2 batch to finalize on L1.
2402
+ * The observed median of recent deliveries on this exact pathway is fetched too
2403
+ * and reported as {@link BridgeTimeEstimate.observedSeconds}, but it no longer
2404
+ * drives `seconds`. History lags the config: after Optimism→Stable was raised to
2405
+ * 1000 confirmations, a 50-message window still reaching back 261 days put the
2406
+ * median at ~15min against a real ~33min, and pathways quiet for months (Unichain,
2407
+ * XAUt0 from Arbitrum) were answering from configs that no longer exist. It stays
2408
+ * the fallback for the case the formula cannot be computed at all — an RPC that
2409
+ * will not serve the ULN read.
2396
2410
  */
2397
2411
  async estimateBridgeTime({
2398
2412
  fromChain,
@@ -2423,16 +2437,15 @@ var LayerZeroService = class {
2423
2437
  );
2424
2438
  formulaSeconds = Number.isFinite(computed) ? computed : void 0;
2425
2439
  }
2426
- const seconds = observedSeconds ?? formulaSeconds;
2427
- if (seconds === void 0) {
2440
+ const resolved = resolveDeliverySeconds(formulaSeconds, observedSeconds, src.minDeliverySec);
2441
+ if (!resolved) {
2428
2442
  throw new StableQuoteError({
2429
2443
  provider: "layerzero",
2430
2444
  shortMessage: `estimateBridgeTime: no pathway data for ${fromChain}\u2192${toChain}`
2431
2445
  });
2432
2446
  }
2433
2447
  return {
2434
- seconds,
2435
- source: observedSeconds !== void 0 ? "observed" : "formula",
2448
+ ...resolved,
2436
2449
  observedSeconds,
2437
2450
  formulaSeconds,
2438
2451
  confirmations: Number.isFinite(uln?.confirmations) ? uln?.confirmations : void 0,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stablechain/sdk",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "Stable SDK",
5
5
  "keywords": ["stable", "usdt", "gasless", "web3", "yield"],
6
6
  "license": "MIT",