@stablechain/sdk 1.0.3 → 1.0.5

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/dist/index.js CHANGED
@@ -1,11 +1,33 @@
1
+ import {
2
+ ERC3009_ABI,
3
+ STABLE_USDT_ADDRESS,
4
+ STABLE_VAULT_ADDRESS,
5
+ StableBulkError,
6
+ StableError,
7
+ StableNetworkError,
8
+ StableQuoteError,
9
+ StableTransactionError,
10
+ StableValidationError,
11
+ cancelAuthorization,
12
+ erc3009Actions,
13
+ getAuthorizationState,
14
+ getAuthorizationUsage,
15
+ prepareTransferWithAuthorization,
16
+ receiveWithAuthorization,
17
+ signReceiveAuthorization,
18
+ signTransferAuthorization,
19
+ transferWithAuthorization,
20
+ verifyAuthorization
21
+ } from "./chunk-VS6GBRDF.js";
22
+
1
23
  // src/stable.ts
2
24
  import {
3
- http as http5,
4
- createPublicClient as createPublicClient5,
5
- createWalletClient as createWalletClient4,
6
- erc20Abi as erc20Abi4,
7
- isAddress as isAddress4,
8
- parseUnits as parseUnits4
25
+ http as http6,
26
+ createPublicClient as createPublicClient6,
27
+ createWalletClient as createWalletClient5,
28
+ erc20Abi as erc20Abi5,
29
+ isAddress as isAddress5,
30
+ parseUnits as parseUnits5
9
31
  } from "viem";
10
32
  import { stable as stableMainnet2, stableTestnet as stableTestnet2 } from "viem/chains";
11
33
 
@@ -25,85 +47,6 @@ import {
25
47
  parseUnits
26
48
  } from "viem";
27
49
 
28
- // src/errors.ts
29
- import { BaseError } from "viem";
30
- var StableError = class extends BaseError {
31
- };
32
- var StableValidationError = class extends StableError {
33
- constructor(field, value, reason, opts) {
34
- super(reason, {
35
- name: "StableValidationError",
36
- cause: opts?.cause,
37
- metaMessages: [`Field: ${field}`, `Value: ${formatValue(value)}`]
38
- });
39
- this.field = field;
40
- this.value = value;
41
- }
42
- };
43
- var StableQuoteError = class extends StableError {
44
- constructor(args) {
45
- const meta = [`Provider: ${args.provider}`];
46
- if (args.httpStatus !== void 0)
47
- meta.push(`HTTP status: ${args.httpStatus}`);
48
- if (args.providerCode) meta.push(`Provider code: ${args.providerCode}`);
49
- super(args.shortMessage, {
50
- name: "StableQuoteError",
51
- cause: args.cause,
52
- metaMessages: meta
53
- });
54
- this.provider = args.provider;
55
- this.httpStatus = args.httpStatus;
56
- this.providerCode = args.providerCode;
57
- this.body = args.body;
58
- }
59
- };
60
- var StableTransactionError = class extends StableError {
61
- constructor(args) {
62
- const meta = [`Phase: ${args.phase}`];
63
- if (args.chainId !== void 0) meta.push(`Chain ID: ${args.chainId}`);
64
- if (args.txHash) meta.push(`Tx hash: ${args.txHash}`);
65
- if (args.revertReason) meta.push(`Revert reason: ${args.revertReason}`);
66
- super(args.shortMessage, {
67
- name: "StableTransactionError",
68
- cause: args.cause,
69
- metaMessages: meta
70
- });
71
- this.phase = args.phase;
72
- this.txHash = args.txHash;
73
- this.chainId = args.chainId;
74
- this.revertReason = args.revertReason;
75
- }
76
- };
77
- var StableNetworkError = class extends StableError {
78
- constructor(args) {
79
- super(args.shortMessage, {
80
- name: "StableNetworkError",
81
- cause: args.cause,
82
- metaMessages: args.url ? [`URL: ${args.url}`] : void 0
83
- });
84
- this.url = args.url;
85
- }
86
- };
87
- var StableBulkError = class extends StableError {
88
- constructor(args) {
89
- super(args.shortMessage, { name: "StableBulkError" });
90
- this.results = args.results;
91
- this.succeeded = args.succeeded;
92
- this.failed = args.failed;
93
- }
94
- };
95
- function formatValue(v) {
96
- if (typeof v === "bigint") return `${v}n`;
97
- if (typeof v === "string") return JSON.stringify(v);
98
- if (v === null) return "null";
99
- if (v === void 0) return "undefined";
100
- try {
101
- return JSON.stringify(v);
102
- } catch {
103
- return String(v);
104
- }
105
- }
106
-
107
50
  // src/helpers/chain.ts
108
51
  import { defineChain } from "viem";
109
52
  function makeChain(id, rpc) {
@@ -819,6 +762,14 @@ var Chain = /* @__PURE__ */ ((Chain2) => {
819
762
  Chain2["Base"] = "base";
820
763
  Chain2["BSC"] = "bsc";
821
764
  Chain2["HyperEVM"] = "hyperevm";
765
+ Chain2["Polygon"] = "polygon";
766
+ Chain2["Plasma"] = "plasma";
767
+ Chain2["Optimism"] = "optimism";
768
+ Chain2["Unichain"] = "unichain";
769
+ Chain2["Sei"] = "sei";
770
+ Chain2["Flare"] = "flare";
771
+ Chain2["XLayer"] = "xlayer";
772
+ Chain2["Rootstock"] = "rootstock";
822
773
  return Chain2;
823
774
  })(Chain || {});
824
775
  var Network = /* @__PURE__ */ ((Network2) => {
@@ -1259,7 +1210,7 @@ var CHAIN_CONFIGS = {
1259
1210
  id: 11155111,
1260
1211
  rpc: "https://ethereum-sepolia-rpc.publicnode.com",
1261
1212
  usdt: "0xc4DCC311c028e341fd8602D8eB89c5de94625927",
1262
- decimals: 18
1213
+ decimals: 6
1263
1214
  },
1264
1215
  ["stable-testnet" /* StableTestnet */]: {
1265
1216
  id: 2201,
@@ -1277,13 +1228,15 @@ var CHAIN_CONFIGS = {
1277
1228
  ["ethereum" /* Ethereum */]: {
1278
1229
  id: 1,
1279
1230
  rpc: "https://ethereum-rpc.publicnode.com",
1280
- usdt: "0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee",
1231
+ // Canonical USDT. Not the OFT adapter — this field is used as an ERC-20
1232
+ // (allowance/approve/balanceOf) and as the token address for LI.FI.
1233
+ usdt: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
1281
1234
  decimals: 6
1282
1235
  },
1283
1236
  ["arbitrum" /* Arbitrum */]: {
1284
1237
  id: 42161,
1285
1238
  rpc: "https://arbitrum-one-rpc.publicnode.com",
1286
- usdt: "0x14E4A1B13bf7F943c8ff7C51fb60FA964A298D92",
1239
+ usdt: "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
1287
1240
  decimals: 6
1288
1241
  },
1289
1242
  ["ink" /* Ink */]: {
@@ -1300,7 +1253,7 @@ var CHAIN_CONFIGS = {
1300
1253
  },
1301
1254
  ["megaeth" /* MegaETH */]: {
1302
1255
  id: 4326,
1303
- rpc: "https://carrot.megaeth.com",
1256
+ rpc: "https://mainnet.megaeth.com/rpc",
1304
1257
  usdt: "0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb",
1305
1258
  decimals: 6
1306
1259
  },
@@ -1321,6 +1274,55 @@ var CHAIN_CONFIGS = {
1321
1274
  rpc: "https://rpc.hyperliquid.xyz/evm",
1322
1275
  usdt: "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb",
1323
1276
  decimals: 6
1277
+ },
1278
+ ["polygon" /* Polygon */]: {
1279
+ id: 137,
1280
+ rpc: "https://polygon-bor-rpc.publicnode.com",
1281
+ // Polygon's canonical USDT, migrated to USDT0
1282
+ usdt: "0xc2132D05D31c914a87C6611C10748AEb04B58e8F",
1283
+ decimals: 6
1284
+ },
1285
+ ["plasma" /* Plasma */]: {
1286
+ id: 9745,
1287
+ rpc: "https://rpc.plasma.to",
1288
+ usdt: "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb",
1289
+ decimals: 6
1290
+ },
1291
+ ["optimism" /* Optimism */]: {
1292
+ id: 10,
1293
+ rpc: "https://optimism-rpc.publicnode.com",
1294
+ usdt: "0x01bFF41798a0BcF287b996046Ca68b395DbC1071",
1295
+ decimals: 6
1296
+ },
1297
+ ["unichain" /* Unichain */]: {
1298
+ id: 130,
1299
+ rpc: "https://unichain-rpc.publicnode.com",
1300
+ usdt: "0x9151434b16b9763660705744891fA906F660EcC5",
1301
+ decimals: 6
1302
+ },
1303
+ ["sei" /* Sei */]: {
1304
+ id: 1329,
1305
+ rpc: "https://sei-evm-rpc.publicnode.com",
1306
+ usdt: "0x9151434b16b9763660705744891fA906F660EcC5",
1307
+ decimals: 6
1308
+ },
1309
+ ["flare" /* Flare */]: {
1310
+ id: 14,
1311
+ rpc: "https://flare-api.flare.network/ext/C/rpc",
1312
+ usdt: "0xe7cd86e13AC4309349F30B3435a9d337750fC82D",
1313
+ decimals: 6
1314
+ },
1315
+ ["xlayer" /* XLayer */]: {
1316
+ id: 196,
1317
+ rpc: "https://rpc.xlayer.tech",
1318
+ usdt: "0x779Ded0c9e1022225f8E0630b35a9b54bE713736",
1319
+ decimals: 6
1320
+ },
1321
+ ["rootstock" /* Rootstock */]: {
1322
+ id: 30,
1323
+ rpc: "https://public-node.rsk.co",
1324
+ usdt: "0x779Ded0c9e1022225f8E0630b35a9b54bE713736",
1325
+ decimals: 6
1324
1326
  }
1325
1327
  };
1326
1328
 
@@ -1723,14 +1725,522 @@ function isLiFiQuoteShape(v) {
1723
1725
  return isObject(tx) && typeof tx.to === "string" && typeof tx.data === "string" && typeof tx.chainId === "number" && isObject(est) && typeof est.fromAmount === "string" && typeof est.toAmount === "string" && isObject(act) && isObject(act.fromToken) && isObject(act.toToken);
1724
1726
  }
1725
1727
 
1728
+ // src/services/layerzero.ts
1729
+ import {
1730
+ concat,
1731
+ createPublicClient as createPublicClient5,
1732
+ createWalletClient as createWalletClient4,
1733
+ erc20Abi as erc20Abi4,
1734
+ http as http5,
1735
+ isAddress as isAddress4,
1736
+ numberToHex,
1737
+ padHex,
1738
+ parseUnits as parseUnits4
1739
+ } from "viem";
1740
+ var C = CHAIN_CONFIGS;
1741
+ var BRIDGE_CHAIN_CONFIGS = {
1742
+ // ─── Testnet ───────────────────────────────────────────────────────────────
1743
+ ["sepolia" /* Sepolia */]: { ...C["sepolia" /* Sepolia */], lzEid: 40161, oftAdapter: "0xc099cD946d5efCC35A99D64E808c1430cEf08126", blockTimeSec: 12 },
1744
+ ["stable-testnet" /* StableTestnet */]: { ...C["stable-testnet" /* StableTestnet */], lzEid: 40374, oftAdapter: "0x779Ded0c9e1022225f8E0630b35a9b54bE713736", blockTimeSec: 0.7 },
1745
+ // ─── Mainnet ──────────────────────────────────────────────────────────────
1746
+ ["stable" /* Stable */]: { ...C["stable" /* Stable */], lzEid: 30396, oftAdapter: "0xedaba024be4d87974d5aB11C6Dd586963CcCB027", blockTimeSec: 0.7 },
1747
+ ["ethereum" /* Ethereum */]: { ...C["ethereum" /* Ethereum */], lzEid: 30101, oftAdapter: "0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee", blockTimeSec: 12 },
1748
+ ["arbitrum" /* Arbitrum */]: { ...C["arbitrum" /* Arbitrum */], lzEid: 30110, oftAdapter: "0x14E4A1B13bf7F943c8ff7C51fb60FA964A298D92", blockTimeSec: 0.25 },
1749
+ ["ink" /* Ink */]: { ...C["ink" /* Ink */], lzEid: 30339, oftAdapter: "0x1cB6De532588fCA4a21B7209DE7C456AF8434A65", blockTimeSec: 1 },
1750
+ ["bera" /* Bera */]: { ...C["bera" /* Bera */], lzEid: 30362, oftAdapter: "0x3Dc96399109df5ceb2C226664A086140bD0379cB", blockTimeSec: 2 },
1751
+ ["megaeth" /* MegaETH */]: { ...C["megaeth" /* MegaETH */], lzEid: 30398, oftAdapter: "0x9151434b16b9763660705744891fa906f660ecc5", blockTimeSec: 1 },
1752
+ ["hyperevm" /* HyperEVM */]: { ...C["hyperevm" /* HyperEVM */], lzEid: 30367, oftAdapter: "0x904861a24F30EC96ea7CFC3bE9EA4B476d237e98", blockTimeSec: 1 },
1753
+ ["polygon" /* Polygon */]: { ...C["polygon" /* Polygon */], lzEid: 30109, oftAdapter: "0x6BA10300f0DC58B7a1e4c0e41f5daBb7D7829e13", blockTimeSec: 1.5 },
1754
+ ["plasma" /* Plasma */]: { ...C["plasma" /* Plasma */], lzEid: 30383, oftAdapter: "0x02ca37966753bDdDf11216B73B16C1dE756A7CF9", blockTimeSec: 1 },
1755
+ ["optimism" /* Optimism */]: { ...C["optimism" /* Optimism */], lzEid: 30111, oftAdapter: "0xF03b4d9AC1D5d1E7c4cEf54C2A313b9fe051A0aD", blockTimeSec: 2 },
1756
+ ["unichain" /* Unichain */]: { ...C["unichain" /* Unichain */], lzEid: 30320, oftAdapter: "0xc07bE8994D035631c36fb4a89C918CeFB2f03EC3", blockTimeSec: 1 },
1757
+ ["sei" /* Sei */]: { ...C["sei" /* Sei */], lzEid: 30280, oftAdapter: "0x56Fe74A2e3b484b921c447357203431a3485CC60", blockTimeSec: 0.5 },
1758
+ ["flare" /* Flare */]: { ...C["flare" /* Flare */], lzEid: 30295, oftAdapter: "0x567287d2A9829215a37e3B88843d32f9221E7588", blockTimeSec: 1.1 },
1759
+ ["xlayer" /* XLayer */]: { ...C["xlayer" /* XLayer */], lzEid: 30274, oftAdapter: "0x94BCCa6bdfd6A61817Ab0E960bFedE4984505554", blockTimeSec: 1 },
1760
+ ["rootstock" /* Rootstock */]: { ...C["rootstock" /* Rootstock */], lzEid: 30333, oftAdapter: "0x1a594d5d5d1c426281C1064B07f23F57B2716B61", blockTimeSec: 31 }
1761
+ };
1762
+ var OFT_ADAPTER_ABI = [
1763
+ {
1764
+ name: "quoteSend",
1765
+ type: "function",
1766
+ inputs: [
1767
+ {
1768
+ name: "_sendParam",
1769
+ type: "tuple",
1770
+ components: [
1771
+ { name: "dstEid", type: "uint32" },
1772
+ { name: "to", type: "bytes32" },
1773
+ { name: "amountLD", type: "uint256" },
1774
+ { name: "minAmountLD", type: "uint256" },
1775
+ { name: "extraOptions", type: "bytes" },
1776
+ { name: "composeMsg", type: "bytes" },
1777
+ { name: "oftCmd", type: "bytes" }
1778
+ ]
1779
+ },
1780
+ { name: "_payInLzToken", type: "bool" }
1781
+ ],
1782
+ outputs: [
1783
+ {
1784
+ name: "msgFee",
1785
+ type: "tuple",
1786
+ components: [
1787
+ { name: "nativeFee", type: "uint256" },
1788
+ { name: "lzTokenFee", type: "uint256" }
1789
+ ]
1790
+ }
1791
+ ],
1792
+ stateMutability: "view"
1793
+ },
1794
+ {
1795
+ name: "send",
1796
+ type: "function",
1797
+ inputs: [
1798
+ {
1799
+ name: "_sendParam",
1800
+ type: "tuple",
1801
+ components: [
1802
+ { name: "dstEid", type: "uint32" },
1803
+ { name: "to", type: "bytes32" },
1804
+ { name: "amountLD", type: "uint256" },
1805
+ { name: "minAmountLD", type: "uint256" },
1806
+ { name: "extraOptions", type: "bytes" },
1807
+ { name: "composeMsg", type: "bytes" },
1808
+ { name: "oftCmd", type: "bytes" }
1809
+ ]
1810
+ },
1811
+ {
1812
+ name: "_fee",
1813
+ type: "tuple",
1814
+ components: [
1815
+ { name: "nativeFee", type: "uint256" },
1816
+ { name: "lzTokenFee", type: "uint256" }
1817
+ ]
1818
+ },
1819
+ { name: "_refundAddress", type: "address" }
1820
+ ],
1821
+ outputs: [],
1822
+ stateMutability: "payable"
1823
+ }
1824
+ ];
1825
+ var OAPP_ABI = [
1826
+ { name: "endpoint", type: "function", stateMutability: "view", inputs: [], outputs: [{ type: "address" }] }
1827
+ ];
1828
+ var ENDPOINT_V2_ABI = [
1829
+ {
1830
+ name: "getSendLibrary",
1831
+ type: "function",
1832
+ stateMutability: "view",
1833
+ inputs: [{ name: "_sender", type: "address" }, { name: "_dstEid", type: "uint32" }],
1834
+ outputs: [{ type: "address" }]
1835
+ }
1836
+ ];
1837
+ var SEND_ULN_ABI = [
1838
+ {
1839
+ name: "getUlnConfig",
1840
+ type: "function",
1841
+ stateMutability: "view",
1842
+ inputs: [{ name: "_oapp", type: "address" }, { name: "_remoteEid", type: "uint32" }],
1843
+ outputs: [
1844
+ {
1845
+ name: "rtnConfig",
1846
+ type: "tuple",
1847
+ components: [
1848
+ { name: "confirmations", type: "uint64" },
1849
+ { name: "requiredDVNCount", type: "uint8" },
1850
+ { name: "optionalDVNCount", type: "uint8" },
1851
+ { name: "optionalDVNThreshold", type: "uint8" },
1852
+ { name: "requiredDVNs", type: "address[]" },
1853
+ { name: "optionalDVNs", type: "address[]" }
1854
+ ]
1855
+ }
1856
+ ]
1857
+ }
1858
+ ];
1859
+ var LZ_SCAN_API = "https://scan.layerzero-api.com/v1";
1860
+ var LZ_SCAN_UI = "https://layerzeroscan.com";
1861
+ var CACHE_TTL_MS = 5 * 6e4;
1862
+ var cache = /* @__PURE__ */ new Map();
1863
+ function clearBridgeEstimateCache() {
1864
+ cache.clear();
1865
+ }
1866
+ async function cached(key, ttlMs, load) {
1867
+ const hit = cache.get(key);
1868
+ if (hit && Date.now() - hit.at < ttlMs) return hit.value;
1869
+ const value = await load();
1870
+ cache.set(key, { at: Date.now(), value });
1871
+ return value;
1872
+ }
1873
+ var ScanHttpError = class extends Error {
1874
+ constructor(status, path) {
1875
+ super(`LayerZero Scan ${status} for ${path}`);
1876
+ this.status = status;
1877
+ this.name = "ScanHttpError";
1878
+ }
1879
+ };
1880
+ async function scanFetch(path, timeoutMs = 8e3) {
1881
+ const res = await fetch(`${LZ_SCAN_API}${path}`, { signal: AbortSignal.timeout(timeoutMs) });
1882
+ if (!res.ok) throw new ScanHttpError(res.status, path);
1883
+ return res.json();
1884
+ }
1885
+ var TERMINAL_FAILURE_STATUSES = /* @__PURE__ */ new Set([
1886
+ "FAILED",
1887
+ "BLOCKED",
1888
+ "PAYLOAD_STORED",
1889
+ "APPLICATION_BURNED",
1890
+ "APPLICATION_SKIPPED",
1891
+ "UNRESOLVABLE_COMMAND",
1892
+ "MALFORMED_COMMAND"
1893
+ ]);
1894
+ function median(values) {
1895
+ const sorted = [...values].sort((a, b) => a - b);
1896
+ const mid = Math.floor(sorted.length / 2);
1897
+ return sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
1898
+ }
1899
+ function buildLzOptions(gasLimit = 0n) {
1900
+ return concat([
1901
+ numberToHex(3, { size: 2 }),
1902
+ // TYPE_3 header
1903
+ numberToHex(1, { size: 1 }),
1904
+ // Executor worker ID
1905
+ numberToHex(17, { size: 2 }),
1906
+ // option payload length = 17 bytes
1907
+ numberToHex(1, { size: 1 }),
1908
+ // LzReceive option type
1909
+ numberToHex(gasLimit, { size: 16 })
1910
+ // gas limit as uint128
1911
+ ]);
1912
+ }
1913
+ var LayerZeroService = class {
1914
+ constructor(config) {
1915
+ this.config = config;
1916
+ }
1917
+ async quoteBridge(params) {
1918
+ const [fee, estimate] = await Promise.all([
1919
+ this.quoteFee(params, "quoteBridge"),
1920
+ this.estimateBridgeTime(params).catch(() => void 0)
1921
+ ]);
1922
+ return { ...fee, toAmount: params.amount, estimate };
1923
+ }
1924
+ /**
1925
+ * LayerZero messaging fee for a send, with no delivery estimate attached — the
1926
+ * send path needs the fee and nothing else, and must not wait on Scan for it.
1927
+ */
1928
+ async quoteFee({ fromChain, toChain, fromToken, toToken, amount, recipient }, method) {
1929
+ const recipientAddr = recipient ?? "0x0000000000000000000000000000000000000000";
1930
+ this.validateBridgeParams({ fromChain, toChain, amount, recipient: recipientAddr }, method);
1931
+ const { src, dst } = this.resolveChains(fromChain, toChain, fromToken, toToken);
1932
+ const oftAdapter = src.oftAdapter;
1933
+ const amountLD = parseUnits4(String(amount), src.decimals);
1934
+ try {
1935
+ const { nativeFee, lzTokenFee } = await createPublicClient5({
1936
+ chain: makeChain(src.id, src.rpc),
1937
+ transport: http5(src.rpc)
1938
+ }).readContract({
1939
+ address: oftAdapter,
1940
+ abi: OFT_ADAPTER_ABI,
1941
+ functionName: "quoteSend",
1942
+ args: [this.buildSendParams(dst, amountLD, recipientAddr), false]
1943
+ });
1944
+ return { nativeFee, lzTokenFee };
1945
+ } catch (err) {
1946
+ throw new StableQuoteError({
1947
+ provider: "layerzero",
1948
+ shortMessage: `${method}: quoteSend call failed`,
1949
+ cause: err
1950
+ });
1951
+ }
1952
+ }
1953
+ async bridge({ fromChain, toChain, fromToken, toToken, amount, recipient, quote }) {
1954
+ const from = this.config.walletClient?.account?.address ?? this.config.account?.address;
1955
+ if (!from) {
1956
+ throw new StableValidationError(
1957
+ "signer",
1958
+ void 0,
1959
+ "bridge: requires a signer \u2014 set walletClient or account in StableConfig"
1960
+ );
1961
+ }
1962
+ const recipientAddr = recipient ?? from;
1963
+ this.validateBridgeParams({ fromChain, toChain, amount, recipient: recipientAddr }, "bridge");
1964
+ const { src, dst } = this.resolveChains(fromChain, toChain, fromToken, toToken);
1965
+ const oftAdapter = src.oftAdapter;
1966
+ const srcChain = makeChain(src.id, src.rpc);
1967
+ const wc = this.config.walletClient ?? createWalletClient4({
1968
+ account: this.config.account,
1969
+ chain: srcChain,
1970
+ transport: this.config.transport ?? http5(src.rpc)
1971
+ });
1972
+ const pc = createPublicClient5({ chain: srcChain, transport: http5(src.rpc) });
1973
+ try {
1974
+ await wc.switchChain({ id: src.id });
1975
+ } catch (err) {
1976
+ throw new StableTransactionError({
1977
+ phase: "switch_chain",
1978
+ shortMessage: `bridge: wallet rejected or failed to switch to chain ${src.id}`,
1979
+ chainId: src.id,
1980
+ cause: err
1981
+ });
1982
+ }
1983
+ const amountLD = parseUnits4(String(amount), src.decimals);
1984
+ const sendParams = this.buildSendParams(dst, amountLD, recipientAddr);
1985
+ const { nativeFee, lzTokenFee } = quote?.nativeFee !== void 0 && quote?.lzTokenFee !== void 0 ? { nativeFee: quote.nativeFee, lzTokenFee: quote.lzTokenFee } : await this.quoteFee({ fromChain, toChain, fromToken, toToken, amount, recipient: recipientAddr }, "bridge");
1986
+ const allowance = await pc.readContract({
1987
+ address: src.usdt,
1988
+ abi: erc20Abi4,
1989
+ functionName: "allowance",
1990
+ args: [from, oftAdapter]
1991
+ });
1992
+ if (allowance < amountLD) {
1993
+ let approveHash;
1994
+ try {
1995
+ approveHash = await wc.writeContract({
1996
+ address: src.usdt,
1997
+ abi: erc20Abi4,
1998
+ functionName: "approve",
1999
+ args: [oftAdapter, amountLD],
2000
+ account: wc.account,
2001
+ chain: srcChain
2002
+ });
2003
+ } catch (err) {
2004
+ throw new StableTransactionError({
2005
+ phase: "approve",
2006
+ shortMessage: "bridge: USDT0 approve failed",
2007
+ chainId: src.id,
2008
+ cause: err
2009
+ });
2010
+ }
2011
+ const approveReceipt = await pc.waitForTransactionReceipt({ hash: approveHash });
2012
+ if (approveReceipt.status === "reverted") {
2013
+ throw new StableTransactionError({
2014
+ phase: "approve",
2015
+ shortMessage: "bridge: USDT0 approve reverted on-chain",
2016
+ txHash: approveReceipt.transactionHash,
2017
+ chainId: src.id
2018
+ });
2019
+ }
2020
+ }
2021
+ try {
2022
+ const hash = await wc.writeContract({
2023
+ address: oftAdapter,
2024
+ abi: OFT_ADAPTER_ABI,
2025
+ functionName: "send",
2026
+ args: [sendParams, { nativeFee, lzTokenFee }, from],
2027
+ value: nativeFee,
2028
+ account: wc.account,
2029
+ chain: srcChain
2030
+ });
2031
+ return { txHash: hash, toAmount: amount };
2032
+ } catch (err) {
2033
+ throw new StableTransactionError({
2034
+ phase: "send",
2035
+ shortMessage: "bridge: OFT send failed",
2036
+ chainId: src.id,
2037
+ cause: err
2038
+ });
2039
+ }
2040
+ }
2041
+ /**
2042
+ * Estimate how long a USDT0 bridge will take, end to end.
2043
+ *
2044
+ * Two independent inputs, both cheap:
2045
+ * - **observed** — median delivery time of recent messages LayerZero Scan has
2046
+ * already settled on this exact pathway.
2047
+ * - **formula** — LayerZero's documented estimate from the on-chain ULN config:
2048
+ * `srcBlockTime × confirmations + dstBlockTime × (2 + dvnCount)`.
2049
+ *
2050
+ * `observed` wins when available. The formula assumes `confirmations` is the whole
2051
+ * wait, which holds for L1 sources but badly understates rollups: Arbitrum→Stable
2052
+ * computes to ~15s (60 blocks × 0.25s) while real deliveries take ~18min, because
2053
+ * the DVNs wait for the L2 batch to finalize on L1.
2054
+ */
2055
+ async estimateBridgeTime({ fromChain, toChain }) {
2056
+ const src = BRIDGE_CHAIN_CONFIGS[fromChain];
2057
+ const dst = BRIDGE_CHAIN_CONFIGS[toChain];
2058
+ if (!src?.oftAdapter) {
2059
+ throw new StableValidationError("fromChain", fromChain, `estimateBridgeTime: no OFT adapter for "${fromChain}"`);
2060
+ }
2061
+ if (!dst?.oftAdapter) {
2062
+ throw new StableValidationError("toChain", toChain, `estimateBridgeTime: no OFT adapter for "${toChain}"`);
2063
+ }
2064
+ const [uln, observedSeconds] = await Promise.all([
2065
+ this.getUlnConfig(src, dst).catch(() => void 0),
2066
+ this.observedBridgeSeconds(src, dst).catch(() => void 0)
2067
+ ]);
2068
+ const dvnCount = uln ? uln.requiredDVNCount + uln.optionalDVNCount : void 0;
2069
+ let formulaSeconds;
2070
+ if (uln && dvnCount !== void 0 && src.blockTimeSec && dst.blockTimeSec) {
2071
+ const computed = Math.round(
2072
+ uln.confirmations * src.blockTimeSec + dst.blockTimeSec * (2 + dvnCount)
2073
+ );
2074
+ formulaSeconds = Number.isFinite(computed) ? computed : void 0;
2075
+ }
2076
+ const seconds = observedSeconds ?? formulaSeconds;
2077
+ if (seconds === void 0) {
2078
+ throw new StableQuoteError({
2079
+ provider: "layerzero",
2080
+ shortMessage: `estimateBridgeTime: no pathway data for ${fromChain}\u2192${toChain}`
2081
+ });
2082
+ }
2083
+ return {
2084
+ seconds,
2085
+ source: observedSeconds !== void 0 ? "observed" : "formula",
2086
+ observedSeconds,
2087
+ formulaSeconds,
2088
+ confirmations: Number.isFinite(uln?.confirmations) ? uln?.confirmations : void 0,
2089
+ dvnCount: Number.isFinite(dvnCount) ? dvnCount : void 0
2090
+ };
2091
+ }
2092
+ /**
2093
+ * Follow an in-flight bridge by its source transaction hash. Poll this after
2094
+ * `bridge()` to drive a progress UI; a message LayerZero Scan has not indexed
2095
+ * yet reports `"pending"` rather than throwing.
2096
+ */
2097
+ async trackBridge(srcTxHash) {
2098
+ const scanUrl = `${LZ_SCAN_UI}/tx/${srcTxHash}`;
2099
+ let message;
2100
+ try {
2101
+ message = (await scanFetch(`/messages/tx/${srcTxHash}`))?.data?.[0];
2102
+ } catch (err) {
2103
+ if (err instanceof ScanHttpError && err.status === 404) {
2104
+ return { state: "pending", srcTxHash, scanUrl };
2105
+ }
2106
+ throw new StableQuoteError({
2107
+ provider: "layerzero",
2108
+ shortMessage: `trackBridge: LayerZero Scan lookup failed for ${srcTxHash}`,
2109
+ cause: err
2110
+ });
2111
+ }
2112
+ if (!message) return { state: "pending", srcTxHash, scanUrl };
2113
+ const raw = message.status?.name ?? "";
2114
+ const sentAt = message.source?.tx?.blockTimestamp;
2115
+ return {
2116
+ // Everything terminal that is not DELIVERED means the tokens will not land
2117
+ // on their own; `raw` carries which of those it was.
2118
+ state: raw === "DELIVERED" ? "delivered" : TERMINAL_FAILURE_STATUSES.has(raw) ? "failed" : "inflight",
2119
+ raw,
2120
+ srcTxHash,
2121
+ dstTxHash: message.destination?.tx?.txHash,
2122
+ elapsedSeconds: sentAt ? Math.max(0, Math.floor(Date.now() / 1e3) - sentAt) : void 0,
2123
+ scanUrl
2124
+ };
2125
+ }
2126
+ /** Effective ULN config the DVNs enforce for this pathway, read from the source chain. */
2127
+ async getUlnConfig(src, dst) {
2128
+ return cached(`uln:${src.lzEid}-${dst.lzEid}`, CACHE_TTL_MS, async () => {
2129
+ const pc = createPublicClient5({ chain: makeChain(src.id, src.rpc), transport: http5(src.rpc) });
2130
+ const oapp = src.oftAdapter;
2131
+ const endpoint = await pc.readContract({ address: oapp, abi: OAPP_ABI, functionName: "endpoint" });
2132
+ const sendLib = await pc.readContract({
2133
+ address: endpoint,
2134
+ abi: ENDPOINT_V2_ABI,
2135
+ functionName: "getSendLibrary",
2136
+ args: [oapp, dst.lzEid]
2137
+ });
2138
+ const cfg = await pc.readContract({
2139
+ address: sendLib,
2140
+ abi: SEND_ULN_ABI,
2141
+ functionName: "getUlnConfig",
2142
+ args: [oapp, dst.lzEid]
2143
+ });
2144
+ return {
2145
+ confirmations: Number(cfg.confirmations),
2146
+ requiredDVNCount: Number(cfg.requiredDVNCount),
2147
+ optionalDVNCount: Number(cfg.optionalDVNCount)
2148
+ };
2149
+ });
2150
+ }
2151
+ /** Median delivery time of recently delivered messages on this pathway, in seconds. */
2152
+ async observedBridgeSeconds(src, dst) {
2153
+ const pathwayId = `${src.lzEid}-${dst.lzEid}-${src.oftAdapter.toLowerCase()}-${dst.oftAdapter.toLowerCase()}`;
2154
+ return cached(`observed:${pathwayId}`, CACHE_TTL_MS, async () => {
2155
+ const { data } = await scanFetch(`/messages/pathway/${pathwayId}?limit=50`);
2156
+ const durations = (data ?? []).filter((m) => m.status?.name === "DELIVERED").map((m) => (m.destination?.tx?.blockTimestamp ?? 0) - (m.source?.tx?.blockTimestamp ?? 0)).filter((d) => d > 0);
2157
+ return durations.length >= 3 ? Math.round(median(durations)) : void 0;
2158
+ });
2159
+ }
2160
+ validateBridgeParams(params, method) {
2161
+ if (!isAddress4(params.recipient)) {
2162
+ throw new StableValidationError(
2163
+ "recipient",
2164
+ params.recipient,
2165
+ `${method}: invalid recipient address "${params.recipient}"`
2166
+ );
2167
+ }
2168
+ if (!Number.isFinite(params.amount) || params.amount <= 0) {
2169
+ throw new StableValidationError(
2170
+ "amount",
2171
+ params.amount,
2172
+ `${method}: amount must be a finite positive number`
2173
+ );
2174
+ }
2175
+ if (params.fromChain === params.toChain) {
2176
+ throw new StableValidationError(
2177
+ "toChain",
2178
+ params.toChain,
2179
+ `${method}: fromChain and toChain must differ`
2180
+ );
2181
+ }
2182
+ }
2183
+ resolveChains(fromChain, toChain, fromToken, toToken) {
2184
+ const src = BRIDGE_CHAIN_CONFIGS[fromChain];
2185
+ const dst = BRIDGE_CHAIN_CONFIGS[toChain];
2186
+ if (!src) throw new StableValidationError("fromChain", fromChain, `Unsupported source chain: "${fromChain}"`);
2187
+ if (!dst) throw new StableValidationError("toChain", toChain, `Unsupported destination chain: "${toChain}"`);
2188
+ if (!src.oftAdapter) {
2189
+ throw new StableValidationError("fromChain", fromChain, `No OFT adapter configured for "${fromChain}"`);
2190
+ }
2191
+ if (fromToken.toLowerCase() !== src.usdt.toLowerCase()) {
2192
+ throw new StableValidationError(
2193
+ "fromToken",
2194
+ fromToken,
2195
+ `USDT0 bridge only moves USDT0 \u2014 fromToken must be ${src.usdt} on "${fromChain}", got "${fromToken}". Use route "lifi" to bridge other tokens.`
2196
+ );
2197
+ }
2198
+ if (toToken.toLowerCase() !== dst.usdt.toLowerCase()) {
2199
+ throw new StableValidationError(
2200
+ "toToken",
2201
+ toToken,
2202
+ `USDT0 bridge only moves USDT0 \u2014 toToken must be ${dst.usdt} on "${toChain}", got "${toToken}". Use route "lifi" to bridge other tokens.`
2203
+ );
2204
+ }
2205
+ return { src, dst };
2206
+ }
2207
+ buildSendParams(dst, amountLD, recipient) {
2208
+ return {
2209
+ dstEid: dst.lzEid,
2210
+ to: padHex(recipient, { dir: "left", size: 32 }),
2211
+ amountLD,
2212
+ minAmountLD: amountLD,
2213
+ extraOptions: buildLzOptions(),
2214
+ composeMsg: "0x",
2215
+ oftCmd: "0x"
2216
+ };
2217
+ }
2218
+ };
2219
+
1726
2220
  // src/stable.ts
2221
+ function isUsdt0Route(fromChain, toChain, fromToken, toToken) {
2222
+ const src = BRIDGE_CHAIN_CONFIGS[fromChain];
2223
+ const dst = BRIDGE_CHAIN_CONFIGS[toChain];
2224
+ return !!src && !!dst && !!src.oftAdapter && src.lzOutbound !== false && src.usdt.toLowerCase() === fromToken.toLowerCase() && dst.usdt.toLowerCase() === toToken.toLowerCase();
2225
+ }
2226
+ function selectBridge(params) {
2227
+ const route = params.route ?? "auto";
2228
+ if (route === "usdt0") return "usdt0";
2229
+ if (route === "lifi") return "lifi";
2230
+ return isUsdt0Route(
2231
+ params.fromChain,
2232
+ params.toChain,
2233
+ params.fromToken,
2234
+ params.toToken
2235
+ ) ? "usdt0" : "lifi";
2236
+ }
1727
2237
  function validateTransferParams({
1728
2238
  to,
1729
2239
  amount,
1730
2240
  token,
1731
2241
  tokenDecimals
1732
2242
  }) {
1733
- if (!isAddress4(to)) {
2243
+ if (!isAddress5(to)) {
1734
2244
  throw new StableValidationError(
1735
2245
  "to",
1736
2246
  to,
@@ -1744,7 +2254,7 @@ function validateTransferParams({
1744
2254
  "transfer: amount must be a finite positive number"
1745
2255
  );
1746
2256
  }
1747
- if (token !== void 0 && !isAddress4(token)) {
2257
+ if (token !== void 0 && !isAddress5(token)) {
1748
2258
  throw new StableValidationError(
1749
2259
  "token",
1750
2260
  token,
@@ -1764,7 +2274,7 @@ async function resolveTokenDecimals(publicClient, token, tokenDecimals) {
1764
2274
  try {
1765
2275
  return await publicClient.readContract({
1766
2276
  address: token,
1767
- abi: erc20Abi4,
2277
+ abi: erc20Abi5,
1768
2278
  functionName: "decimals"
1769
2279
  });
1770
2280
  } catch {
@@ -1791,9 +2301,9 @@ async function executeTokenTransfer(wc, publicClient, token, to, amount, tokenDe
1791
2301
  );
1792
2302
  return wc.writeContract({
1793
2303
  address: token,
1794
- abi: erc20Abi4,
2304
+ abi: erc20Abi5,
1795
2305
  functionName: "transfer",
1796
- args: [to, parseUnits4(String(amount), decimals)],
2306
+ args: [to, parseUnits5(String(amount), decimals)],
1797
2307
  account: acc,
1798
2308
  chain
1799
2309
  });
@@ -1814,6 +2324,7 @@ function createStable({
1814
2324
  );
1815
2325
  const signerConfig = { account, transport, walletClient };
1816
2326
  const lifi = new LiFiService({ ...signerConfig, rpc, chainId: chain.id });
2327
+ const lz = new LayerZeroService(signerConfig);
1817
2328
  const merkl = new MerklService({ ...signerConfig, rpc, chainId: chain.id, merklApiBase });
1818
2329
  const morpho = earnConfig ? new MorphoService({
1819
2330
  ...signerConfig,
@@ -1821,6 +2332,12 @@ function createStable({
1821
2332
  chainId: chain.id,
1822
2333
  vault: earnConfig.vault
1823
2334
  }) : null;
2335
+ const erc3009Client = walletClient ? walletClient : account ? createWalletClient5({
2336
+ account,
2337
+ chain,
2338
+ transport: transport ?? http6(rpc)
2339
+ }) : createPublicClient6({ chain, transport: http6(rpc) });
2340
+ const authorization = erc3009Actions(erc3009Client);
1824
2341
  function requireEarn() {
1825
2342
  if (!morpho) {
1826
2343
  throw new StableValidationError(
@@ -1846,10 +2363,10 @@ function createStable({
1846
2363
  validateTransferParams(params);
1847
2364
  const { from, to, amount, token, tokenDecimals } = params;
1848
2365
  const acc = walletClient?.account ?? account ?? from;
1849
- const wc = walletClient ?? createWalletClient4({
2366
+ const wc = walletClient ?? createWalletClient5({
1850
2367
  account: acc,
1851
2368
  chain,
1852
- transport: transport ?? http5(rpc)
2369
+ transport: transport ?? http6(rpc)
1853
2370
  });
1854
2371
  try {
1855
2372
  await wc.switchChain({ id: chain.id });
@@ -1861,9 +2378,9 @@ function createStable({
1861
2378
  cause: err
1862
2379
  });
1863
2380
  }
1864
- const publicClient = createPublicClient5({
2381
+ const publicClient = createPublicClient6({
1865
2382
  chain,
1866
- transport: http5(rpc)
2383
+ transport: http6(rpc)
1867
2384
  });
1868
2385
  const hash = token !== void 0 ? await executeTokenTransfer(
1869
2386
  wc,
@@ -1881,7 +2398,7 @@ function createStable({
1881
2398
  account: acc,
1882
2399
  chain,
1883
2400
  to,
1884
- value: parseUnits4(String(amount), 18)
2401
+ value: parseUnits5(String(amount), 18)
1885
2402
  })
1886
2403
  );
1887
2404
  const receipt = await publicClient.waitForTransactionReceipt({ hash });
@@ -1897,10 +2414,13 @@ function createStable({
1897
2414
  }
1898
2415
  return {
1899
2416
  transfer,
1900
- quoteBridge: (params) => lifi.quoteBridge(params),
1901
- bridge: (params) => lifi.bridge(params),
2417
+ quoteBridge: (params) => selectBridge(params) === "usdt0" ? lz.quoteBridge(params) : lifi.quoteBridge(params),
2418
+ bridge: (params) => selectBridge(params) === "usdt0" ? lz.bridge(params) : lifi.bridge(params),
2419
+ estimateBridgeTime: (params) => lz.estimateBridgeTime(params),
2420
+ trackBridge: (srcTxHash) => lz.trackBridge(srcTxHash),
1902
2421
  quoteSwap: (params) => lifi.quoteSwap(params),
1903
2422
  swap: (params) => lifi.swap(params),
2423
+ authorization,
1904
2424
  earn: {
1905
2425
  deposit: (params) => requireEarn().deposit(params),
1906
2426
  bulkDeposit: (items, options) => requireEarn().bulkDeposit(items, options),
@@ -1936,13 +2456,11 @@ function createStableReader(config) {
1936
2456
  }
1937
2457
  };
1938
2458
  }
1939
-
1940
- // src/constants.ts
1941
- var STABLE_USDT_ADDRESS = "0x779ded0c9e1022225f8e0630b35a9b54be713736";
1942
- var STABLE_VAULT_ADDRESS = "0xb7Df8db22A5DBBFA9ebeb94b3910aec6a4f05c08";
1943
2459
  export {
2460
+ BRIDGE_CHAIN_CONFIGS,
1944
2461
  CHAIN_CONFIGS,
1945
2462
  Chain,
2463
+ ERC3009_ABI,
1946
2464
  Network,
1947
2465
  STABLE_USDT_ADDRESS,
1948
2466
  STABLE_VAULT_ADDRESS,
@@ -1952,6 +2470,17 @@ export {
1952
2470
  StableQuoteError,
1953
2471
  StableTransactionError,
1954
2472
  StableValidationError,
2473
+ cancelAuthorization,
2474
+ clearBridgeEstimateCache,
1955
2475
  createStable,
1956
- createStableReader
2476
+ createStableReader,
2477
+ erc3009Actions,
2478
+ getAuthorizationState,
2479
+ getAuthorizationUsage,
2480
+ prepareTransferWithAuthorization,
2481
+ receiveWithAuthorization,
2482
+ signReceiveAuthorization,
2483
+ signTransferAuthorization,
2484
+ transferWithAuthorization,
2485
+ verifyAuthorization
1957
2486
  };