@stablechain/sdk 1.0.3 → 1.0.4
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.cjs +317 -18
- package/dist/index.d.cts +28 -1
- package/dist/index.d.ts +28 -1
- package/dist/index.js +330 -31
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -807,19 +807,19 @@ var MorphoService = class {
|
|
|
807
807
|
|
|
808
808
|
|
|
809
809
|
// src/types.ts
|
|
810
|
-
var Chain = /* @__PURE__ */ ((
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
return
|
|
810
|
+
var Chain = /* @__PURE__ */ ((Chain3) => {
|
|
811
|
+
Chain3["Sepolia"] = "sepolia";
|
|
812
|
+
Chain3["StableTestnet"] = "stable-testnet";
|
|
813
|
+
Chain3["Stable"] = "stable";
|
|
814
|
+
Chain3["Ethereum"] = "ethereum";
|
|
815
|
+
Chain3["Arbitrum"] = "arbitrum";
|
|
816
|
+
Chain3["Ink"] = "ink";
|
|
817
|
+
Chain3["Bera"] = "bera";
|
|
818
|
+
Chain3["MegaETH"] = "megaeth";
|
|
819
|
+
Chain3["Base"] = "base";
|
|
820
|
+
Chain3["BSC"] = "bsc";
|
|
821
|
+
Chain3["HyperEVM"] = "hyperevm";
|
|
822
|
+
return Chain3;
|
|
823
823
|
})(Chain || {});
|
|
824
824
|
var Network = /* @__PURE__ */ ((Network2) => {
|
|
825
825
|
Network2["Mainnet"] = "mainnet";
|
|
@@ -1723,7 +1723,304 @@ function isLiFiQuoteShape(v) {
|
|
|
1723
1723
|
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
1724
|
}
|
|
1725
1725
|
|
|
1726
|
+
// src/services/layerzero.ts
|
|
1727
|
+
|
|
1728
|
+
|
|
1729
|
+
|
|
1730
|
+
|
|
1731
|
+
|
|
1732
|
+
|
|
1733
|
+
|
|
1734
|
+
|
|
1735
|
+
|
|
1736
|
+
|
|
1737
|
+
|
|
1738
|
+
var C = CHAIN_CONFIGS;
|
|
1739
|
+
var BRIDGE_CHAIN_CONFIGS = {
|
|
1740
|
+
// ─── Testnet ───────────────────────────────────────────────────────────────
|
|
1741
|
+
["sepolia" /* Sepolia */]: { ...C["sepolia" /* Sepolia */], lzEid: 40161, oftAdapter: "0xc099cD946d5efCC35A99D64E808c1430cEf08126" },
|
|
1742
|
+
["stable-testnet" /* StableTestnet */]: { ...C["stable-testnet" /* StableTestnet */], lzEid: 40374, oftAdapter: "0x779Ded0c9e1022225f8E0630b35a9b54bE713736" },
|
|
1743
|
+
// ─── 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" }
|
|
1751
|
+
};
|
|
1752
|
+
var OFT_ADAPTER_ABI = [
|
|
1753
|
+
{
|
|
1754
|
+
name: "quoteSend",
|
|
1755
|
+
type: "function",
|
|
1756
|
+
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
|
+
},
|
|
1770
|
+
{ name: "_payInLzToken", type: "bool" }
|
|
1771
|
+
],
|
|
1772
|
+
outputs: [
|
|
1773
|
+
{
|
|
1774
|
+
name: "msgFee",
|
|
1775
|
+
type: "tuple",
|
|
1776
|
+
components: [
|
|
1777
|
+
{ name: "nativeFee", type: "uint256" },
|
|
1778
|
+
{ name: "lzTokenFee", type: "uint256" }
|
|
1779
|
+
]
|
|
1780
|
+
}
|
|
1781
|
+
],
|
|
1782
|
+
stateMutability: "view"
|
|
1783
|
+
},
|
|
1784
|
+
{
|
|
1785
|
+
name: "send",
|
|
1786
|
+
type: "function",
|
|
1787
|
+
inputs: [
|
|
1788
|
+
{
|
|
1789
|
+
name: "_sendParam",
|
|
1790
|
+
type: "tuple",
|
|
1791
|
+
components: [
|
|
1792
|
+
{ name: "dstEid", type: "uint32" },
|
|
1793
|
+
{ name: "to", type: "bytes32" },
|
|
1794
|
+
{ name: "amountLD", type: "uint256" },
|
|
1795
|
+
{ name: "minAmountLD", type: "uint256" },
|
|
1796
|
+
{ name: "extraOptions", type: "bytes" },
|
|
1797
|
+
{ name: "composeMsg", type: "bytes" },
|
|
1798
|
+
{ name: "oftCmd", type: "bytes" }
|
|
1799
|
+
]
|
|
1800
|
+
},
|
|
1801
|
+
{
|
|
1802
|
+
name: "_fee",
|
|
1803
|
+
type: "tuple",
|
|
1804
|
+
components: [
|
|
1805
|
+
{ name: "nativeFee", type: "uint256" },
|
|
1806
|
+
{ name: "lzTokenFee", type: "uint256" }
|
|
1807
|
+
]
|
|
1808
|
+
},
|
|
1809
|
+
{ name: "_refundAddress", type: "address" }
|
|
1810
|
+
],
|
|
1811
|
+
outputs: [],
|
|
1812
|
+
stateMutability: "payable"
|
|
1813
|
+
}
|
|
1814
|
+
];
|
|
1815
|
+
function buildLzOptions(gasLimit = 0n) {
|
|
1816
|
+
return _viem.concat.call(void 0, [
|
|
1817
|
+
_viem.numberToHex.call(void 0, 3, { size: 2 }),
|
|
1818
|
+
// TYPE_3 header
|
|
1819
|
+
_viem.numberToHex.call(void 0, 1, { size: 1 }),
|
|
1820
|
+
// Executor worker ID
|
|
1821
|
+
_viem.numberToHex.call(void 0, 17, { size: 2 }),
|
|
1822
|
+
// option payload length = 17 bytes
|
|
1823
|
+
_viem.numberToHex.call(void 0, 1, { size: 1 }),
|
|
1824
|
+
// LzReceive option type
|
|
1825
|
+
_viem.numberToHex.call(void 0, gasLimit, { size: 16 })
|
|
1826
|
+
// gas limit as uint128
|
|
1827
|
+
]);
|
|
1828
|
+
}
|
|
1829
|
+
var LayerZeroService = class {
|
|
1830
|
+
constructor(config) {
|
|
1831
|
+
this.config = config;
|
|
1832
|
+
}
|
|
1833
|
+
async quoteBridge({ fromChain, toChain, fromToken, toToken, amount, recipient }) {
|
|
1834
|
+
const recipientAddr = _nullishCoalesce(recipient, () => ( "0x0000000000000000000000000000000000000000"));
|
|
1835
|
+
this.validateBridgeParams({ fromChain, toChain, amount, recipient: recipientAddr }, "quoteBridge");
|
|
1836
|
+
const { src, dst } = this.resolveChains(fromChain, toChain, fromToken, toToken);
|
|
1837
|
+
const oftAdapter = src.oftAdapter;
|
|
1838
|
+
const amountLD = _viem.parseUnits.call(void 0, String(amount), src.decimals);
|
|
1839
|
+
try {
|
|
1840
|
+
const { nativeFee, lzTokenFee } = await _viem.createPublicClient.call(void 0, {
|
|
1841
|
+
chain: makeChain(src.id, src.rpc),
|
|
1842
|
+
transport: _viem.http.call(void 0, src.rpc)
|
|
1843
|
+
}).readContract({
|
|
1844
|
+
address: oftAdapter,
|
|
1845
|
+
abi: OFT_ADAPTER_ABI,
|
|
1846
|
+
functionName: "quoteSend",
|
|
1847
|
+
args: [this.buildSendParams(dst, amountLD, recipientAddr), false]
|
|
1848
|
+
});
|
|
1849
|
+
return { nativeFee, lzTokenFee, toAmount: amount };
|
|
1850
|
+
} catch (err) {
|
|
1851
|
+
throw new StableQuoteError({
|
|
1852
|
+
provider: "layerzero",
|
|
1853
|
+
shortMessage: "quoteBridge: quoteSend call failed",
|
|
1854
|
+
cause: err
|
|
1855
|
+
});
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
async bridge({ fromChain, toChain, fromToken, toToken, amount, recipient, quote }) {
|
|
1859
|
+
const from = _nullishCoalesce(_optionalChain([this, 'access', _37 => _37.config, 'access', _38 => _38.walletClient, 'optionalAccess', _39 => _39.account, 'optionalAccess', _40 => _40.address]), () => ( _optionalChain([this, 'access', _41 => _41.config, 'access', _42 => _42.account, 'optionalAccess', _43 => _43.address])));
|
|
1860
|
+
if (!from) {
|
|
1861
|
+
throw new StableValidationError(
|
|
1862
|
+
"signer",
|
|
1863
|
+
void 0,
|
|
1864
|
+
"bridge: requires a signer \u2014 set walletClient or account in StableConfig"
|
|
1865
|
+
);
|
|
1866
|
+
}
|
|
1867
|
+
const recipientAddr = _nullishCoalesce(recipient, () => ( from));
|
|
1868
|
+
this.validateBridgeParams({ fromChain, toChain, amount, recipient: recipientAddr }, "bridge");
|
|
1869
|
+
const { src, dst } = this.resolveChains(fromChain, toChain, fromToken, toToken);
|
|
1870
|
+
const oftAdapter = src.oftAdapter;
|
|
1871
|
+
const srcChain = makeChain(src.id, src.rpc);
|
|
1872
|
+
const wc = _nullishCoalesce(this.config.walletClient, () => ( _viem.createWalletClient.call(void 0, {
|
|
1873
|
+
account: this.config.account,
|
|
1874
|
+
chain: srcChain,
|
|
1875
|
+
transport: _nullishCoalesce(this.config.transport, () => ( _viem.http.call(void 0, src.rpc)))
|
|
1876
|
+
})));
|
|
1877
|
+
const pc = _viem.createPublicClient.call(void 0, { chain: srcChain, transport: _viem.http.call(void 0, src.rpc) });
|
|
1878
|
+
try {
|
|
1879
|
+
await wc.switchChain({ id: src.id });
|
|
1880
|
+
} catch (err) {
|
|
1881
|
+
throw new StableTransactionError({
|
|
1882
|
+
phase: "switch_chain",
|
|
1883
|
+
shortMessage: `bridge: wallet rejected or failed to switch to chain ${src.id}`,
|
|
1884
|
+
chainId: src.id,
|
|
1885
|
+
cause: err
|
|
1886
|
+
});
|
|
1887
|
+
}
|
|
1888
|
+
const amountLD = _viem.parseUnits.call(void 0, String(amount), src.decimals);
|
|
1889
|
+
const sendParams = this.buildSendParams(dst, amountLD, recipientAddr);
|
|
1890
|
+
const lzFee = _optionalChain([quote, 'optionalAccess', _44 => _44.nativeFee]) !== void 0 && _optionalChain([quote, 'optionalAccess', _45 => _45.lzTokenFee]) !== void 0 ? { nativeFee: quote.nativeFee, lzTokenFee: quote.lzTokenFee } : await this.quoteBridge({ fromChain, toChain, fromToken, toToken, amount, recipient: recipientAddr });
|
|
1891
|
+
const { nativeFee = 0n, lzTokenFee = 0n } = lzFee;
|
|
1892
|
+
const allowance = await pc.readContract({
|
|
1893
|
+
address: src.usdt,
|
|
1894
|
+
abi: _viem.erc20Abi,
|
|
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: _viem.erc20Abi,
|
|
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
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
try {
|
|
1928
|
+
const hash = await wc.writeContract({
|
|
1929
|
+
address: oftAdapter,
|
|
1930
|
+
abi: OFT_ADAPTER_ABI,
|
|
1931
|
+
functionName: "send",
|
|
1932
|
+
args: [sendParams, { nativeFee, lzTokenFee }, from],
|
|
1933
|
+
value: nativeFee,
|
|
1934
|
+
account: wc.account,
|
|
1935
|
+
chain: srcChain
|
|
1936
|
+
});
|
|
1937
|
+
return { txHash: hash, toAmount: amount };
|
|
1938
|
+
} catch (err) {
|
|
1939
|
+
throw new StableTransactionError({
|
|
1940
|
+
phase: "send",
|
|
1941
|
+
shortMessage: "bridge: OFT send failed",
|
|
1942
|
+
chainId: src.id,
|
|
1943
|
+
cause: err
|
|
1944
|
+
});
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
validateBridgeParams(params, method) {
|
|
1948
|
+
if (!_viem.isAddress.call(void 0, params.recipient)) {
|
|
1949
|
+
throw new StableValidationError(
|
|
1950
|
+
"recipient",
|
|
1951
|
+
params.recipient,
|
|
1952
|
+
`${method}: invalid recipient address "${params.recipient}"`
|
|
1953
|
+
);
|
|
1954
|
+
}
|
|
1955
|
+
if (!Number.isFinite(params.amount) || params.amount <= 0) {
|
|
1956
|
+
throw new StableValidationError(
|
|
1957
|
+
"amount",
|
|
1958
|
+
params.amount,
|
|
1959
|
+
`${method}: amount must be a finite positive number`
|
|
1960
|
+
);
|
|
1961
|
+
}
|
|
1962
|
+
if (params.fromChain === params.toChain) {
|
|
1963
|
+
throw new StableValidationError(
|
|
1964
|
+
"toChain",
|
|
1965
|
+
params.toChain,
|
|
1966
|
+
`${method}: fromChain and toChain must differ`
|
|
1967
|
+
);
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
resolveChains(fromChain, toChain, fromToken, toToken) {
|
|
1971
|
+
const src = BRIDGE_CHAIN_CONFIGS[fromChain];
|
|
1972
|
+
const dst = BRIDGE_CHAIN_CONFIGS[toChain];
|
|
1973
|
+
if (!src) throw new StableValidationError("fromChain", fromChain, `Unsupported source chain: "${fromChain}"`);
|
|
1974
|
+
if (!dst) throw new StableValidationError("toChain", toChain, `Unsupported destination chain: "${toChain}"`);
|
|
1975
|
+
if (!src.oftAdapter) {
|
|
1976
|
+
throw new StableValidationError("fromChain", fromChain, `No OFT adapter configured for "${fromChain}"`);
|
|
1977
|
+
}
|
|
1978
|
+
if (fromToken.toLowerCase() !== src.usdt.toLowerCase()) {
|
|
1979
|
+
throw new StableValidationError(
|
|
1980
|
+
"fromToken",
|
|
1981
|
+
fromToken,
|
|
1982
|
+
`USDT0 bridge only moves USDT0 \u2014 fromToken must be ${src.usdt} on "${fromChain}", got "${fromToken}". Use route "lifi" to bridge other tokens.`
|
|
1983
|
+
);
|
|
1984
|
+
}
|
|
1985
|
+
if (toToken.toLowerCase() !== dst.usdt.toLowerCase()) {
|
|
1986
|
+
throw new StableValidationError(
|
|
1987
|
+
"toToken",
|
|
1988
|
+
toToken,
|
|
1989
|
+
`USDT0 bridge only moves USDT0 \u2014 toToken must be ${dst.usdt} on "${toChain}", got "${toToken}". Use route "lifi" to bridge other tokens.`
|
|
1990
|
+
);
|
|
1991
|
+
}
|
|
1992
|
+
return { src, dst };
|
|
1993
|
+
}
|
|
1994
|
+
buildSendParams(dst, amountLD, recipient) {
|
|
1995
|
+
return {
|
|
1996
|
+
dstEid: dst.lzEid,
|
|
1997
|
+
to: _viem.padHex.call(void 0, recipient, { dir: "left", size: 32 }),
|
|
1998
|
+
amountLD,
|
|
1999
|
+
minAmountLD: amountLD,
|
|
2000
|
+
extraOptions: buildLzOptions(),
|
|
2001
|
+
composeMsg: "0x",
|
|
2002
|
+
oftCmd: "0x"
|
|
2003
|
+
};
|
|
2004
|
+
}
|
|
2005
|
+
};
|
|
2006
|
+
|
|
1726
2007
|
// src/stable.ts
|
|
2008
|
+
function isUsdt0Route(fromChain, toChain, fromToken, toToken) {
|
|
2009
|
+
const src = BRIDGE_CHAIN_CONFIGS[fromChain];
|
|
2010
|
+
const dst = BRIDGE_CHAIN_CONFIGS[toChain];
|
|
2011
|
+
return !!src && !!dst && !!src.oftAdapter && src.lzOutbound !== false && src.usdt.toLowerCase() === fromToken.toLowerCase() && dst.usdt.toLowerCase() === toToken.toLowerCase();
|
|
2012
|
+
}
|
|
2013
|
+
function selectBridge(params) {
|
|
2014
|
+
const route = _nullishCoalesce(params.route, () => ( "auto"));
|
|
2015
|
+
if (route === "usdt0") return "usdt0";
|
|
2016
|
+
if (route === "lifi") return "lifi";
|
|
2017
|
+
return isUsdt0Route(
|
|
2018
|
+
params.fromChain,
|
|
2019
|
+
params.toChain,
|
|
2020
|
+
params.fromToken,
|
|
2021
|
+
params.toToken
|
|
2022
|
+
) ? "usdt0" : "lifi";
|
|
2023
|
+
}
|
|
1727
2024
|
function validateTransferParams({
|
|
1728
2025
|
to,
|
|
1729
2026
|
amount,
|
|
@@ -1814,6 +2111,7 @@ function createStable({
|
|
|
1814
2111
|
);
|
|
1815
2112
|
const signerConfig = { account, transport, walletClient };
|
|
1816
2113
|
const lifi = new LiFiService({ ...signerConfig, rpc, chainId: chain.id });
|
|
2114
|
+
const lz = new LayerZeroService(signerConfig);
|
|
1817
2115
|
const merkl = new MerklService({ ...signerConfig, rpc, chainId: chain.id, merklApiBase });
|
|
1818
2116
|
const morpho = earnConfig ? new MorphoService({
|
|
1819
2117
|
...signerConfig,
|
|
@@ -1832,7 +2130,7 @@ function createStable({
|
|
|
1832
2130
|
return morpho;
|
|
1833
2131
|
}
|
|
1834
2132
|
function requireSignerAddress() {
|
|
1835
|
-
const from = _nullishCoalesce(_optionalChain([walletClient, 'optionalAccess',
|
|
2133
|
+
const from = _nullishCoalesce(_optionalChain([walletClient, 'optionalAccess', _46 => _46.account, 'optionalAccess', _47 => _47.address]), () => ( _optionalChain([account, 'optionalAccess', _48 => _48.address])));
|
|
1836
2134
|
if (!from) {
|
|
1837
2135
|
throw new StableValidationError(
|
|
1838
2136
|
"signer",
|
|
@@ -1845,7 +2143,7 @@ function createStable({
|
|
|
1845
2143
|
async function transfer(params) {
|
|
1846
2144
|
validateTransferParams(params);
|
|
1847
2145
|
const { from, to, amount, token, tokenDecimals } = params;
|
|
1848
|
-
const acc = _nullishCoalesce(_nullishCoalesce(_optionalChain([walletClient, 'optionalAccess',
|
|
2146
|
+
const acc = _nullishCoalesce(_nullishCoalesce(_optionalChain([walletClient, 'optionalAccess', _49 => _49.account]), () => ( account)), () => ( from));
|
|
1849
2147
|
const wc = _nullishCoalesce(walletClient, () => ( _viem.createWalletClient.call(void 0, {
|
|
1850
2148
|
account: acc,
|
|
1851
2149
|
chain,
|
|
@@ -1897,8 +2195,8 @@ function createStable({
|
|
|
1897
2195
|
}
|
|
1898
2196
|
return {
|
|
1899
2197
|
transfer,
|
|
1900
|
-
quoteBridge: (params) => lifi.quoteBridge(params),
|
|
1901
|
-
bridge: (params) => lifi.bridge(params),
|
|
2198
|
+
quoteBridge: (params) => selectBridge(params) === "usdt0" ? lz.quoteBridge(params) : lifi.quoteBridge(params),
|
|
2199
|
+
bridge: (params) => selectBridge(params) === "usdt0" ? lz.bridge(params) : lifi.bridge(params),
|
|
1902
2200
|
quoteSwap: (params) => lifi.quoteSwap(params),
|
|
1903
2201
|
swap: (params) => lifi.swap(params),
|
|
1904
2202
|
earn: {
|
|
@@ -1954,4 +2252,5 @@ var STABLE_VAULT_ADDRESS = "0xb7Df8db22A5DBBFA9ebeb94b3910aec6a4f05c08";
|
|
|
1954
2252
|
|
|
1955
2253
|
|
|
1956
2254
|
|
|
1957
|
-
|
|
2255
|
+
|
|
2256
|
+
exports.BRIDGE_CHAIN_CONFIGS = BRIDGE_CHAIN_CONFIGS; exports.CHAIN_CONFIGS = CHAIN_CONFIGS; exports.Chain = Chain; exports.Network = Network; exports.STABLE_USDT_ADDRESS = STABLE_USDT_ADDRESS; exports.STABLE_VAULT_ADDRESS = STABLE_VAULT_ADDRESS; exports.StableBulkError = StableBulkError; exports.StableError = StableError; exports.StableNetworkError = StableNetworkError; exports.StableQuoteError = StableQuoteError; exports.StableTransactionError = StableTransactionError; exports.StableValidationError = StableValidationError; exports.createStable = createStable; exports.createStableReader = createStableReader;
|
package/dist/index.d.cts
CHANGED
|
@@ -95,7 +95,20 @@ declare enum Network {
|
|
|
95
95
|
interface BridgeQuote {
|
|
96
96
|
/** Estimated output amount on the destination chain */
|
|
97
97
|
toAmount?: number;
|
|
98
|
+
/** LayerZero native messaging fee (wei) — only present for USDT0→USDT0 (OFT) routes */
|
|
99
|
+
nativeFee?: bigint;
|
|
100
|
+
/** LayerZero fee paid in LZ token (wei) — only present for USDT0→USDT0 (OFT) routes */
|
|
101
|
+
lzTokenFee?: bigint;
|
|
98
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Bridge route selection:
|
|
105
|
+
* - `"usdt0"` — force the USDT0 LayerZero OFT path (no protocol fee, just gas + LZ
|
|
106
|
+
* messaging fee; slower). Throws if the chain pair has no USDT0 adapter.
|
|
107
|
+
* - `"lifi"` — force the LI.FI aggregator path (fast, charges a fee).
|
|
108
|
+
* - `"auto"` — use USDT0 when both chains support it and tokens match, else LI.FI.
|
|
109
|
+
* This is the default.
|
|
110
|
+
*/
|
|
111
|
+
type BridgeRoute = "auto" | "usdt0" | "lifi";
|
|
99
112
|
interface BridgeParams {
|
|
100
113
|
fromChain: Chain;
|
|
101
114
|
toChain: Chain;
|
|
@@ -108,6 +121,11 @@ interface BridgeParams {
|
|
|
108
121
|
fromDecimals?: number;
|
|
109
122
|
/** Recipient address — defaults to the signer */
|
|
110
123
|
recipient?: string;
|
|
124
|
+
/**
|
|
125
|
+
* Which bridge to use. Defaults to "auto" (USDT0 OFT when available, else LI.FI).
|
|
126
|
+
* See {@link BridgeRoute}.
|
|
127
|
+
*/
|
|
128
|
+
route?: BridgeRoute;
|
|
111
129
|
/** Pre-fetched quote from quoteBridge() — skips the quote fetch when passed to bridge() */
|
|
112
130
|
quote?: BridgeQuote;
|
|
113
131
|
}
|
|
@@ -395,6 +413,15 @@ interface ChainConfig {
|
|
|
395
413
|
}
|
|
396
414
|
declare const CHAIN_CONFIGS: Partial<Record<Chain, ChainConfig>>;
|
|
397
415
|
|
|
416
|
+
interface BridgeChainConfig extends ChainConfig {
|
|
417
|
+
lzEid: number;
|
|
418
|
+
/** Only set on source chains that have an OFT Adapter deployed */
|
|
419
|
+
oftAdapter?: Address;
|
|
420
|
+
/** False when the OFT adapter exists but has no outbound send pathways configured */
|
|
421
|
+
lzOutbound?: boolean;
|
|
422
|
+
}
|
|
423
|
+
declare const BRIDGE_CHAIN_CONFIGS: Partial<Record<Chain, BridgeChainConfig>>;
|
|
424
|
+
|
|
398
425
|
declare const STABLE_USDT_ADDRESS: Address;
|
|
399
426
|
declare const STABLE_VAULT_ADDRESS: Address;
|
|
400
427
|
|
|
@@ -455,4 +482,4 @@ declare class StableBulkError extends StableError {
|
|
|
455
482
|
});
|
|
456
483
|
}
|
|
457
484
|
|
|
458
|
-
export { type BridgeParams, type BridgeQuote, type BulkDepositOptions, type BulkEarnResult, type BulkEarnResultItem, type BulkWithdrawOptions, CHAIN_CONFIGS, Chain, type ChainConfig, type EarnConfig, type EarnDepositCalldata, type EarnDepositParams, type EarnRedeemParams, type EarnResult, type EarnWithdrawCalldata, type EarnWithdrawParams, type ForceDeallocation, type ForceRedeemParams, type ForceWithdrawParams, type MerklClaimCalldata, type MerklClaimResult, type MerklReward, type MerklRewardToken, type MerklRewardsResult, Network, type OperationResult, STABLE_USDT_ADDRESS, STABLE_VAULT_ADDRESS, StableBulkError, type StableClient, type StableConfig, StableError, StableNetworkError, StableQuoteError, type StableReader, type StableReaderConfig, StableTransactionError, StableValidationError, type SwapParams, type SwapQuote, type TransferParams, type VaultPosition, type VaultYield, type VaultYieldReward, type WithdrawStatus, type YieldPreview, createStable, createStableReader };
|
|
485
|
+
export { BRIDGE_CHAIN_CONFIGS, type BridgeChainConfig, type BridgeParams, type BridgeQuote, type BridgeRoute, type BulkDepositOptions, type BulkEarnResult, type BulkEarnResultItem, type BulkWithdrawOptions, CHAIN_CONFIGS, Chain, type ChainConfig, type EarnConfig, type EarnDepositCalldata, type EarnDepositParams, type EarnRedeemParams, type EarnResult, type EarnWithdrawCalldata, type EarnWithdrawParams, type ForceDeallocation, type ForceRedeemParams, type ForceWithdrawParams, type MerklClaimCalldata, type MerklClaimResult, type MerklReward, type MerklRewardToken, type MerklRewardsResult, Network, type OperationResult, STABLE_USDT_ADDRESS, STABLE_VAULT_ADDRESS, StableBulkError, type StableClient, type StableConfig, StableError, StableNetworkError, StableQuoteError, type StableReader, type StableReaderConfig, StableTransactionError, StableValidationError, type SwapParams, type SwapQuote, type TransferParams, type VaultPosition, type VaultYield, type VaultYieldReward, type WithdrawStatus, type YieldPreview, createStable, createStableReader };
|
package/dist/index.d.ts
CHANGED
|
@@ -95,7 +95,20 @@ declare enum Network {
|
|
|
95
95
|
interface BridgeQuote {
|
|
96
96
|
/** Estimated output amount on the destination chain */
|
|
97
97
|
toAmount?: number;
|
|
98
|
+
/** LayerZero native messaging fee (wei) — only present for USDT0→USDT0 (OFT) routes */
|
|
99
|
+
nativeFee?: bigint;
|
|
100
|
+
/** LayerZero fee paid in LZ token (wei) — only present for USDT0→USDT0 (OFT) routes */
|
|
101
|
+
lzTokenFee?: bigint;
|
|
98
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Bridge route selection:
|
|
105
|
+
* - `"usdt0"` — force the USDT0 LayerZero OFT path (no protocol fee, just gas + LZ
|
|
106
|
+
* messaging fee; slower). Throws if the chain pair has no USDT0 adapter.
|
|
107
|
+
* - `"lifi"` — force the LI.FI aggregator path (fast, charges a fee).
|
|
108
|
+
* - `"auto"` — use USDT0 when both chains support it and tokens match, else LI.FI.
|
|
109
|
+
* This is the default.
|
|
110
|
+
*/
|
|
111
|
+
type BridgeRoute = "auto" | "usdt0" | "lifi";
|
|
99
112
|
interface BridgeParams {
|
|
100
113
|
fromChain: Chain;
|
|
101
114
|
toChain: Chain;
|
|
@@ -108,6 +121,11 @@ interface BridgeParams {
|
|
|
108
121
|
fromDecimals?: number;
|
|
109
122
|
/** Recipient address — defaults to the signer */
|
|
110
123
|
recipient?: string;
|
|
124
|
+
/**
|
|
125
|
+
* Which bridge to use. Defaults to "auto" (USDT0 OFT when available, else LI.FI).
|
|
126
|
+
* See {@link BridgeRoute}.
|
|
127
|
+
*/
|
|
128
|
+
route?: BridgeRoute;
|
|
111
129
|
/** Pre-fetched quote from quoteBridge() — skips the quote fetch when passed to bridge() */
|
|
112
130
|
quote?: BridgeQuote;
|
|
113
131
|
}
|
|
@@ -395,6 +413,15 @@ interface ChainConfig {
|
|
|
395
413
|
}
|
|
396
414
|
declare const CHAIN_CONFIGS: Partial<Record<Chain, ChainConfig>>;
|
|
397
415
|
|
|
416
|
+
interface BridgeChainConfig extends ChainConfig {
|
|
417
|
+
lzEid: number;
|
|
418
|
+
/** Only set on source chains that have an OFT Adapter deployed */
|
|
419
|
+
oftAdapter?: Address;
|
|
420
|
+
/** False when the OFT adapter exists but has no outbound send pathways configured */
|
|
421
|
+
lzOutbound?: boolean;
|
|
422
|
+
}
|
|
423
|
+
declare const BRIDGE_CHAIN_CONFIGS: Partial<Record<Chain, BridgeChainConfig>>;
|
|
424
|
+
|
|
398
425
|
declare const STABLE_USDT_ADDRESS: Address;
|
|
399
426
|
declare const STABLE_VAULT_ADDRESS: Address;
|
|
400
427
|
|
|
@@ -455,4 +482,4 @@ declare class StableBulkError extends StableError {
|
|
|
455
482
|
});
|
|
456
483
|
}
|
|
457
484
|
|
|
458
|
-
export { type BridgeParams, type BridgeQuote, type BulkDepositOptions, type BulkEarnResult, type BulkEarnResultItem, type BulkWithdrawOptions, CHAIN_CONFIGS, Chain, type ChainConfig, type EarnConfig, type EarnDepositCalldata, type EarnDepositParams, type EarnRedeemParams, type EarnResult, type EarnWithdrawCalldata, type EarnWithdrawParams, type ForceDeallocation, type ForceRedeemParams, type ForceWithdrawParams, type MerklClaimCalldata, type MerklClaimResult, type MerklReward, type MerklRewardToken, type MerklRewardsResult, Network, type OperationResult, STABLE_USDT_ADDRESS, STABLE_VAULT_ADDRESS, StableBulkError, type StableClient, type StableConfig, StableError, StableNetworkError, StableQuoteError, type StableReader, type StableReaderConfig, StableTransactionError, StableValidationError, type SwapParams, type SwapQuote, type TransferParams, type VaultPosition, type VaultYield, type VaultYieldReward, type WithdrawStatus, type YieldPreview, createStable, createStableReader };
|
|
485
|
+
export { BRIDGE_CHAIN_CONFIGS, type BridgeChainConfig, type BridgeParams, type BridgeQuote, type BridgeRoute, type BulkDepositOptions, type BulkEarnResult, type BulkEarnResultItem, type BulkWithdrawOptions, CHAIN_CONFIGS, Chain, type ChainConfig, type EarnConfig, type EarnDepositCalldata, type EarnDepositParams, type EarnRedeemParams, type EarnResult, type EarnWithdrawCalldata, type EarnWithdrawParams, type ForceDeallocation, type ForceRedeemParams, type ForceWithdrawParams, type MerklClaimCalldata, type MerklClaimResult, type MerklReward, type MerklRewardToken, type MerklRewardsResult, Network, type OperationResult, STABLE_USDT_ADDRESS, STABLE_VAULT_ADDRESS, StableBulkError, type StableClient, type StableConfig, StableError, StableNetworkError, StableQuoteError, type StableReader, type StableReaderConfig, StableTransactionError, StableValidationError, type SwapParams, type SwapQuote, type TransferParams, type VaultPosition, type VaultYield, type VaultYieldReward, type WithdrawStatus, type YieldPreview, createStable, createStableReader };
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// src/stable.ts
|
|
2
2
|
import {
|
|
3
|
-
http as
|
|
4
|
-
createPublicClient as
|
|
5
|
-
createWalletClient as
|
|
6
|
-
erc20Abi as
|
|
7
|
-
isAddress as
|
|
8
|
-
parseUnits as
|
|
3
|
+
http as http6,
|
|
4
|
+
createPublicClient as createPublicClient6,
|
|
5
|
+
createWalletClient as createWalletClient5,
|
|
6
|
+
erc20Abi as erc20Abi5,
|
|
7
|
+
isAddress as isAddress5,
|
|
8
|
+
parseUnits as parseUnits5
|
|
9
9
|
} from "viem";
|
|
10
10
|
import { stable as stableMainnet2, stableTestnet as stableTestnet2 } from "viem/chains";
|
|
11
11
|
|
|
@@ -807,19 +807,19 @@ import {
|
|
|
807
807
|
import { stable as stableMainnet, stableTestnet } from "viem/chains";
|
|
808
808
|
|
|
809
809
|
// src/types.ts
|
|
810
|
-
var Chain = /* @__PURE__ */ ((
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
return
|
|
810
|
+
var Chain = /* @__PURE__ */ ((Chain3) => {
|
|
811
|
+
Chain3["Sepolia"] = "sepolia";
|
|
812
|
+
Chain3["StableTestnet"] = "stable-testnet";
|
|
813
|
+
Chain3["Stable"] = "stable";
|
|
814
|
+
Chain3["Ethereum"] = "ethereum";
|
|
815
|
+
Chain3["Arbitrum"] = "arbitrum";
|
|
816
|
+
Chain3["Ink"] = "ink";
|
|
817
|
+
Chain3["Bera"] = "bera";
|
|
818
|
+
Chain3["MegaETH"] = "megaeth";
|
|
819
|
+
Chain3["Base"] = "base";
|
|
820
|
+
Chain3["BSC"] = "bsc";
|
|
821
|
+
Chain3["HyperEVM"] = "hyperevm";
|
|
822
|
+
return Chain3;
|
|
823
823
|
})(Chain || {});
|
|
824
824
|
var Network = /* @__PURE__ */ ((Network2) => {
|
|
825
825
|
Network2["Mainnet"] = "mainnet";
|
|
@@ -1723,14 +1723,311 @@ function isLiFiQuoteShape(v) {
|
|
|
1723
1723
|
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
1724
|
}
|
|
1725
1725
|
|
|
1726
|
+
// src/services/layerzero.ts
|
|
1727
|
+
import {
|
|
1728
|
+
concat,
|
|
1729
|
+
createPublicClient as createPublicClient5,
|
|
1730
|
+
createWalletClient as createWalletClient4,
|
|
1731
|
+
erc20Abi as erc20Abi4,
|
|
1732
|
+
http as http5,
|
|
1733
|
+
isAddress as isAddress4,
|
|
1734
|
+
numberToHex,
|
|
1735
|
+
padHex,
|
|
1736
|
+
parseUnits as parseUnits4
|
|
1737
|
+
} from "viem";
|
|
1738
|
+
var C = CHAIN_CONFIGS;
|
|
1739
|
+
var BRIDGE_CHAIN_CONFIGS = {
|
|
1740
|
+
// ─── Testnet ───────────────────────────────────────────────────────────────
|
|
1741
|
+
["sepolia" /* Sepolia */]: { ...C["sepolia" /* Sepolia */], lzEid: 40161, oftAdapter: "0xc099cD946d5efCC35A99D64E808c1430cEf08126" },
|
|
1742
|
+
["stable-testnet" /* StableTestnet */]: { ...C["stable-testnet" /* StableTestnet */], lzEid: 40374, oftAdapter: "0x779Ded0c9e1022225f8E0630b35a9b54bE713736" },
|
|
1743
|
+
// ─── 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" }
|
|
1751
|
+
};
|
|
1752
|
+
var OFT_ADAPTER_ABI = [
|
|
1753
|
+
{
|
|
1754
|
+
name: "quoteSend",
|
|
1755
|
+
type: "function",
|
|
1756
|
+
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
|
+
},
|
|
1770
|
+
{ name: "_payInLzToken", type: "bool" }
|
|
1771
|
+
],
|
|
1772
|
+
outputs: [
|
|
1773
|
+
{
|
|
1774
|
+
name: "msgFee",
|
|
1775
|
+
type: "tuple",
|
|
1776
|
+
components: [
|
|
1777
|
+
{ name: "nativeFee", type: "uint256" },
|
|
1778
|
+
{ name: "lzTokenFee", type: "uint256" }
|
|
1779
|
+
]
|
|
1780
|
+
}
|
|
1781
|
+
],
|
|
1782
|
+
stateMutability: "view"
|
|
1783
|
+
},
|
|
1784
|
+
{
|
|
1785
|
+
name: "send",
|
|
1786
|
+
type: "function",
|
|
1787
|
+
inputs: [
|
|
1788
|
+
{
|
|
1789
|
+
name: "_sendParam",
|
|
1790
|
+
type: "tuple",
|
|
1791
|
+
components: [
|
|
1792
|
+
{ name: "dstEid", type: "uint32" },
|
|
1793
|
+
{ name: "to", type: "bytes32" },
|
|
1794
|
+
{ name: "amountLD", type: "uint256" },
|
|
1795
|
+
{ name: "minAmountLD", type: "uint256" },
|
|
1796
|
+
{ name: "extraOptions", type: "bytes" },
|
|
1797
|
+
{ name: "composeMsg", type: "bytes" },
|
|
1798
|
+
{ name: "oftCmd", type: "bytes" }
|
|
1799
|
+
]
|
|
1800
|
+
},
|
|
1801
|
+
{
|
|
1802
|
+
name: "_fee",
|
|
1803
|
+
type: "tuple",
|
|
1804
|
+
components: [
|
|
1805
|
+
{ name: "nativeFee", type: "uint256" },
|
|
1806
|
+
{ name: "lzTokenFee", type: "uint256" }
|
|
1807
|
+
]
|
|
1808
|
+
},
|
|
1809
|
+
{ name: "_refundAddress", type: "address" }
|
|
1810
|
+
],
|
|
1811
|
+
outputs: [],
|
|
1812
|
+
stateMutability: "payable"
|
|
1813
|
+
}
|
|
1814
|
+
];
|
|
1815
|
+
function buildLzOptions(gasLimit = 0n) {
|
|
1816
|
+
return concat([
|
|
1817
|
+
numberToHex(3, { size: 2 }),
|
|
1818
|
+
// TYPE_3 header
|
|
1819
|
+
numberToHex(1, { size: 1 }),
|
|
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
|
|
1827
|
+
]);
|
|
1828
|
+
}
|
|
1829
|
+
var LayerZeroService = class {
|
|
1830
|
+
constructor(config) {
|
|
1831
|
+
this.config = config;
|
|
1832
|
+
}
|
|
1833
|
+
async quoteBridge({ fromChain, toChain, fromToken, toToken, amount, recipient }) {
|
|
1834
|
+
const recipientAddr = recipient ?? "0x0000000000000000000000000000000000000000";
|
|
1835
|
+
this.validateBridgeParams({ fromChain, toChain, amount, recipient: recipientAddr }, "quoteBridge");
|
|
1836
|
+
const { src, dst } = this.resolveChains(fromChain, toChain, fromToken, toToken);
|
|
1837
|
+
const oftAdapter = src.oftAdapter;
|
|
1838
|
+
const amountLD = parseUnits4(String(amount), src.decimals);
|
|
1839
|
+
try {
|
|
1840
|
+
const { nativeFee, lzTokenFee } = await createPublicClient5({
|
|
1841
|
+
chain: makeChain(src.id, src.rpc),
|
|
1842
|
+
transport: http5(src.rpc)
|
|
1843
|
+
}).readContract({
|
|
1844
|
+
address: oftAdapter,
|
|
1845
|
+
abi: OFT_ADAPTER_ABI,
|
|
1846
|
+
functionName: "quoteSend",
|
|
1847
|
+
args: [this.buildSendParams(dst, amountLD, recipientAddr), false]
|
|
1848
|
+
});
|
|
1849
|
+
return { nativeFee, lzTokenFee, toAmount: amount };
|
|
1850
|
+
} catch (err) {
|
|
1851
|
+
throw new StableQuoteError({
|
|
1852
|
+
provider: "layerzero",
|
|
1853
|
+
shortMessage: "quoteBridge: quoteSend call failed",
|
|
1854
|
+
cause: err
|
|
1855
|
+
});
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
async bridge({ fromChain, toChain, fromToken, toToken, amount, recipient, quote }) {
|
|
1859
|
+
const from = this.config.walletClient?.account?.address ?? this.config.account?.address;
|
|
1860
|
+
if (!from) {
|
|
1861
|
+
throw new StableValidationError(
|
|
1862
|
+
"signer",
|
|
1863
|
+
void 0,
|
|
1864
|
+
"bridge: requires a signer \u2014 set walletClient or account in StableConfig"
|
|
1865
|
+
);
|
|
1866
|
+
}
|
|
1867
|
+
const recipientAddr = recipient ?? from;
|
|
1868
|
+
this.validateBridgeParams({ fromChain, toChain, amount, recipient: recipientAddr }, "bridge");
|
|
1869
|
+
const { src, dst } = this.resolveChains(fromChain, toChain, fromToken, toToken);
|
|
1870
|
+
const oftAdapter = src.oftAdapter;
|
|
1871
|
+
const srcChain = makeChain(src.id, src.rpc);
|
|
1872
|
+
const wc = this.config.walletClient ?? createWalletClient4({
|
|
1873
|
+
account: this.config.account,
|
|
1874
|
+
chain: srcChain,
|
|
1875
|
+
transport: this.config.transport ?? http5(src.rpc)
|
|
1876
|
+
});
|
|
1877
|
+
const pc = createPublicClient5({ chain: srcChain, transport: http5(src.rpc) });
|
|
1878
|
+
try {
|
|
1879
|
+
await wc.switchChain({ id: src.id });
|
|
1880
|
+
} catch (err) {
|
|
1881
|
+
throw new StableTransactionError({
|
|
1882
|
+
phase: "switch_chain",
|
|
1883
|
+
shortMessage: `bridge: wallet rejected or failed to switch to chain ${src.id}`,
|
|
1884
|
+
chainId: src.id,
|
|
1885
|
+
cause: err
|
|
1886
|
+
});
|
|
1887
|
+
}
|
|
1888
|
+
const amountLD = parseUnits4(String(amount), src.decimals);
|
|
1889
|
+
const sendParams = this.buildSendParams(dst, amountLD, recipientAddr);
|
|
1890
|
+
const lzFee = quote?.nativeFee !== void 0 && quote?.lzTokenFee !== void 0 ? { nativeFee: quote.nativeFee, lzTokenFee: quote.lzTokenFee } : await this.quoteBridge({ fromChain, toChain, fromToken, toToken, amount, recipient: recipientAddr });
|
|
1891
|
+
const { nativeFee = 0n, lzTokenFee = 0n } = lzFee;
|
|
1892
|
+
const allowance = await pc.readContract({
|
|
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
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
try {
|
|
1928
|
+
const hash = await wc.writeContract({
|
|
1929
|
+
address: oftAdapter,
|
|
1930
|
+
abi: OFT_ADAPTER_ABI,
|
|
1931
|
+
functionName: "send",
|
|
1932
|
+
args: [sendParams, { nativeFee, lzTokenFee }, from],
|
|
1933
|
+
value: nativeFee,
|
|
1934
|
+
account: wc.account,
|
|
1935
|
+
chain: srcChain
|
|
1936
|
+
});
|
|
1937
|
+
return { txHash: hash, toAmount: amount };
|
|
1938
|
+
} catch (err) {
|
|
1939
|
+
throw new StableTransactionError({
|
|
1940
|
+
phase: "send",
|
|
1941
|
+
shortMessage: "bridge: OFT send failed",
|
|
1942
|
+
chainId: src.id,
|
|
1943
|
+
cause: err
|
|
1944
|
+
});
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
validateBridgeParams(params, method) {
|
|
1948
|
+
if (!isAddress4(params.recipient)) {
|
|
1949
|
+
throw new StableValidationError(
|
|
1950
|
+
"recipient",
|
|
1951
|
+
params.recipient,
|
|
1952
|
+
`${method}: invalid recipient address "${params.recipient}"`
|
|
1953
|
+
);
|
|
1954
|
+
}
|
|
1955
|
+
if (!Number.isFinite(params.amount) || params.amount <= 0) {
|
|
1956
|
+
throw new StableValidationError(
|
|
1957
|
+
"amount",
|
|
1958
|
+
params.amount,
|
|
1959
|
+
`${method}: amount must be a finite positive number`
|
|
1960
|
+
);
|
|
1961
|
+
}
|
|
1962
|
+
if (params.fromChain === params.toChain) {
|
|
1963
|
+
throw new StableValidationError(
|
|
1964
|
+
"toChain",
|
|
1965
|
+
params.toChain,
|
|
1966
|
+
`${method}: fromChain and toChain must differ`
|
|
1967
|
+
);
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
resolveChains(fromChain, toChain, fromToken, toToken) {
|
|
1971
|
+
const src = BRIDGE_CHAIN_CONFIGS[fromChain];
|
|
1972
|
+
const dst = BRIDGE_CHAIN_CONFIGS[toChain];
|
|
1973
|
+
if (!src) throw new StableValidationError("fromChain", fromChain, `Unsupported source chain: "${fromChain}"`);
|
|
1974
|
+
if (!dst) throw new StableValidationError("toChain", toChain, `Unsupported destination chain: "${toChain}"`);
|
|
1975
|
+
if (!src.oftAdapter) {
|
|
1976
|
+
throw new StableValidationError("fromChain", fromChain, `No OFT adapter configured for "${fromChain}"`);
|
|
1977
|
+
}
|
|
1978
|
+
if (fromToken.toLowerCase() !== src.usdt.toLowerCase()) {
|
|
1979
|
+
throw new StableValidationError(
|
|
1980
|
+
"fromToken",
|
|
1981
|
+
fromToken,
|
|
1982
|
+
`USDT0 bridge only moves USDT0 \u2014 fromToken must be ${src.usdt} on "${fromChain}", got "${fromToken}". Use route "lifi" to bridge other tokens.`
|
|
1983
|
+
);
|
|
1984
|
+
}
|
|
1985
|
+
if (toToken.toLowerCase() !== dst.usdt.toLowerCase()) {
|
|
1986
|
+
throw new StableValidationError(
|
|
1987
|
+
"toToken",
|
|
1988
|
+
toToken,
|
|
1989
|
+
`USDT0 bridge only moves USDT0 \u2014 toToken must be ${dst.usdt} on "${toChain}", got "${toToken}". Use route "lifi" to bridge other tokens.`
|
|
1990
|
+
);
|
|
1991
|
+
}
|
|
1992
|
+
return { src, dst };
|
|
1993
|
+
}
|
|
1994
|
+
buildSendParams(dst, amountLD, recipient) {
|
|
1995
|
+
return {
|
|
1996
|
+
dstEid: dst.lzEid,
|
|
1997
|
+
to: padHex(recipient, { dir: "left", size: 32 }),
|
|
1998
|
+
amountLD,
|
|
1999
|
+
minAmountLD: amountLD,
|
|
2000
|
+
extraOptions: buildLzOptions(),
|
|
2001
|
+
composeMsg: "0x",
|
|
2002
|
+
oftCmd: "0x"
|
|
2003
|
+
};
|
|
2004
|
+
}
|
|
2005
|
+
};
|
|
2006
|
+
|
|
1726
2007
|
// src/stable.ts
|
|
2008
|
+
function isUsdt0Route(fromChain, toChain, fromToken, toToken) {
|
|
2009
|
+
const src = BRIDGE_CHAIN_CONFIGS[fromChain];
|
|
2010
|
+
const dst = BRIDGE_CHAIN_CONFIGS[toChain];
|
|
2011
|
+
return !!src && !!dst && !!src.oftAdapter && src.lzOutbound !== false && src.usdt.toLowerCase() === fromToken.toLowerCase() && dst.usdt.toLowerCase() === toToken.toLowerCase();
|
|
2012
|
+
}
|
|
2013
|
+
function selectBridge(params) {
|
|
2014
|
+
const route = params.route ?? "auto";
|
|
2015
|
+
if (route === "usdt0") return "usdt0";
|
|
2016
|
+
if (route === "lifi") return "lifi";
|
|
2017
|
+
return isUsdt0Route(
|
|
2018
|
+
params.fromChain,
|
|
2019
|
+
params.toChain,
|
|
2020
|
+
params.fromToken,
|
|
2021
|
+
params.toToken
|
|
2022
|
+
) ? "usdt0" : "lifi";
|
|
2023
|
+
}
|
|
1727
2024
|
function validateTransferParams({
|
|
1728
2025
|
to,
|
|
1729
2026
|
amount,
|
|
1730
2027
|
token,
|
|
1731
2028
|
tokenDecimals
|
|
1732
2029
|
}) {
|
|
1733
|
-
if (!
|
|
2030
|
+
if (!isAddress5(to)) {
|
|
1734
2031
|
throw new StableValidationError(
|
|
1735
2032
|
"to",
|
|
1736
2033
|
to,
|
|
@@ -1744,7 +2041,7 @@ function validateTransferParams({
|
|
|
1744
2041
|
"transfer: amount must be a finite positive number"
|
|
1745
2042
|
);
|
|
1746
2043
|
}
|
|
1747
|
-
if (token !== void 0 && !
|
|
2044
|
+
if (token !== void 0 && !isAddress5(token)) {
|
|
1748
2045
|
throw new StableValidationError(
|
|
1749
2046
|
"token",
|
|
1750
2047
|
token,
|
|
@@ -1764,7 +2061,7 @@ async function resolveTokenDecimals(publicClient, token, tokenDecimals) {
|
|
|
1764
2061
|
try {
|
|
1765
2062
|
return await publicClient.readContract({
|
|
1766
2063
|
address: token,
|
|
1767
|
-
abi:
|
|
2064
|
+
abi: erc20Abi5,
|
|
1768
2065
|
functionName: "decimals"
|
|
1769
2066
|
});
|
|
1770
2067
|
} catch {
|
|
@@ -1791,9 +2088,9 @@ async function executeTokenTransfer(wc, publicClient, token, to, amount, tokenDe
|
|
|
1791
2088
|
);
|
|
1792
2089
|
return wc.writeContract({
|
|
1793
2090
|
address: token,
|
|
1794
|
-
abi:
|
|
2091
|
+
abi: erc20Abi5,
|
|
1795
2092
|
functionName: "transfer",
|
|
1796
|
-
args: [to,
|
|
2093
|
+
args: [to, parseUnits5(String(amount), decimals)],
|
|
1797
2094
|
account: acc,
|
|
1798
2095
|
chain
|
|
1799
2096
|
});
|
|
@@ -1814,6 +2111,7 @@ function createStable({
|
|
|
1814
2111
|
);
|
|
1815
2112
|
const signerConfig = { account, transport, walletClient };
|
|
1816
2113
|
const lifi = new LiFiService({ ...signerConfig, rpc, chainId: chain.id });
|
|
2114
|
+
const lz = new LayerZeroService(signerConfig);
|
|
1817
2115
|
const merkl = new MerklService({ ...signerConfig, rpc, chainId: chain.id, merklApiBase });
|
|
1818
2116
|
const morpho = earnConfig ? new MorphoService({
|
|
1819
2117
|
...signerConfig,
|
|
@@ -1846,10 +2144,10 @@ function createStable({
|
|
|
1846
2144
|
validateTransferParams(params);
|
|
1847
2145
|
const { from, to, amount, token, tokenDecimals } = params;
|
|
1848
2146
|
const acc = walletClient?.account ?? account ?? from;
|
|
1849
|
-
const wc = walletClient ??
|
|
2147
|
+
const wc = walletClient ?? createWalletClient5({
|
|
1850
2148
|
account: acc,
|
|
1851
2149
|
chain,
|
|
1852
|
-
transport: transport ??
|
|
2150
|
+
transport: transport ?? http6(rpc)
|
|
1853
2151
|
});
|
|
1854
2152
|
try {
|
|
1855
2153
|
await wc.switchChain({ id: chain.id });
|
|
@@ -1861,9 +2159,9 @@ function createStable({
|
|
|
1861
2159
|
cause: err
|
|
1862
2160
|
});
|
|
1863
2161
|
}
|
|
1864
|
-
const publicClient =
|
|
2162
|
+
const publicClient = createPublicClient6({
|
|
1865
2163
|
chain,
|
|
1866
|
-
transport:
|
|
2164
|
+
transport: http6(rpc)
|
|
1867
2165
|
});
|
|
1868
2166
|
const hash = token !== void 0 ? await executeTokenTransfer(
|
|
1869
2167
|
wc,
|
|
@@ -1881,7 +2179,7 @@ function createStable({
|
|
|
1881
2179
|
account: acc,
|
|
1882
2180
|
chain,
|
|
1883
2181
|
to,
|
|
1884
|
-
value:
|
|
2182
|
+
value: parseUnits5(String(amount), 18)
|
|
1885
2183
|
})
|
|
1886
2184
|
);
|
|
1887
2185
|
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
@@ -1897,8 +2195,8 @@ function createStable({
|
|
|
1897
2195
|
}
|
|
1898
2196
|
return {
|
|
1899
2197
|
transfer,
|
|
1900
|
-
quoteBridge: (params) => lifi.quoteBridge(params),
|
|
1901
|
-
bridge: (params) => lifi.bridge(params),
|
|
2198
|
+
quoteBridge: (params) => selectBridge(params) === "usdt0" ? lz.quoteBridge(params) : lifi.quoteBridge(params),
|
|
2199
|
+
bridge: (params) => selectBridge(params) === "usdt0" ? lz.bridge(params) : lifi.bridge(params),
|
|
1902
2200
|
quoteSwap: (params) => lifi.quoteSwap(params),
|
|
1903
2201
|
swap: (params) => lifi.swap(params),
|
|
1904
2202
|
earn: {
|
|
@@ -1941,6 +2239,7 @@ function createStableReader(config) {
|
|
|
1941
2239
|
var STABLE_USDT_ADDRESS = "0x779ded0c9e1022225f8e0630b35a9b54be713736";
|
|
1942
2240
|
var STABLE_VAULT_ADDRESS = "0xb7Df8db22A5DBBFA9ebeb94b3910aec6a4f05c08";
|
|
1943
2241
|
export {
|
|
2242
|
+
BRIDGE_CHAIN_CONFIGS,
|
|
1944
2243
|
CHAIN_CONFIGS,
|
|
1945
2244
|
Chain,
|
|
1946
2245
|
Network,
|