@stablechain/sdk 1.0.4 → 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/README.md +757 -0
- package/dist/chunk-QQ26UI3F.cjs +665 -0
- package/dist/chunk-VS6GBRDF.js +665 -0
- package/dist/erc3009.cjs +26 -0
- package/dist/erc3009.d.cts +429 -0
- package/dist/erc3009.d.ts +429 -0
- package/dist/erc3009.js +26 -0
- package/dist/index.cjs +451 -221
- package/dist/index.d.cts +82 -4
- package/dist/index.d.ts +82 -4
- package/dist/index.js +346 -116
- package/package.json +6 -1
package/dist/index.js
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
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
25
|
http as http6,
|
|
@@ -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) {
|
|
@@ -807,19 +750,27 @@ import {
|
|
|
807
750
|
import { stable as stableMainnet, stableTestnet } from "viem/chains";
|
|
808
751
|
|
|
809
752
|
// src/types.ts
|
|
810
|
-
var Chain = /* @__PURE__ */ ((
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
753
|
+
var Chain = /* @__PURE__ */ ((Chain2) => {
|
|
754
|
+
Chain2["Sepolia"] = "sepolia";
|
|
755
|
+
Chain2["StableTestnet"] = "stable-testnet";
|
|
756
|
+
Chain2["Stable"] = "stable";
|
|
757
|
+
Chain2["Ethereum"] = "ethereum";
|
|
758
|
+
Chain2["Arbitrum"] = "arbitrum";
|
|
759
|
+
Chain2["Ink"] = "ink";
|
|
760
|
+
Chain2["Bera"] = "bera";
|
|
761
|
+
Chain2["MegaETH"] = "megaeth";
|
|
762
|
+
Chain2["Base"] = "base";
|
|
763
|
+
Chain2["BSC"] = "bsc";
|
|
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";
|
|
773
|
+
return Chain2;
|
|
823
774
|
})(Chain || {});
|
|
824
775
|
var Network = /* @__PURE__ */ ((Network2) => {
|
|
825
776
|
Network2["Mainnet"] = "mainnet";
|
|
@@ -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:
|
|
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
|
-
|
|
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: "
|
|
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://
|
|
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
|
|
|
@@ -1738,16 +1740,24 @@ import {
|
|
|
1738
1740
|
var C = CHAIN_CONFIGS;
|
|
1739
1741
|
var BRIDGE_CHAIN_CONFIGS = {
|
|
1740
1742
|
// ─── Testnet ───────────────────────────────────────────────────────────────
|
|
1741
|
-
["sepolia" /* Sepolia */]: { ...C["sepolia" /* Sepolia */], lzEid: 40161, oftAdapter: "0xc099cD946d5efCC35A99D64E808c1430cEf08126" },
|
|
1742
|
-
["stable-testnet" /* StableTestnet */]: { ...C["stable-testnet" /* StableTestnet */], lzEid: 40374, oftAdapter: "0x779Ded0c9e1022225f8E0630b35a9b54bE713736" },
|
|
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 },
|
|
1743
1745
|
// ─── Mainnet ──────────────────────────────────────────────────────────────
|
|
1744
|
-
["stable" /* Stable */]: { ...C["stable" /* Stable */], lzEid: 30396, oftAdapter: "0xedaba024be4d87974d5aB11C6Dd586963CcCB027" },
|
|
1745
|
-
["ethereum" /* Ethereum */]: { ...C["ethereum" /* Ethereum */], lzEid: 30101, oftAdapter: "0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee" },
|
|
1746
|
-
["arbitrum" /* Arbitrum */]: { ...C["arbitrum" /* Arbitrum */], lzEid: 30110, oftAdapter: "0x14E4A1B13bf7F943c8ff7C51fb60FA964A298D92" },
|
|
1747
|
-
["ink" /* Ink */]: { ...C["ink" /* Ink */], lzEid: 30339, oftAdapter: "0x1cB6De532588fCA4a21B7209DE7C456AF8434A65" },
|
|
1748
|
-
["bera" /* Bera */]: { ...C["bera" /* Bera */], lzEid: 30362, oftAdapter: "0x3Dc96399109df5ceb2C226664A086140bD0379cB" },
|
|
1749
|
-
["megaeth" /* MegaETH */]: { ...C["megaeth" /* MegaETH */], lzEid: 30398, oftAdapter: "0x9151434b16b9763660705744891fa906f660ecc5" },
|
|
1750
|
-
["hyperevm" /* HyperEVM */]: { ...C["hyperevm" /* HyperEVM */], lzEid: 30367, oftAdapter: "0x904861a24F30EC96ea7CFC3bE9EA4B476d237e98" }
|
|
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 }
|
|
1751
1761
|
};
|
|
1752
1762
|
var OFT_ADAPTER_ABI = [
|
|
1753
1763
|
{
|
|
@@ -1812,6 +1822,80 @@ var OFT_ADAPTER_ABI = [
|
|
|
1812
1822
|
stateMutability: "payable"
|
|
1813
1823
|
}
|
|
1814
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
|
+
}
|
|
1815
1899
|
function buildLzOptions(gasLimit = 0n) {
|
|
1816
1900
|
return concat([
|
|
1817
1901
|
numberToHex(3, { size: 2 }),
|
|
@@ -1830,9 +1914,20 @@ var LayerZeroService = class {
|
|
|
1830
1914
|
constructor(config) {
|
|
1831
1915
|
this.config = config;
|
|
1832
1916
|
}
|
|
1833
|
-
async quoteBridge(
|
|
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) {
|
|
1834
1929
|
const recipientAddr = recipient ?? "0x0000000000000000000000000000000000000000";
|
|
1835
|
-
this.validateBridgeParams({ fromChain, toChain, amount, recipient: recipientAddr },
|
|
1930
|
+
this.validateBridgeParams({ fromChain, toChain, amount, recipient: recipientAddr }, method);
|
|
1836
1931
|
const { src, dst } = this.resolveChains(fromChain, toChain, fromToken, toToken);
|
|
1837
1932
|
const oftAdapter = src.oftAdapter;
|
|
1838
1933
|
const amountLD = parseUnits4(String(amount), src.decimals);
|
|
@@ -1846,11 +1941,11 @@ var LayerZeroService = class {
|
|
|
1846
1941
|
functionName: "quoteSend",
|
|
1847
1942
|
args: [this.buildSendParams(dst, amountLD, recipientAddr), false]
|
|
1848
1943
|
});
|
|
1849
|
-
return { nativeFee, lzTokenFee
|
|
1944
|
+
return { nativeFee, lzTokenFee };
|
|
1850
1945
|
} catch (err) {
|
|
1851
1946
|
throw new StableQuoteError({
|
|
1852
1947
|
provider: "layerzero",
|
|
1853
|
-
shortMessage:
|
|
1948
|
+
shortMessage: `${method}: quoteSend call failed`,
|
|
1854
1949
|
cause: err
|
|
1855
1950
|
});
|
|
1856
1951
|
}
|
|
@@ -1887,8 +1982,7 @@ var LayerZeroService = class {
|
|
|
1887
1982
|
}
|
|
1888
1983
|
const amountLD = parseUnits4(String(amount), src.decimals);
|
|
1889
1984
|
const sendParams = this.buildSendParams(dst, amountLD, recipientAddr);
|
|
1890
|
-
const
|
|
1891
|
-
const { nativeFee = 0n, lzTokenFee = 0n } = lzFee;
|
|
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");
|
|
1892
1986
|
const allowance = await pc.readContract({
|
|
1893
1987
|
address: src.usdt,
|
|
1894
1988
|
abi: erc20Abi4,
|
|
@@ -1944,6 +2038,125 @@ var LayerZeroService = class {
|
|
|
1944
2038
|
});
|
|
1945
2039
|
}
|
|
1946
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
|
+
}
|
|
1947
2160
|
validateBridgeParams(params, method) {
|
|
1948
2161
|
if (!isAddress4(params.recipient)) {
|
|
1949
2162
|
throw new StableValidationError(
|
|
@@ -2119,6 +2332,12 @@ function createStable({
|
|
|
2119
2332
|
chainId: chain.id,
|
|
2120
2333
|
vault: earnConfig.vault
|
|
2121
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);
|
|
2122
2341
|
function requireEarn() {
|
|
2123
2342
|
if (!morpho) {
|
|
2124
2343
|
throw new StableValidationError(
|
|
@@ -2197,8 +2416,11 @@ function createStable({
|
|
|
2197
2416
|
transfer,
|
|
2198
2417
|
quoteBridge: (params) => selectBridge(params) === "usdt0" ? lz.quoteBridge(params) : lifi.quoteBridge(params),
|
|
2199
2418
|
bridge: (params) => selectBridge(params) === "usdt0" ? lz.bridge(params) : lifi.bridge(params),
|
|
2419
|
+
estimateBridgeTime: (params) => lz.estimateBridgeTime(params),
|
|
2420
|
+
trackBridge: (srcTxHash) => lz.trackBridge(srcTxHash),
|
|
2200
2421
|
quoteSwap: (params) => lifi.quoteSwap(params),
|
|
2201
2422
|
swap: (params) => lifi.swap(params),
|
|
2423
|
+
authorization,
|
|
2202
2424
|
earn: {
|
|
2203
2425
|
deposit: (params) => requireEarn().deposit(params),
|
|
2204
2426
|
bulkDeposit: (items, options) => requireEarn().bulkDeposit(items, options),
|
|
@@ -2234,14 +2456,11 @@ function createStableReader(config) {
|
|
|
2234
2456
|
}
|
|
2235
2457
|
};
|
|
2236
2458
|
}
|
|
2237
|
-
|
|
2238
|
-
// src/constants.ts
|
|
2239
|
-
var STABLE_USDT_ADDRESS = "0x779ded0c9e1022225f8e0630b35a9b54be713736";
|
|
2240
|
-
var STABLE_VAULT_ADDRESS = "0xb7Df8db22A5DBBFA9ebeb94b3910aec6a4f05c08";
|
|
2241
2459
|
export {
|
|
2242
2460
|
BRIDGE_CHAIN_CONFIGS,
|
|
2243
2461
|
CHAIN_CONFIGS,
|
|
2244
2462
|
Chain,
|
|
2463
|
+
ERC3009_ABI,
|
|
2245
2464
|
Network,
|
|
2246
2465
|
STABLE_USDT_ADDRESS,
|
|
2247
2466
|
STABLE_VAULT_ADDRESS,
|
|
@@ -2251,6 +2470,17 @@ export {
|
|
|
2251
2470
|
StableQuoteError,
|
|
2252
2471
|
StableTransactionError,
|
|
2253
2472
|
StableValidationError,
|
|
2473
|
+
cancelAuthorization,
|
|
2474
|
+
clearBridgeEstimateCache,
|
|
2254
2475
|
createStable,
|
|
2255
|
-
createStableReader
|
|
2476
|
+
createStableReader,
|
|
2477
|
+
erc3009Actions,
|
|
2478
|
+
getAuthorizationState,
|
|
2479
|
+
getAuthorizationUsage,
|
|
2480
|
+
prepareTransferWithAuthorization,
|
|
2481
|
+
receiveWithAuthorization,
|
|
2482
|
+
signReceiveAuthorization,
|
|
2483
|
+
signTransferAuthorization,
|
|
2484
|
+
transferWithAuthorization,
|
|
2485
|
+
verifyAuthorization
|
|
2256
2486
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stablechain/sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Stable SDK",
|
|
5
5
|
"keywords": ["stable", "usdt", "gasless", "web3", "yield"],
|
|
6
6
|
"license": "MIT",
|
|
@@ -22,6 +22,11 @@
|
|
|
22
22
|
"types": "./dist/index.d.ts",
|
|
23
23
|
"import": "./dist/index.js",
|
|
24
24
|
"default": "./dist/index.cjs"
|
|
25
|
+
},
|
|
26
|
+
"./erc3009": {
|
|
27
|
+
"types": "./dist/erc3009.d.ts",
|
|
28
|
+
"import": "./dist/erc3009.js",
|
|
29
|
+
"default": "./dist/erc3009.cjs"
|
|
25
30
|
}
|
|
26
31
|
},
|
|
27
32
|
"scripts": {
|