@stablechain/sdk 1.0.4 → 1.0.6
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 +878 -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 +824 -326
- package/dist/index.d.cts +223 -14
- package/dist/index.d.ts +223 -14
- package/dist/index.js +724 -226
- 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
|
|
|
@@ -1733,40 +1735,83 @@ import {
|
|
|
1733
1735
|
isAddress as isAddress4,
|
|
1734
1736
|
numberToHex,
|
|
1735
1737
|
padHex,
|
|
1738
|
+
formatUnits as formatUnits5,
|
|
1736
1739
|
parseUnits as parseUnits4
|
|
1737
1740
|
} from "viem";
|
|
1738
1741
|
var C = CHAIN_CONFIGS;
|
|
1742
|
+
var XAUT0_DEPLOYMENTS = {
|
|
1743
|
+
["ethereum" /* Ethereum */]: { address: "0x68749665FF8D2d112Fa859AA293F07A622782F38", decimals: 6, oftAdapter: "0xb9c2321BB7D0Db468f570D10A424d1Cc8EFd696C", approvalRequired: true },
|
|
1744
|
+
["stable" /* Stable */]: { address: "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb", decimals: 6, oftAdapter: "0xD8479f87686ed263D00Ca7505F86327dbeD4171A", approvalRequired: false },
|
|
1745
|
+
["arbitrum" /* Arbitrum */]: { address: "0x40461291347e1eCbb09499F3371D3f17f10d7159", decimals: 6, oftAdapter: "0xf40542a7B66AD7C68C459EE3679635D2fDB6dF39", approvalRequired: false },
|
|
1746
|
+
["ink" /* Ink */]: { address: "0xF50258D3c1dd88946C567920B986A12e65b50dAc", decimals: 6, oftAdapter: "0xA1bE1572B4beef24f812EfDc58bdc41D56a0dAB2", approvalRequired: false },
|
|
1747
|
+
["hyperevm" /* HyperEVM */]: { address: "0xf4D9235269a96aaDaFc9aDAe454a0618eBE37949", decimals: 6, oftAdapter: "0x4E41cfc3F3B19E29E323D2c36F8f202a1e151dAF", approvalRequired: false },
|
|
1748
|
+
["polygon" /* Polygon */]: { address: "0xF1815bd50389c46847f0Bda824eC8da914045D14", decimals: 6, oftAdapter: "0x5421Cf4288d8007D3c43AC4246eaFCe5b049e352", approvalRequired: false },
|
|
1749
|
+
["plasma" /* Plasma */]: { address: "0x1B64B9025EEbb9A6239575dF9Ea4b9Ac46D4d193", decimals: 6, oftAdapter: "0x63aB93cBC9d4ecD9c4947b1A38F458147C08E6F7", approvalRequired: false },
|
|
1750
|
+
["bsc" /* BSC */]: { address: "0x21cAef8A43163Eea865baeE23b9C2E327696A3bf", decimals: 6, oftAdapter: "0x53C3A64c8942288e12813C1F8457Db45980BcfC2", approvalRequired: false }
|
|
1751
|
+
};
|
|
1752
|
+
function bridgeChain(chain, lzEid, blockTimeSec, usdt0Adapter) {
|
|
1753
|
+
const base = C[chain];
|
|
1754
|
+
const tokens = {};
|
|
1755
|
+
if (usdt0Adapter) {
|
|
1756
|
+
tokens.usdt0 = { address: base.usdt, decimals: base.decimals, oftAdapter: usdt0Adapter };
|
|
1757
|
+
}
|
|
1758
|
+
const xaut0 = XAUT0_DEPLOYMENTS[chain];
|
|
1759
|
+
if (xaut0) tokens.xaut0 = xaut0;
|
|
1760
|
+
return { ...base, lzEid, blockTimeSec, tokens, oftAdapter: usdt0Adapter };
|
|
1761
|
+
}
|
|
1739
1762
|
var BRIDGE_CHAIN_CONFIGS = {
|
|
1740
1763
|
// ─── Testnet ───────────────────────────────────────────────────────────────
|
|
1741
|
-
["sepolia" /* Sepolia */]:
|
|
1742
|
-
["stable-testnet" /* StableTestnet */]:
|
|
1764
|
+
["sepolia" /* Sepolia */]: bridgeChain("sepolia" /* Sepolia */, 40161, 12, "0xc099cD946d5efCC35A99D64E808c1430cEf08126"),
|
|
1765
|
+
["stable-testnet" /* StableTestnet */]: bridgeChain("stable-testnet" /* StableTestnet */, 40374, 0.7, "0x779Ded0c9e1022225f8E0630b35a9b54bE713736"),
|
|
1743
1766
|
// ─── Mainnet ──────────────────────────────────────────────────────────────
|
|
1744
|
-
["stable" /* Stable */]:
|
|
1745
|
-
["ethereum" /* Ethereum */]:
|
|
1746
|
-
["arbitrum" /* Arbitrum */]:
|
|
1747
|
-
["ink" /* Ink */]:
|
|
1748
|
-
["bera" /* Bera */]:
|
|
1749
|
-
["megaeth" /* MegaETH */]:
|
|
1750
|
-
["hyperevm" /* HyperEVM */]:
|
|
1767
|
+
["stable" /* Stable */]: bridgeChain("stable" /* Stable */, 30396, 0.7, "0xedaba024be4d87974d5aB11C6Dd586963CcCB027"),
|
|
1768
|
+
["ethereum" /* Ethereum */]: bridgeChain("ethereum" /* Ethereum */, 30101, 12, "0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee"),
|
|
1769
|
+
["arbitrum" /* Arbitrum */]: bridgeChain("arbitrum" /* Arbitrum */, 30110, 0.25, "0x14E4A1B13bf7F943c8ff7C51fb60FA964A298D92"),
|
|
1770
|
+
["ink" /* Ink */]: bridgeChain("ink" /* Ink */, 30339, 1, "0x1cB6De532588fCA4a21B7209DE7C456AF8434A65"),
|
|
1771
|
+
["bera" /* Bera */]: bridgeChain("bera" /* Bera */, 30362, 2, "0x3Dc96399109df5ceb2C226664A086140bD0379cB"),
|
|
1772
|
+
["megaeth" /* MegaETH */]: bridgeChain("megaeth" /* MegaETH */, 30398, 1, "0x9151434b16b9763660705744891fa906f660ecc5"),
|
|
1773
|
+
["hyperevm" /* HyperEVM */]: bridgeChain("hyperevm" /* HyperEVM */, 30367, 1, "0x904861a24F30EC96ea7CFC3bE9EA4B476d237e98"),
|
|
1774
|
+
["polygon" /* Polygon */]: bridgeChain("polygon" /* Polygon */, 30109, 1.5, "0x6BA10300f0DC58B7a1e4c0e41f5daBb7D7829e13"),
|
|
1775
|
+
["plasma" /* Plasma */]: bridgeChain("plasma" /* Plasma */, 30383, 1, "0x02ca37966753bDdDf11216B73B16C1dE756A7CF9"),
|
|
1776
|
+
["optimism" /* Optimism */]: bridgeChain("optimism" /* Optimism */, 30111, 2, "0xF03b4d9AC1D5d1E7c4cEf54C2A313b9fe051A0aD"),
|
|
1777
|
+
["unichain" /* Unichain */]: bridgeChain("unichain" /* Unichain */, 30320, 1, "0xc07bE8994D035631c36fb4a89C918CeFB2f03EC3"),
|
|
1778
|
+
["sei" /* Sei */]: bridgeChain("sei" /* Sei */, 30280, 0.5, "0x56Fe74A2e3b484b921c447357203431a3485CC60"),
|
|
1779
|
+
["flare" /* Flare */]: bridgeChain("flare" /* Flare */, 30295, 1.1, "0x567287d2A9829215a37e3B88843d32f9221E7588"),
|
|
1780
|
+
["xlayer" /* XLayer */]: bridgeChain("xlayer" /* XLayer */, 30274, 1, "0x94BCCa6bdfd6A61817Ab0E960bFedE4984505554"),
|
|
1781
|
+
["rootstock" /* Rootstock */]: bridgeChain("rootstock" /* Rootstock */, 30333, 31, "0x1a594d5d5d1c426281C1064B07f23F57B2716B61"),
|
|
1782
|
+
// XAUt0 only — BSC has no USDT0 deployment. Block time measured 2026-08-20.
|
|
1783
|
+
["bsc" /* BSC */]: bridgeChain("bsc" /* BSC */, 30102, 0.45)
|
|
1784
|
+
};
|
|
1785
|
+
function bridgeAssetFor(cfg, token) {
|
|
1786
|
+
const needle = token.toLowerCase();
|
|
1787
|
+
return Object.keys(cfg.tokens).find(
|
|
1788
|
+
(asset) => cfg.tokens[asset]?.address.toLowerCase() === needle
|
|
1789
|
+
);
|
|
1790
|
+
}
|
|
1791
|
+
function describeAssets(cfg) {
|
|
1792
|
+
const assets = Object.entries(cfg.tokens);
|
|
1793
|
+
if (assets.length === 0) return "no OFT assets";
|
|
1794
|
+
return assets.map(([asset, token]) => `${asset} (${token.address})`).join(" or ");
|
|
1795
|
+
}
|
|
1796
|
+
var SEND_PARAM_INPUT = {
|
|
1797
|
+
name: "_sendParam",
|
|
1798
|
+
type: "tuple",
|
|
1799
|
+
components: [
|
|
1800
|
+
{ name: "dstEid", type: "uint32" },
|
|
1801
|
+
{ name: "to", type: "bytes32" },
|
|
1802
|
+
{ name: "amountLD", type: "uint256" },
|
|
1803
|
+
{ name: "minAmountLD", type: "uint256" },
|
|
1804
|
+
{ name: "extraOptions", type: "bytes" },
|
|
1805
|
+
{ name: "composeMsg", type: "bytes" },
|
|
1806
|
+
{ name: "oftCmd", type: "bytes" }
|
|
1807
|
+
]
|
|
1751
1808
|
};
|
|
1752
1809
|
var OFT_ADAPTER_ABI = [
|
|
1753
1810
|
{
|
|
1754
1811
|
name: "quoteSend",
|
|
1755
1812
|
type: "function",
|
|
1756
1813
|
inputs: [
|
|
1757
|
-
|
|
1758
|
-
name: "_sendParam",
|
|
1759
|
-
type: "tuple",
|
|
1760
|
-
components: [
|
|
1761
|
-
{ name: "dstEid", type: "uint32" },
|
|
1762
|
-
{ name: "to", type: "bytes32" },
|
|
1763
|
-
{ name: "amountLD", type: "uint256" },
|
|
1764
|
-
{ name: "minAmountLD", type: "uint256" },
|
|
1765
|
-
{ name: "extraOptions", type: "bytes" },
|
|
1766
|
-
{ name: "composeMsg", type: "bytes" },
|
|
1767
|
-
{ name: "oftCmd", type: "bytes" }
|
|
1768
|
-
]
|
|
1769
|
-
},
|
|
1814
|
+
SEND_PARAM_INPUT,
|
|
1770
1815
|
{ name: "_payInLzToken", type: "bool" }
|
|
1771
1816
|
],
|
|
1772
1817
|
outputs: [
|
|
@@ -1782,22 +1827,48 @@ var OFT_ADAPTER_ABI = [
|
|
|
1782
1827
|
stateMutability: "view"
|
|
1783
1828
|
},
|
|
1784
1829
|
{
|
|
1785
|
-
|
|
1830
|
+
// OFT-level accounting, independent of the LayerZero messaging fee: the
|
|
1831
|
+
// pathway's accepted range, any fee the adapter charges itself, and what the
|
|
1832
|
+
// recipient actually ends up credited.
|
|
1833
|
+
name: "quoteOFT",
|
|
1786
1834
|
type: "function",
|
|
1787
1835
|
inputs: [
|
|
1836
|
+
SEND_PARAM_INPUT
|
|
1837
|
+
],
|
|
1838
|
+
outputs: [
|
|
1788
1839
|
{
|
|
1789
|
-
name: "
|
|
1840
|
+
name: "oftLimit",
|
|
1790
1841
|
type: "tuple",
|
|
1791
1842
|
components: [
|
|
1792
|
-
{ name: "dstEid", type: "uint32" },
|
|
1793
|
-
{ name: "to", type: "bytes32" },
|
|
1794
|
-
{ name: "amountLD", type: "uint256" },
|
|
1795
1843
|
{ name: "minAmountLD", type: "uint256" },
|
|
1796
|
-
{ name: "
|
|
1797
|
-
{ name: "composeMsg", type: "bytes" },
|
|
1798
|
-
{ name: "oftCmd", type: "bytes" }
|
|
1844
|
+
{ name: "maxAmountLD", type: "uint256" }
|
|
1799
1845
|
]
|
|
1800
1846
|
},
|
|
1847
|
+
{
|
|
1848
|
+
name: "oftFeeDetails",
|
|
1849
|
+
type: "tuple[]",
|
|
1850
|
+
components: [
|
|
1851
|
+
// Signed: a negative detail is a credit to the sender, not a charge.
|
|
1852
|
+
{ name: "feeAmountLD", type: "int256" },
|
|
1853
|
+
{ name: "description", type: "string" }
|
|
1854
|
+
]
|
|
1855
|
+
},
|
|
1856
|
+
{
|
|
1857
|
+
name: "oftReceipt",
|
|
1858
|
+
type: "tuple",
|
|
1859
|
+
components: [
|
|
1860
|
+
{ name: "amountSentLD", type: "uint256" },
|
|
1861
|
+
{ name: "amountReceivedLD", type: "uint256" }
|
|
1862
|
+
]
|
|
1863
|
+
}
|
|
1864
|
+
],
|
|
1865
|
+
stateMutability: "view"
|
|
1866
|
+
},
|
|
1867
|
+
{
|
|
1868
|
+
name: "send",
|
|
1869
|
+
type: "function",
|
|
1870
|
+
inputs: [
|
|
1871
|
+
SEND_PARAM_INPUT,
|
|
1801
1872
|
{
|
|
1802
1873
|
name: "_fee",
|
|
1803
1874
|
type: "tuple",
|
|
@@ -1812,51 +1883,295 @@ var OFT_ADAPTER_ABI = [
|
|
|
1812
1883
|
stateMutability: "payable"
|
|
1813
1884
|
}
|
|
1814
1885
|
];
|
|
1815
|
-
|
|
1886
|
+
var OAPP_ABI = [
|
|
1887
|
+
{ name: "endpoint", type: "function", stateMutability: "view", inputs: [], outputs: [{ type: "address" }] }
|
|
1888
|
+
];
|
|
1889
|
+
var ENDPOINT_V2_ABI = [
|
|
1890
|
+
{
|
|
1891
|
+
name: "getSendLibrary",
|
|
1892
|
+
type: "function",
|
|
1893
|
+
stateMutability: "view",
|
|
1894
|
+
inputs: [{ name: "_sender", type: "address" }, { name: "_dstEid", type: "uint32" }],
|
|
1895
|
+
outputs: [{ type: "address" }]
|
|
1896
|
+
}
|
|
1897
|
+
];
|
|
1898
|
+
var SEND_ULN_ABI = [
|
|
1899
|
+
{
|
|
1900
|
+
name: "getUlnConfig",
|
|
1901
|
+
type: "function",
|
|
1902
|
+
stateMutability: "view",
|
|
1903
|
+
inputs: [{ name: "_oapp", type: "address" }, { name: "_remoteEid", type: "uint32" }],
|
|
1904
|
+
outputs: [
|
|
1905
|
+
{
|
|
1906
|
+
name: "rtnConfig",
|
|
1907
|
+
type: "tuple",
|
|
1908
|
+
components: [
|
|
1909
|
+
{ name: "confirmations", type: "uint64" },
|
|
1910
|
+
{ name: "requiredDVNCount", type: "uint8" },
|
|
1911
|
+
{ name: "optionalDVNCount", type: "uint8" },
|
|
1912
|
+
{ name: "optionalDVNThreshold", type: "uint8" },
|
|
1913
|
+
{ name: "requiredDVNs", type: "address[]" },
|
|
1914
|
+
{ name: "optionalDVNs", type: "address[]" }
|
|
1915
|
+
]
|
|
1916
|
+
}
|
|
1917
|
+
]
|
|
1918
|
+
}
|
|
1919
|
+
];
|
|
1920
|
+
var LZ_SCAN_API = "https://scan.layerzero-api.com/v1";
|
|
1921
|
+
var LZ_SCAN_UI = "https://layerzeroscan.com";
|
|
1922
|
+
var CACHE_TTL_MS = 5 * 6e4;
|
|
1923
|
+
var cache = /* @__PURE__ */ new Map();
|
|
1924
|
+
function clearBridgeEstimateCache() {
|
|
1925
|
+
cache.clear();
|
|
1926
|
+
}
|
|
1927
|
+
async function cached(key, ttlMs, load) {
|
|
1928
|
+
const hit = cache.get(key);
|
|
1929
|
+
if (hit && Date.now() - hit.at < ttlMs) return hit.value;
|
|
1930
|
+
const value = await load();
|
|
1931
|
+
cache.set(key, { at: Date.now(), value });
|
|
1932
|
+
return value;
|
|
1933
|
+
}
|
|
1934
|
+
var ScanHttpError = class extends Error {
|
|
1935
|
+
constructor(status, path) {
|
|
1936
|
+
super(`LayerZero Scan ${status} for ${path}`);
|
|
1937
|
+
this.status = status;
|
|
1938
|
+
this.name = "ScanHttpError";
|
|
1939
|
+
}
|
|
1940
|
+
};
|
|
1941
|
+
async function scanFetch(path, timeoutMs = 8e3) {
|
|
1942
|
+
const res = await fetch(`${LZ_SCAN_API}${path}`, { signal: AbortSignal.timeout(timeoutMs) });
|
|
1943
|
+
if (!res.ok) throw new ScanHttpError(res.status, path);
|
|
1944
|
+
return await res.json();
|
|
1945
|
+
}
|
|
1946
|
+
var TERMINAL_FAILURE_STATUSES = /* @__PURE__ */ new Set([
|
|
1947
|
+
"FAILED",
|
|
1948
|
+
"BLOCKED",
|
|
1949
|
+
"PAYLOAD_STORED",
|
|
1950
|
+
"APPLICATION_BURNED",
|
|
1951
|
+
"APPLICATION_SKIPPED",
|
|
1952
|
+
"UNRESOLVABLE_COMMAND",
|
|
1953
|
+
"MALFORMED_COMMAND"
|
|
1954
|
+
]);
|
|
1955
|
+
function median(values) {
|
|
1956
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
1957
|
+
const mid = Math.floor(sorted.length / 2);
|
|
1958
|
+
return sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
|
|
1959
|
+
}
|
|
1960
|
+
var WORKER_EXECUTOR = 1;
|
|
1961
|
+
var OPTION_LZRECEIVE = 1;
|
|
1962
|
+
var OPTION_NATIVE_DROP = 2;
|
|
1963
|
+
function lzOption(optionType, payload) {
|
|
1964
|
+
const payloadBytes = (payload.length - 2) / 2;
|
|
1965
|
+
return concat([
|
|
1966
|
+
numberToHex(WORKER_EXECUTOR, { size: 1 }),
|
|
1967
|
+
numberToHex(1 + payloadBytes, { size: 2 }),
|
|
1968
|
+
numberToHex(optionType, { size: 1 }),
|
|
1969
|
+
payload
|
|
1970
|
+
]);
|
|
1971
|
+
}
|
|
1972
|
+
function buildLzOptions(gasLimit = 0n, nativeDrop = 0n, dropReceiver) {
|
|
1973
|
+
const options = [
|
|
1974
|
+
lzOption(OPTION_LZRECEIVE, numberToHex(gasLimit, { size: 16 }))
|
|
1975
|
+
];
|
|
1976
|
+
if (nativeDrop > 0n) {
|
|
1977
|
+
if (!dropReceiver) {
|
|
1978
|
+
throw new StableValidationError(
|
|
1979
|
+
"recipient",
|
|
1980
|
+
dropReceiver,
|
|
1981
|
+
"buildLzOptions: a native drop needs a receiver address"
|
|
1982
|
+
);
|
|
1983
|
+
}
|
|
1984
|
+
options.push(
|
|
1985
|
+
lzOption(
|
|
1986
|
+
OPTION_NATIVE_DROP,
|
|
1987
|
+
concat([
|
|
1988
|
+
numberToHex(nativeDrop, { size: 16 }),
|
|
1989
|
+
padHex(dropReceiver, { dir: "left", size: 32 })
|
|
1990
|
+
])
|
|
1991
|
+
)
|
|
1992
|
+
);
|
|
1993
|
+
}
|
|
1816
1994
|
return concat([
|
|
1817
1995
|
numberToHex(3, { size: 2 }),
|
|
1818
1996
|
// TYPE_3 header
|
|
1819
|
-
|
|
1820
|
-
// Executor worker ID
|
|
1821
|
-
numberToHex(17, { size: 2 }),
|
|
1822
|
-
// option payload length = 17 bytes
|
|
1823
|
-
numberToHex(1, { size: 1 }),
|
|
1824
|
-
// LzReceive option type
|
|
1825
|
-
numberToHex(gasLimit, { size: 16 })
|
|
1826
|
-
// gas limit as uint128
|
|
1997
|
+
...options
|
|
1827
1998
|
]);
|
|
1828
1999
|
}
|
|
2000
|
+
var FALLBACK_SEND_GAS = 400000n;
|
|
2001
|
+
function usableQuote(quote, amountLD) {
|
|
2002
|
+
if (!quote?.oft) return quote;
|
|
2003
|
+
return quote.oft.amountSentLD <= amountLD ? quote : void 0;
|
|
2004
|
+
}
|
|
2005
|
+
var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
1829
2006
|
var LayerZeroService = class {
|
|
1830
2007
|
constructor(config) {
|
|
1831
2008
|
this.config = config;
|
|
1832
2009
|
}
|
|
1833
|
-
async quoteBridge(
|
|
1834
|
-
const
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
2010
|
+
async quoteBridge(params) {
|
|
2011
|
+
const plan = this.plan(params, "quoteBridge");
|
|
2012
|
+
const [{ fee, oft }, estimate] = await Promise.all([
|
|
2013
|
+
this.quotePathway(plan, "quoteBridge"),
|
|
2014
|
+
this.estimateBridgeTime(params).catch(() => void 0)
|
|
2015
|
+
]);
|
|
2016
|
+
const srcGas = await this.estimateSendGas(plan, fee, oft);
|
|
2017
|
+
return {
|
|
2018
|
+
...fee,
|
|
2019
|
+
oft,
|
|
2020
|
+
// What the recipient is actually credited: input minus any OFT fee and
|
|
2021
|
+
// minus dust. Falls back to the input when quoteOFT could not be read.
|
|
2022
|
+
toAmount: oft?.amountReceived ?? params.amount,
|
|
2023
|
+
srcGas,
|
|
2024
|
+
totalCostWei: srcGas ? srcGas.costWei + fee.nativeFee : void 0,
|
|
2025
|
+
estimate
|
|
2026
|
+
};
|
|
2027
|
+
}
|
|
2028
|
+
/**
|
|
2029
|
+
* The LayerZero messaging fee for a send, plus the OFT's own accounting.
|
|
2030
|
+
*
|
|
2031
|
+
* The fee is required — a send cannot be built without it. The OFT accounting is
|
|
2032
|
+
* best-effort: an adapter that does not answer `quoteOFT` costs us the fee
|
|
2033
|
+
* breakdown and the exact output amount, not the whole quote.
|
|
2034
|
+
*/
|
|
2035
|
+
async quotePathway(plan, method) {
|
|
2036
|
+
const sendParam = this.buildSendParams(plan, 0n);
|
|
2037
|
+
const [msgFee, oft] = await Promise.all([
|
|
2038
|
+
plan.pc.readContract({
|
|
2039
|
+
address: plan.oftAdapter,
|
|
1845
2040
|
abi: OFT_ADAPTER_ABI,
|
|
1846
2041
|
functionName: "quoteSend",
|
|
1847
|
-
args: [
|
|
2042
|
+
args: [sendParam, false]
|
|
2043
|
+
}).catch((err) => {
|
|
2044
|
+
throw new StableQuoteError({
|
|
2045
|
+
provider: "layerzero",
|
|
2046
|
+
shortMessage: plan.nativeDrop > 0n ? `${method}: quoteSend call failed \u2014 a dstNativeDrop over the pathway's executor nativeCap is the usual cause` : `${method}: quoteSend call failed`,
|
|
2047
|
+
cause: err
|
|
2048
|
+
});
|
|
2049
|
+
}),
|
|
2050
|
+
this.readOftQuote(plan, sendParam)
|
|
2051
|
+
]);
|
|
2052
|
+
const { nativeFee, lzTokenFee } = msgFee;
|
|
2053
|
+
return { fee: { nativeFee, lzTokenFee }, oft };
|
|
2054
|
+
}
|
|
2055
|
+
/**
|
|
2056
|
+
* The adapter's own view of a send: accepted range, its fees, and what lands on
|
|
2057
|
+
* the far side. Resolves to `undefined` rather than throwing — see `quoteSend`.
|
|
2058
|
+
*/
|
|
2059
|
+
async readOftQuote(plan, sendParam) {
|
|
2060
|
+
try {
|
|
2061
|
+
const [limit, feeDetails, receipt] = await plan.pc.readContract({
|
|
2062
|
+
address: plan.oftAdapter,
|
|
2063
|
+
abi: OFT_ADAPTER_ABI,
|
|
2064
|
+
functionName: "quoteOFT",
|
|
2065
|
+
args: [sendParam]
|
|
2066
|
+
});
|
|
2067
|
+
const decimals = plan.srcToken.decimals;
|
|
2068
|
+
return {
|
|
2069
|
+
amountSentLD: receipt.amountSentLD,
|
|
2070
|
+
amountReceivedLD: receipt.amountReceivedLD,
|
|
2071
|
+
amountReceived: Number(formatUnits5(receipt.amountReceivedLD, decimals)),
|
|
2072
|
+
fees: feeDetails.map((detail) => ({
|
|
2073
|
+
amountLD: detail.feeAmountLD,
|
|
2074
|
+
amount: Number(formatUnits5(detail.feeAmountLD, decimals)),
|
|
2075
|
+
description: detail.description
|
|
2076
|
+
})),
|
|
2077
|
+
minAmountLD: limit.minAmountLD,
|
|
2078
|
+
maxAmountLD: limit.maxAmountLD
|
|
2079
|
+
};
|
|
2080
|
+
} catch {
|
|
2081
|
+
return void 0;
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
/**
|
|
2085
|
+
* Source-chain cost of the `send` transaction.
|
|
2086
|
+
*
|
|
2087
|
+
* Simulating the real call is the only honest answer, and it needs a sender with
|
|
2088
|
+
* the allowance and the native balance already in place — which a quote taken
|
|
2089
|
+
* before approving does not have. When the simulation reverts, fall back to a
|
|
2090
|
+
* measured constant so the caller still has a cost to show, flagged as such.
|
|
2091
|
+
*/
|
|
2092
|
+
async estimateSendGas(plan, fee, oft) {
|
|
2093
|
+
const gasPriceWei = await this.gasPrice(plan);
|
|
2094
|
+
if (gasPriceWei === void 0) return void 0;
|
|
2095
|
+
let gas = FALLBACK_SEND_GAS;
|
|
2096
|
+
let source = "fallback";
|
|
2097
|
+
const account = this.signerAddress();
|
|
2098
|
+
if (account) {
|
|
2099
|
+
try {
|
|
2100
|
+
gas = await plan.pc.estimateContractGas({
|
|
2101
|
+
address: plan.oftAdapter,
|
|
2102
|
+
abi: OFT_ADAPTER_ABI,
|
|
2103
|
+
functionName: "send",
|
|
2104
|
+
args: [
|
|
2105
|
+
this.buildSendParams(plan, oft?.amountReceivedLD ?? plan.amountLD),
|
|
2106
|
+
fee,
|
|
2107
|
+
account
|
|
2108
|
+
],
|
|
2109
|
+
account,
|
|
2110
|
+
value: fee.nativeFee
|
|
2111
|
+
});
|
|
2112
|
+
source = "simulated";
|
|
2113
|
+
} catch {
|
|
2114
|
+
}
|
|
2115
|
+
}
|
|
2116
|
+
return { gas, gasPriceWei, costWei: gas * gasPriceWei, source };
|
|
2117
|
+
}
|
|
2118
|
+
/** Price per gas unit on the source chain, or undefined if the RPC won't say. */
|
|
2119
|
+
async gasPrice(plan) {
|
|
2120
|
+
try {
|
|
2121
|
+
const fees = await plan.pc.estimateFeesPerGas();
|
|
2122
|
+
const price = fees.maxFeePerGas ?? fees.gasPrice;
|
|
2123
|
+
if (price !== void 0) return price;
|
|
2124
|
+
} catch {
|
|
2125
|
+
}
|
|
2126
|
+
try {
|
|
2127
|
+
return await plan.pc.getGasPrice();
|
|
2128
|
+
} catch {
|
|
2129
|
+
return void 0;
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
/**
|
|
2133
|
+
* Top the adapter's allowance up to `amountLD` when it is short. Only the
|
|
2134
|
+
* lockbox chain reaches here — remote deployments burn from the caller.
|
|
2135
|
+
*/
|
|
2136
|
+
async ensureAllowance(plan, wc, owner, amountLD) {
|
|
2137
|
+
const { src, asset, srcToken, oftAdapter, pc, chain: srcChain } = plan;
|
|
2138
|
+
const allowance = await pc.readContract({
|
|
2139
|
+
address: srcToken.address,
|
|
2140
|
+
abi: erc20Abi4,
|
|
2141
|
+
functionName: "allowance",
|
|
2142
|
+
args: [owner, oftAdapter]
|
|
2143
|
+
});
|
|
2144
|
+
if (allowance >= amountLD) return;
|
|
2145
|
+
let approveHash;
|
|
2146
|
+
try {
|
|
2147
|
+
approveHash = await wc.writeContract({
|
|
2148
|
+
address: srcToken.address,
|
|
2149
|
+
abi: erc20Abi4,
|
|
2150
|
+
functionName: "approve",
|
|
2151
|
+
args: [oftAdapter, amountLD],
|
|
2152
|
+
account: wc.account,
|
|
2153
|
+
chain: srcChain
|
|
1848
2154
|
});
|
|
1849
|
-
return { nativeFee, lzTokenFee, toAmount: amount };
|
|
1850
2155
|
} catch (err) {
|
|
1851
|
-
throw new
|
|
1852
|
-
|
|
1853
|
-
shortMessage:
|
|
2156
|
+
throw new StableTransactionError({
|
|
2157
|
+
phase: "approve",
|
|
2158
|
+
shortMessage: `bridge: ${asset} approve failed`,
|
|
2159
|
+
chainId: src.id,
|
|
1854
2160
|
cause: err
|
|
1855
2161
|
});
|
|
1856
2162
|
}
|
|
2163
|
+
const approveReceipt = await pc.waitForTransactionReceipt({ hash: approveHash });
|
|
2164
|
+
if (approveReceipt.status === "reverted") {
|
|
2165
|
+
throw new StableTransactionError({
|
|
2166
|
+
phase: "approve",
|
|
2167
|
+
shortMessage: `bridge: ${asset} approve reverted on-chain`,
|
|
2168
|
+
txHash: approveReceipt.transactionHash,
|
|
2169
|
+
chainId: src.id
|
|
2170
|
+
});
|
|
2171
|
+
}
|
|
1857
2172
|
}
|
|
1858
|
-
async bridge(
|
|
1859
|
-
const from = this.
|
|
2173
|
+
async bridge(params) {
|
|
2174
|
+
const from = this.signerAddress();
|
|
1860
2175
|
if (!from) {
|
|
1861
2176
|
throw new StableValidationError(
|
|
1862
2177
|
"signer",
|
|
@@ -1864,17 +2179,13 @@ var LayerZeroService = class {
|
|
|
1864
2179
|
"bridge: requires a signer \u2014 set walletClient or account in StableConfig"
|
|
1865
2180
|
);
|
|
1866
2181
|
}
|
|
1867
|
-
const
|
|
1868
|
-
|
|
1869
|
-
const { src, dst } = this.resolveChains(fromChain, toChain, fromToken, toToken);
|
|
1870
|
-
const oftAdapter = src.oftAdapter;
|
|
1871
|
-
const srcChain = makeChain(src.id, src.rpc);
|
|
2182
|
+
const plan = this.plan({ ...params, recipient: params.recipient ?? from }, "bridge");
|
|
2183
|
+
const { src, srcToken, oftAdapter, amountLD, chain: srcChain } = plan;
|
|
1872
2184
|
const wc = this.config.walletClient ?? createWalletClient4({
|
|
1873
2185
|
account: this.config.account,
|
|
1874
2186
|
chain: srcChain,
|
|
1875
2187
|
transport: this.config.transport ?? http5(src.rpc)
|
|
1876
2188
|
});
|
|
1877
|
-
const pc = createPublicClient5({ chain: srcChain, transport: http5(src.rpc) });
|
|
1878
2189
|
try {
|
|
1879
2190
|
await wc.switchChain({ id: src.id });
|
|
1880
2191
|
} catch (err) {
|
|
@@ -1885,44 +2196,11 @@ var LayerZeroService = class {
|
|
|
1885
2196
|
cause: err
|
|
1886
2197
|
});
|
|
1887
2198
|
}
|
|
1888
|
-
const
|
|
1889
|
-
const
|
|
1890
|
-
const
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
address: src.usdt,
|
|
1894
|
-
abi: erc20Abi4,
|
|
1895
|
-
functionName: "allowance",
|
|
1896
|
-
args: [from, oftAdapter]
|
|
1897
|
-
});
|
|
1898
|
-
if (allowance < amountLD) {
|
|
1899
|
-
let approveHash;
|
|
1900
|
-
try {
|
|
1901
|
-
approveHash = await wc.writeContract({
|
|
1902
|
-
address: src.usdt,
|
|
1903
|
-
abi: erc20Abi4,
|
|
1904
|
-
functionName: "approve",
|
|
1905
|
-
args: [oftAdapter, amountLD],
|
|
1906
|
-
account: wc.account,
|
|
1907
|
-
chain: srcChain
|
|
1908
|
-
});
|
|
1909
|
-
} catch (err) {
|
|
1910
|
-
throw new StableTransactionError({
|
|
1911
|
-
phase: "approve",
|
|
1912
|
-
shortMessage: "bridge: USDT0 approve failed",
|
|
1913
|
-
chainId: src.id,
|
|
1914
|
-
cause: err
|
|
1915
|
-
});
|
|
1916
|
-
}
|
|
1917
|
-
const approveReceipt = await pc.waitForTransactionReceipt({ hash: approveHash });
|
|
1918
|
-
if (approveReceipt.status === "reverted") {
|
|
1919
|
-
throw new StableTransactionError({
|
|
1920
|
-
phase: "approve",
|
|
1921
|
-
shortMessage: "bridge: USDT0 approve reverted on-chain",
|
|
1922
|
-
txHash: approveReceipt.transactionHash,
|
|
1923
|
-
chainId: src.id
|
|
1924
|
-
});
|
|
1925
|
-
}
|
|
2199
|
+
const quote = usableQuote(params.quote, amountLD);
|
|
2200
|
+
const { nativeFee, lzTokenFee, oft } = quote?.nativeFee !== void 0 && quote?.lzTokenFee !== void 0 ? { nativeFee: quote.nativeFee, lzTokenFee: quote.lzTokenFee, oft: quote.oft } : await this.quotePathway(plan, "bridge").then((r) => ({ ...r.fee, oft: r.oft }));
|
|
2201
|
+
const sendParams = this.buildSendParams(plan, oft?.amountReceivedLD ?? amountLD);
|
|
2202
|
+
if (srcToken.approvalRequired !== false) {
|
|
2203
|
+
await this.ensureAllowance(plan, wc, from, amountLD);
|
|
1926
2204
|
}
|
|
1927
2205
|
try {
|
|
1928
2206
|
const hash = await wc.writeContract({
|
|
@@ -1934,7 +2212,7 @@ var LayerZeroService = class {
|
|
|
1934
2212
|
account: wc.account,
|
|
1935
2213
|
chain: srcChain
|
|
1936
2214
|
});
|
|
1937
|
-
return { txHash: hash, toAmount: amount };
|
|
2215
|
+
return { txHash: hash, toAmount: oft?.amountReceived ?? params.amount };
|
|
1938
2216
|
} catch (err) {
|
|
1939
2217
|
throw new StableTransactionError({
|
|
1940
2218
|
phase: "send",
|
|
@@ -1944,6 +2222,132 @@ var LayerZeroService = class {
|
|
|
1944
2222
|
});
|
|
1945
2223
|
}
|
|
1946
2224
|
}
|
|
2225
|
+
/**
|
|
2226
|
+
* Estimate how long a USDT0 bridge will take, end to end.
|
|
2227
|
+
*
|
|
2228
|
+
* Two independent inputs, both cheap:
|
|
2229
|
+
* - **observed** — median delivery time of recent messages LayerZero Scan has
|
|
2230
|
+
* already settled on this exact pathway.
|
|
2231
|
+
* - **formula** — LayerZero's documented estimate from the on-chain ULN config:
|
|
2232
|
+
* `srcBlockTime × confirmations + dstBlockTime × (2 + dvnCount)`.
|
|
2233
|
+
*
|
|
2234
|
+
* `observed` wins when available. The formula assumes `confirmations` is the whole
|
|
2235
|
+
* wait, which holds for L1 sources but badly understates rollups: Arbitrum→Stable
|
|
2236
|
+
* computes to ~15s (60 blocks × 0.25s) while real deliveries take ~18min, because
|
|
2237
|
+
* the DVNs wait for the L2 batch to finalize on L1.
|
|
2238
|
+
*/
|
|
2239
|
+
async estimateBridgeTime({
|
|
2240
|
+
fromChain,
|
|
2241
|
+
toChain,
|
|
2242
|
+
fromToken,
|
|
2243
|
+
toToken
|
|
2244
|
+
}) {
|
|
2245
|
+
const src = BRIDGE_CHAIN_CONFIGS[fromChain];
|
|
2246
|
+
const dst = BRIDGE_CHAIN_CONFIGS[toChain];
|
|
2247
|
+
const from = fromToken ?? src?.tokens.usdt0?.address;
|
|
2248
|
+
const to = toToken ?? dst?.tokens.usdt0?.address;
|
|
2249
|
+
if (!(src && from)) {
|
|
2250
|
+
throw new StableValidationError("fromChain", fromChain, `estimateBridgeTime: no OFT adapter for "${fromChain}"`);
|
|
2251
|
+
}
|
|
2252
|
+
if (!(dst && to)) {
|
|
2253
|
+
throw new StableValidationError("toChain", toChain, `estimateBridgeTime: no OFT adapter for "${toChain}"`);
|
|
2254
|
+
}
|
|
2255
|
+
const { srcToken, dstToken } = this.resolveRoute(fromChain, toChain, from, to);
|
|
2256
|
+
const [uln, observedSeconds] = await Promise.all([
|
|
2257
|
+
this.getUlnConfig(src, dst, srcToken.oftAdapter).catch(() => void 0),
|
|
2258
|
+
this.observedBridgeSeconds(src, dst, srcToken.oftAdapter, dstToken.oftAdapter).catch(() => void 0)
|
|
2259
|
+
]);
|
|
2260
|
+
const dvnCount = uln ? uln.requiredDVNCount + uln.optionalDVNCount : void 0;
|
|
2261
|
+
let formulaSeconds;
|
|
2262
|
+
if (uln && dvnCount !== void 0 && src.blockTimeSec && dst.blockTimeSec) {
|
|
2263
|
+
const computed = Math.round(
|
|
2264
|
+
uln.confirmations * src.blockTimeSec + dst.blockTimeSec * (2 + dvnCount)
|
|
2265
|
+
);
|
|
2266
|
+
formulaSeconds = Number.isFinite(computed) ? computed : void 0;
|
|
2267
|
+
}
|
|
2268
|
+
const seconds = observedSeconds ?? formulaSeconds;
|
|
2269
|
+
if (seconds === void 0) {
|
|
2270
|
+
throw new StableQuoteError({
|
|
2271
|
+
provider: "layerzero",
|
|
2272
|
+
shortMessage: `estimateBridgeTime: no pathway data for ${fromChain}\u2192${toChain}`
|
|
2273
|
+
});
|
|
2274
|
+
}
|
|
2275
|
+
return {
|
|
2276
|
+
seconds,
|
|
2277
|
+
source: observedSeconds !== void 0 ? "observed" : "formula",
|
|
2278
|
+
observedSeconds,
|
|
2279
|
+
formulaSeconds,
|
|
2280
|
+
confirmations: Number.isFinite(uln?.confirmations) ? uln?.confirmations : void 0,
|
|
2281
|
+
dvnCount: Number.isFinite(dvnCount) ? dvnCount : void 0
|
|
2282
|
+
};
|
|
2283
|
+
}
|
|
2284
|
+
/**
|
|
2285
|
+
* Follow an in-flight bridge by its source transaction hash. Poll this after
|
|
2286
|
+
* `bridge()` to drive a progress UI; a message LayerZero Scan has not indexed
|
|
2287
|
+
* yet reports `"pending"` rather than throwing.
|
|
2288
|
+
*/
|
|
2289
|
+
async trackBridge(srcTxHash) {
|
|
2290
|
+
const scanUrl = `${LZ_SCAN_UI}/tx/${srcTxHash}`;
|
|
2291
|
+
let message;
|
|
2292
|
+
try {
|
|
2293
|
+
message = (await scanFetch(`/messages/tx/${srcTxHash}`))?.data?.[0];
|
|
2294
|
+
} catch (err) {
|
|
2295
|
+
if (err instanceof ScanHttpError && err.status === 404) {
|
|
2296
|
+
return { state: "pending", srcTxHash, scanUrl };
|
|
2297
|
+
}
|
|
2298
|
+
throw new StableQuoteError({
|
|
2299
|
+
provider: "layerzero",
|
|
2300
|
+
shortMessage: `trackBridge: LayerZero Scan lookup failed for ${srcTxHash}`,
|
|
2301
|
+
cause: err
|
|
2302
|
+
});
|
|
2303
|
+
}
|
|
2304
|
+
if (!message) return { state: "pending", srcTxHash, scanUrl };
|
|
2305
|
+
const raw = message.status?.name ?? "";
|
|
2306
|
+
const sentAt = message.source?.tx?.blockTimestamp;
|
|
2307
|
+
return {
|
|
2308
|
+
// Everything terminal that is not DELIVERED means the tokens will not land
|
|
2309
|
+
// on their own; `raw` carries which of those it was.
|
|
2310
|
+
state: raw === "DELIVERED" ? "delivered" : TERMINAL_FAILURE_STATUSES.has(raw) ? "failed" : "inflight",
|
|
2311
|
+
raw,
|
|
2312
|
+
srcTxHash,
|
|
2313
|
+
dstTxHash: message.destination?.tx?.txHash,
|
|
2314
|
+
elapsedSeconds: sentAt ? Math.max(0, Math.floor(Date.now() / 1e3) - sentAt) : void 0,
|
|
2315
|
+
scanUrl
|
|
2316
|
+
};
|
|
2317
|
+
}
|
|
2318
|
+
/** Effective ULN config the DVNs enforce for this pathway, read from the source chain. */
|
|
2319
|
+
async getUlnConfig(src, dst, oapp) {
|
|
2320
|
+
return cached(`uln:${src.lzEid}-${dst.lzEid}-${oapp.toLowerCase()}`, CACHE_TTL_MS, async () => {
|
|
2321
|
+
const pc = createPublicClient5({ chain: makeChain(src.id, src.rpc), transport: http5(src.rpc) });
|
|
2322
|
+
const endpoint = await pc.readContract({ address: oapp, abi: OAPP_ABI, functionName: "endpoint" });
|
|
2323
|
+
const sendLib = await pc.readContract({
|
|
2324
|
+
address: endpoint,
|
|
2325
|
+
abi: ENDPOINT_V2_ABI,
|
|
2326
|
+
functionName: "getSendLibrary",
|
|
2327
|
+
args: [oapp, dst.lzEid]
|
|
2328
|
+
});
|
|
2329
|
+
const cfg = await pc.readContract({
|
|
2330
|
+
address: sendLib,
|
|
2331
|
+
abi: SEND_ULN_ABI,
|
|
2332
|
+
functionName: "getUlnConfig",
|
|
2333
|
+
args: [oapp, dst.lzEid]
|
|
2334
|
+
});
|
|
2335
|
+
return {
|
|
2336
|
+
confirmations: Number(cfg.confirmations),
|
|
2337
|
+
requiredDVNCount: Number(cfg.requiredDVNCount),
|
|
2338
|
+
optionalDVNCount: Number(cfg.optionalDVNCount)
|
|
2339
|
+
};
|
|
2340
|
+
});
|
|
2341
|
+
}
|
|
2342
|
+
/** Median delivery time of recently delivered messages on this pathway, in seconds. */
|
|
2343
|
+
async observedBridgeSeconds(src, dst, srcOapp, dstOapp) {
|
|
2344
|
+
const pathwayId = `${src.lzEid}-${dst.lzEid}-${srcOapp.toLowerCase()}-${dstOapp.toLowerCase()}`;
|
|
2345
|
+
return cached(`observed:${pathwayId}`, CACHE_TTL_MS, async () => {
|
|
2346
|
+
const { data } = await scanFetch(`/messages/pathway/${pathwayId}?limit=50`);
|
|
2347
|
+
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);
|
|
2348
|
+
return durations.length >= 3 ? Math.round(median(durations)) : void 0;
|
|
2349
|
+
});
|
|
2350
|
+
}
|
|
1947
2351
|
validateBridgeParams(params, method) {
|
|
1948
2352
|
if (!isAddress4(params.recipient)) {
|
|
1949
2353
|
throw new StableValidationError(
|
|
@@ -1967,37 +2371,95 @@ var LayerZeroService = class {
|
|
|
1967
2371
|
);
|
|
1968
2372
|
}
|
|
1969
2373
|
}
|
|
1970
|
-
|
|
2374
|
+
/**
|
|
2375
|
+
* Resolve a bridge request to one OFT mesh and its two endpoints.
|
|
2376
|
+
*
|
|
2377
|
+
* Both sides must name the same asset. An OFT escrows one specific ERC-20 and
|
|
2378
|
+
* mints its own peer on the far side — it cannot hand back a different token,
|
|
2379
|
+
* so a USDT0→XAUt0 request is a trade, not a bridge, and belongs to LI.FI.
|
|
2380
|
+
*/
|
|
2381
|
+
resolveRoute(fromChain, toChain, fromToken, toToken) {
|
|
1971
2382
|
const src = BRIDGE_CHAIN_CONFIGS[fromChain];
|
|
1972
2383
|
const dst = BRIDGE_CHAIN_CONFIGS[toChain];
|
|
1973
2384
|
if (!src) throw new StableValidationError("fromChain", fromChain, `Unsupported source chain: "${fromChain}"`);
|
|
1974
2385
|
if (!dst) throw new StableValidationError("toChain", toChain, `Unsupported destination chain: "${toChain}"`);
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
}
|
|
1978
|
-
if (fromToken.toLowerCase() !== src.usdt.toLowerCase()) {
|
|
2386
|
+
const asset = bridgeAssetFor(src, fromToken);
|
|
2387
|
+
if (!asset) {
|
|
1979
2388
|
throw new StableValidationError(
|
|
1980
2389
|
"fromToken",
|
|
1981
2390
|
fromToken,
|
|
1982
|
-
`
|
|
2391
|
+
`Native OFT bridge does not recognize "${fromToken}" on "${fromChain}" \u2014 it moves ${describeAssets(src)}. Use route "lifi" to bridge other tokens.`
|
|
1983
2392
|
);
|
|
1984
2393
|
}
|
|
1985
|
-
|
|
2394
|
+
const srcToken = src.tokens[asset];
|
|
2395
|
+
const dstToken = dst.tokens[asset];
|
|
2396
|
+
if (!dstToken) {
|
|
2397
|
+
throw new StableValidationError(
|
|
2398
|
+
"toChain",
|
|
2399
|
+
toChain,
|
|
2400
|
+
`Native OFT bridge cannot deliver ${asset} to "${toChain}" \u2014 no ${asset} deployment there. Use route "lifi" instead.`
|
|
2401
|
+
);
|
|
2402
|
+
}
|
|
2403
|
+
if (dstToken.address.toLowerCase() !== toToken.toLowerCase()) {
|
|
1986
2404
|
throw new StableValidationError(
|
|
1987
2405
|
"toToken",
|
|
1988
2406
|
toToken,
|
|
1989
|
-
`
|
|
2407
|
+
`Native OFT bridge moves ${asset} to itself \u2014 toToken must be ${dstToken.address} on "${toChain}", got "${toToken}". Use route "lifi" to bridge between different tokens.`
|
|
1990
2408
|
);
|
|
1991
2409
|
}
|
|
1992
|
-
return { src, dst };
|
|
2410
|
+
return { src, dst, asset, srcToken, dstToken };
|
|
2411
|
+
}
|
|
2412
|
+
/**
|
|
2413
|
+
* Validate a request and resolve it to a single OFT pathway. No network calls —
|
|
2414
|
+
* everything here is config, arithmetic and encoding.
|
|
2415
|
+
*/
|
|
2416
|
+
plan(params, method) {
|
|
2417
|
+
const { fromChain, toChain, fromToken, toToken, amount, recipient, dstNativeDrop } = params;
|
|
2418
|
+
const recipientAddr = recipient ?? ZERO_ADDRESS;
|
|
2419
|
+
this.validateBridgeParams({ fromChain, toChain, amount, recipient: recipientAddr }, method);
|
|
2420
|
+
if (dstNativeDrop !== void 0 && dstNativeDrop < 0n) {
|
|
2421
|
+
throw new StableValidationError(
|
|
2422
|
+
"dstNativeDrop",
|
|
2423
|
+
dstNativeDrop,
|
|
2424
|
+
`${method}: dstNativeDrop must not be negative`
|
|
2425
|
+
);
|
|
2426
|
+
}
|
|
2427
|
+
const { src, dst, asset, srcToken, dstToken } = this.resolveRoute(
|
|
2428
|
+
fromChain,
|
|
2429
|
+
toChain,
|
|
2430
|
+
fromToken,
|
|
2431
|
+
toToken
|
|
2432
|
+
);
|
|
2433
|
+
const chain = makeChain(src.id, src.rpc);
|
|
2434
|
+
return {
|
|
2435
|
+
src,
|
|
2436
|
+
dst,
|
|
2437
|
+
asset,
|
|
2438
|
+
srcToken,
|
|
2439
|
+
dstToken,
|
|
2440
|
+
oftAdapter: srcToken.oftAdapter,
|
|
2441
|
+
amountLD: parseUnits4(String(amount), srcToken.decimals),
|
|
2442
|
+
recipient: recipientAddr,
|
|
2443
|
+
nativeDrop: dstNativeDrop ?? 0n,
|
|
2444
|
+
chain,
|
|
2445
|
+
pc: createPublicClient5({ chain, transport: http5(src.rpc) })
|
|
2446
|
+
};
|
|
2447
|
+
}
|
|
2448
|
+
/** The address that will sign, or undefined when this service has no signer. */
|
|
2449
|
+
signerAddress() {
|
|
2450
|
+
return this.config.walletClient?.account?.address ?? this.config.account?.address;
|
|
1993
2451
|
}
|
|
1994
|
-
|
|
2452
|
+
/**
|
|
2453
|
+
* `SendParam` for one call. `minAmountLD` is the caller's slippage floor: 0 when
|
|
2454
|
+
* asking what a send would yield, and the quoted output when actually sending.
|
|
2455
|
+
*/
|
|
2456
|
+
buildSendParams(plan, minAmountLD) {
|
|
1995
2457
|
return {
|
|
1996
|
-
dstEid: dst.lzEid,
|
|
1997
|
-
to: padHex(recipient, { dir: "left", size: 32 }),
|
|
1998
|
-
amountLD,
|
|
1999
|
-
minAmountLD
|
|
2000
|
-
extraOptions: buildLzOptions(),
|
|
2458
|
+
dstEid: plan.dst.lzEid,
|
|
2459
|
+
to: padHex(plan.recipient, { dir: "left", size: 32 }),
|
|
2460
|
+
amountLD: plan.amountLD,
|
|
2461
|
+
minAmountLD,
|
|
2462
|
+
extraOptions: buildLzOptions(0n, plan.nativeDrop, plan.recipient),
|
|
2001
2463
|
composeMsg: "0x",
|
|
2002
2464
|
oftCmd: "0x"
|
|
2003
2465
|
};
|
|
@@ -2005,21 +2467,37 @@ var LayerZeroService = class {
|
|
|
2005
2467
|
};
|
|
2006
2468
|
|
|
2007
2469
|
// src/stable.ts
|
|
2008
|
-
function
|
|
2470
|
+
function isNativeOftRoute(fromChain, toChain, fromToken, toToken) {
|
|
2009
2471
|
const src = BRIDGE_CHAIN_CONFIGS[fromChain];
|
|
2010
2472
|
const dst = BRIDGE_CHAIN_CONFIGS[toChain];
|
|
2011
|
-
|
|
2473
|
+
if (!(src && dst)) return false;
|
|
2474
|
+
const asset = bridgeAssetFor(src, fromToken);
|
|
2475
|
+
if (!asset) return false;
|
|
2476
|
+
const srcToken = src.tokens[asset];
|
|
2477
|
+
const dstToken = dst.tokens[asset];
|
|
2478
|
+
return src.lzOutbound !== false && !!srcToken && srcToken.lzOutbound !== false && !!dstToken && dstToken.address.toLowerCase() === toToken.toLowerCase();
|
|
2012
2479
|
}
|
|
2013
2480
|
function selectBridge(params) {
|
|
2481
|
+
const selected = resolveBridge(params);
|
|
2482
|
+
if (selected === "lifi" && (params.dstNativeDrop ?? 0n) > 0n) {
|
|
2483
|
+
throw new StableValidationError(
|
|
2484
|
+
"dstNativeDrop",
|
|
2485
|
+
params.dstNativeDrop,
|
|
2486
|
+
`bridge: dstNativeDrop is only supported on the native OFT route, and ${params.fromChain}\u2192${params.toChain} resolved to LI.FI`
|
|
2487
|
+
);
|
|
2488
|
+
}
|
|
2489
|
+
return selected;
|
|
2490
|
+
}
|
|
2491
|
+
function resolveBridge(params) {
|
|
2014
2492
|
const route = params.route ?? "auto";
|
|
2015
|
-
if (route === "usdt0") return "
|
|
2493
|
+
if (route === "oft" || route === "usdt0") return "oft";
|
|
2016
2494
|
if (route === "lifi") return "lifi";
|
|
2017
|
-
return
|
|
2495
|
+
return isNativeOftRoute(
|
|
2018
2496
|
params.fromChain,
|
|
2019
2497
|
params.toChain,
|
|
2020
2498
|
params.fromToken,
|
|
2021
2499
|
params.toToken
|
|
2022
|
-
) ? "
|
|
2500
|
+
) ? "oft" : "lifi";
|
|
2023
2501
|
}
|
|
2024
2502
|
function validateTransferParams({
|
|
2025
2503
|
to,
|
|
@@ -2119,6 +2597,12 @@ function createStable({
|
|
|
2119
2597
|
chainId: chain.id,
|
|
2120
2598
|
vault: earnConfig.vault
|
|
2121
2599
|
}) : null;
|
|
2600
|
+
const erc3009Client = walletClient ? walletClient : account ? createWalletClient5({
|
|
2601
|
+
account,
|
|
2602
|
+
chain,
|
|
2603
|
+
transport: transport ?? http6(rpc)
|
|
2604
|
+
}) : createPublicClient6({ chain, transport: http6(rpc) });
|
|
2605
|
+
const authorization = erc3009Actions(erc3009Client);
|
|
2122
2606
|
function requireEarn() {
|
|
2123
2607
|
if (!morpho) {
|
|
2124
2608
|
throw new StableValidationError(
|
|
@@ -2195,10 +2679,15 @@ function createStable({
|
|
|
2195
2679
|
}
|
|
2196
2680
|
return {
|
|
2197
2681
|
transfer,
|
|
2198
|
-
|
|
2199
|
-
|
|
2682
|
+
// `async` so a rejected route (see selectBridge) surfaces as a rejected
|
|
2683
|
+
// promise like every other validation error, not a synchronous throw.
|
|
2684
|
+
quoteBridge: async (params) => selectBridge(params) === "oft" ? lz.quoteBridge(params) : lifi.quoteBridge(params),
|
|
2685
|
+
bridge: async (params) => selectBridge(params) === "oft" ? lz.bridge(params) : lifi.bridge(params),
|
|
2686
|
+
estimateBridgeTime: (params) => lz.estimateBridgeTime(params),
|
|
2687
|
+
trackBridge: (srcTxHash) => lz.trackBridge(srcTxHash),
|
|
2200
2688
|
quoteSwap: (params) => lifi.quoteSwap(params),
|
|
2201
2689
|
swap: (params) => lifi.swap(params),
|
|
2690
|
+
authorization,
|
|
2202
2691
|
earn: {
|
|
2203
2692
|
deposit: (params) => requireEarn().deposit(params),
|
|
2204
2693
|
bulkDeposit: (items, options) => requireEarn().bulkDeposit(items, options),
|
|
@@ -2234,14 +2723,11 @@ function createStableReader(config) {
|
|
|
2234
2723
|
}
|
|
2235
2724
|
};
|
|
2236
2725
|
}
|
|
2237
|
-
|
|
2238
|
-
// src/constants.ts
|
|
2239
|
-
var STABLE_USDT_ADDRESS = "0x779ded0c9e1022225f8e0630b35a9b54be713736";
|
|
2240
|
-
var STABLE_VAULT_ADDRESS = "0xb7Df8db22A5DBBFA9ebeb94b3910aec6a4f05c08";
|
|
2241
2726
|
export {
|
|
2242
2727
|
BRIDGE_CHAIN_CONFIGS,
|
|
2243
2728
|
CHAIN_CONFIGS,
|
|
2244
2729
|
Chain,
|
|
2730
|
+
ERC3009_ABI,
|
|
2245
2731
|
Network,
|
|
2246
2732
|
STABLE_USDT_ADDRESS,
|
|
2247
2733
|
STABLE_VAULT_ADDRESS,
|
|
@@ -2251,6 +2737,18 @@ export {
|
|
|
2251
2737
|
StableQuoteError,
|
|
2252
2738
|
StableTransactionError,
|
|
2253
2739
|
StableValidationError,
|
|
2740
|
+
bridgeAssetFor,
|
|
2741
|
+
cancelAuthorization,
|
|
2742
|
+
clearBridgeEstimateCache,
|
|
2254
2743
|
createStable,
|
|
2255
|
-
createStableReader
|
|
2744
|
+
createStableReader,
|
|
2745
|
+
erc3009Actions,
|
|
2746
|
+
getAuthorizationState,
|
|
2747
|
+
getAuthorizationUsage,
|
|
2748
|
+
prepareTransferWithAuthorization,
|
|
2749
|
+
receiveWithAuthorization,
|
|
2750
|
+
signReceiveAuthorization,
|
|
2751
|
+
signTransferAuthorization,
|
|
2752
|
+
transferWithAuthorization,
|
|
2753
|
+
verifyAuthorization
|
|
2256
2754
|
};
|