@anywalletkit/protocol-swap2-usdt0 0.2.1 → 0.4.0
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 +4 -2
- package/dist/activity.cjs +73 -4
- package/dist/activity.cjs.map +1 -1
- package/dist/activity.js +2 -2
- package/dist/chunk-M5SCA7EC.js +142 -0
- package/dist/chunk-M5SCA7EC.js.map +1 -0
- package/dist/{chunk-DGMQ563D.js → chunk-PUDGFJJG.js} +2 -2
- package/dist/{chunk-COIPAOOA.js → chunk-QSOGG65J.js} +8 -12
- package/dist/chunk-QSOGG65J.js.map +1 -0
- package/dist/{chunk-DDUVPDD6.js → chunk-TPBH2Z3B.js} +3 -3
- package/dist/config.cjs +74 -2
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts +29 -5
- package/dist/config.d.ts +29 -5
- package/dist/config.js +9 -3
- package/dist/index.cjs +99 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +12 -6
- package/dist/protocol.cjs +95 -31
- package/dist/protocol.cjs.map +1 -1
- package/dist/protocol.js +4 -4
- package/dist/provider.cjs +95 -31
- package/dist/provider.cjs.map +1 -1
- package/dist/provider.js +3 -3
- package/package.json +7 -7
- package/dist/chunk-7YL2Y3QA.js +0 -70
- package/dist/chunk-7YL2Y3QA.js.map +0 -1
- package/dist/chunk-COIPAOOA.js.map +0 -1
- /package/dist/{chunk-DGMQ563D.js.map → chunk-PUDGFJJG.js.map} +0 -0
- /package/dist/{chunk-DDUVPDD6.js.map → chunk-TPBH2Z3B.js.map} +0 -0
package/dist/config.cjs
CHANGED
|
@@ -24,11 +24,16 @@ __export(config_exports, {
|
|
|
24
24
|
USDT0_BY_EID: () => USDT0_BY_EID,
|
|
25
25
|
USDT0_CHAINS: () => USDT0_CHAINS,
|
|
26
26
|
USDT0_SYMBOLS: () => USDT0_SYMBOLS,
|
|
27
|
+
tokenMatches: () => tokenMatches,
|
|
27
28
|
usdt0: () => usdt0,
|
|
28
|
-
|
|
29
|
+
usdt0ClaimTargets: () => usdt0ClaimTargets,
|
|
30
|
+
usdt0Pins: () => usdt0Pins,
|
|
31
|
+
usdt0VerifyQuote: () => usdt0VerifyQuote
|
|
29
32
|
});
|
|
30
33
|
module.exports = __toCommonJS(config_exports);
|
|
34
|
+
var import_viem = require("viem");
|
|
31
35
|
var import_util = require("@anywalletkit/protocol-swap2-core/util");
|
|
36
|
+
var import_swap2_assets = require("@anywalletkit/swap2-assets");
|
|
32
37
|
var LZ_ENDPOINT_A = "0x1a44076050125825900e736c501f859c50fE728c";
|
|
33
38
|
var LZ_ENDPOINT_B = "0x6F475642a6e85809B1c36Fa62763669b1b48DD5B";
|
|
34
39
|
var c = (chainId, eid, name, token, oft, endpoint, adapter = false) => ({
|
|
@@ -74,6 +79,71 @@ var usdt0 = {
|
|
|
74
79
|
return from !== to && this.supportsChain(from) && this.supportsChain(to);
|
|
75
80
|
}
|
|
76
81
|
};
|
|
82
|
+
var OFT_SEND_ABI = (0, import_viem.parseAbi)([
|
|
83
|
+
"struct SendParam { uint32 dstEid; bytes32 to; uint256 amountLD; uint256 minAmountLD; bytes extraOptions; bytes composeMsg; bytes oftCmd; }",
|
|
84
|
+
"struct MessagingFee { uint256 nativeFee; uint256 lzTokenFee; }",
|
|
85
|
+
"function send(SendParam s, MessagingFee fee, address refundAddress) payable"
|
|
86
|
+
]);
|
|
87
|
+
function tokenMatches(chain, symbolOrAddress) {
|
|
88
|
+
const same = (a, b) => !!a && a.toLowerCase() === b.toLowerCase();
|
|
89
|
+
if (symbolOrAddress.startsWith("0x")) return same(symbolOrAddress, chain.token);
|
|
90
|
+
if (!USDT0_SYMBOLS.includes(symbolOrAddress.toUpperCase().replace("USD\u20AE", "USDT"))) return false;
|
|
91
|
+
const known = (0, import_swap2_assets.ccAsset)(chain.caip2, symbolOrAddress);
|
|
92
|
+
return !known || same(known.contract ?? "", chain.token);
|
|
93
|
+
}
|
|
94
|
+
function usdt0VerifyQuote(params, quote) {
|
|
95
|
+
const src = usdt0ChainFor(params.sourceChain);
|
|
96
|
+
const dst = usdt0ChainFor(params.destChain);
|
|
97
|
+
if (!src || !dst || src === dst) return "not a USDT0 route between two deployments";
|
|
98
|
+
if (!tokenMatches(src, params.sourceToken)) return `source token ${params.sourceToken} is not USDT0 (${src.token}) on ${src.name}`;
|
|
99
|
+
if (!tokenMatches(dst, params.destToken)) return `destination token ${params.destToken} is not USDT0 (${dst.token}) on ${dst.name}`;
|
|
100
|
+
const tx = quote.txRequest;
|
|
101
|
+
if (!tx?.data || !tx.to || tx.serializedTx !== void 0) return "the quote carries no OFT call";
|
|
102
|
+
if (tx.to.toLowerCase() !== src.oft.toLowerCase()) return `the call targets ${tx.to}, not the ${src.name} OFT`;
|
|
103
|
+
if (tx.chainId !== void 0 && tx.chainId !== src.chainId) return `the call is for chain ${tx.chainId}, not ${src.chainId}`;
|
|
104
|
+
if (src.adapter ? !tx.approvalAddress || tx.approvalAddress.toLowerCase() !== src.oft.toLowerCase() : tx.approvalAddress !== void 0) {
|
|
105
|
+
return src.adapter ? "the Ethereum send approves another spender than the adapter" : "an OFT send needs no approval";
|
|
106
|
+
}
|
|
107
|
+
let sp;
|
|
108
|
+
let fee;
|
|
109
|
+
let refund;
|
|
110
|
+
try {
|
|
111
|
+
const d = (0, import_viem.decodeFunctionData)({ abi: OFT_SEND_ABI, data: tx.data });
|
|
112
|
+
[sp, fee, refund] = d.args;
|
|
113
|
+
} catch {
|
|
114
|
+
return "the call is not send(SendParam, MessagingFee, refund)";
|
|
115
|
+
}
|
|
116
|
+
let amountIn, floor, value;
|
|
117
|
+
try {
|
|
118
|
+
amountIn = BigInt(params.amountIn);
|
|
119
|
+
floor = BigInt(quote.minDestAmount);
|
|
120
|
+
if (params.minAmountOut !== void 0 && BigInt(params.minAmountOut) > floor) floor = BigInt(params.minAmountOut);
|
|
121
|
+
value = BigInt(tx.value ?? "0");
|
|
122
|
+
} catch {
|
|
123
|
+
return "the request does not name amounts";
|
|
124
|
+
}
|
|
125
|
+
if (sp.dstEid !== dst.eid) return `the send goes to eid ${sp.dstEid}, not ${dst.name} (${dst.eid})`;
|
|
126
|
+
if (sp.amountLD !== amountIn || BigInt(quote.sourceAmount) !== amountIn) return `the send moves ${sp.amountLD}, not the ${amountIn} input`;
|
|
127
|
+
if (sp.minAmountLD > sp.amountLD || sp.minAmountLD <= 0n || sp.minAmountLD < floor) return `the send's minAmountLD ${sp.minAmountLD} is below the ${floor} minimum`;
|
|
128
|
+
if (sp.extraOptions !== "0x" || sp.composeMsg !== "0x" || sp.oftCmd !== "0x") return "the send carries options, a compose message or an OFT command";
|
|
129
|
+
const live = params.dry === false;
|
|
130
|
+
if (params.destAddress || live) {
|
|
131
|
+
if (!(0, import_viem.isAddress)(params.destAddress)) return "the recipient is not an EVM address";
|
|
132
|
+
if (sp.to.toLowerCase() !== (0, import_viem.pad)((0, import_viem.getAddress)(params.destAddress), { size: 32 }).toLowerCase()) return `the send delivers to ${sp.to}, not the recipient`;
|
|
133
|
+
}
|
|
134
|
+
if (params.refundAddress || live) {
|
|
135
|
+
if (!(0, import_viem.isAddress)(params.refundAddress ?? "")) return "the refund address is not an EVM address";
|
|
136
|
+
if ((0, import_viem.getAddress)(refund) !== (0, import_viem.getAddress)(params.refundAddress)) return `the send refunds ${refund}, not the trader`;
|
|
137
|
+
}
|
|
138
|
+
if (fee.lzTokenFee !== 0n) return "the send pays an LZ-token fee";
|
|
139
|
+
if (value !== fee.nativeFee || (tx.nativeFee ?? "0") !== fee.nativeFee.toString()) return "msg.value is not the declared native fee of the send";
|
|
140
|
+
if (quote.intentId.replace(/~[0-9a-f]{32}$/, "") !== `usdt0:${src.eid}:${dst.eid}`) return "the intent id does not name this send";
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
function usdt0ClaimTargets(chain) {
|
|
144
|
+
const c2 = usdt0ChainFor(chain);
|
|
145
|
+
return c2 ? [c2.endpoint] : [];
|
|
146
|
+
}
|
|
77
147
|
var usdt0Pins = Object.freeze({
|
|
78
148
|
callTargets: (chain) => {
|
|
79
149
|
const c2 = usdt0ChainFor(chain);
|
|
@@ -82,7 +152,9 @@ var usdt0Pins = Object.freeze({
|
|
|
82
152
|
spenders: (chain) => {
|
|
83
153
|
const c2 = usdt0ChainFor(chain);
|
|
84
154
|
return c2?.adapter ? [c2.oft] : [];
|
|
85
|
-
}
|
|
155
|
+
},
|
|
156
|
+
claimTargets: usdt0ClaimTargets,
|
|
157
|
+
verifyQuote: usdt0VerifyQuote
|
|
86
158
|
});
|
|
87
159
|
function usdt0ChainFor(chain) {
|
|
88
160
|
return Object.prototype.hasOwnProperty.call(USDT0_CHAINS, chain) ? USDT0_CHAINS[chain] : void 0;
|
package/dist/config.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/config.ts"],"sourcesContent":["// USDT0 — Tether's LayerZero OFT for USDT. Source: docs.usdt0.to\n// (technical-documentation/deployments, read 2026-10-20) — every row below was\n// then re-read on-chain over public RPCs the same day: the OFT's `token()` is\n// the listed token, `endpoint()` is the listed endpoint (and reports the\n// listed eid), `approvalRequired()` is true only for the Ethereum adapter,\n// `sharedDecimals()` = 6 everywhere, and `peers(eid)` are wired between the\n// chains. `oftVersion()` = (0x02e49c2c, 1) on all of them.\n//\n// Only chains the SDK ships (swap2-assets / wallet-evm) are listed. USDT0 is\n// also deployed on Conflux eSpace (1030, eid 30212), Flare (14, 30295),\n// Hedera (295, 30316), Mantle (5000, 30181), Morph (2818, 30322), Rootstock\n// (30, 30333), Stable (988, 30396), Tempo (4217, 30410), plus HyperCore, IOTA\n// and Stellar (non-EVM) — not wallet chains here, so not routable here.\nimport type {RemotePins} from \"@anywalletkit/protocol-swap2-core/types\";\nimport {evmRef, isEvm} from \"@anywalletkit/protocol-swap2-core/util\";\n\nexport interface Usdt0Chain {\n caip2: string;\n chainId: number;\n /** LayerZero V2 endpoint id. */\n eid: number;\n name: string;\n /** The ERC-20 the user holds: USDT on Ethereum, USDT0 elsewhere (6 decimals everywhere). */\n token: string;\n /**\n * The OFT contract `quoteSend` / `send` are called on. On Ethereum it is the\n * OFT *adapter* that locks USDT (`approvalRequired()` = true, approve the\n * token to it); elsewhere it is the OUpgradeable OFT that burns the caller's\n * USDT0 through the token's mint/burn role — no approval.\n */\n oft: string;\n /** LayerZero V2 EndpointV2 on that chain (`lzReceive` retries of a stored payload go here). */\n endpoint: string;\n /** True on Ethereum: the OFT is a lockbox adapter in front of plain USDT. */\n adapter: boolean;\n}\n\nconst LZ_ENDPOINT_A = \"0x1a44076050125825900e736c501f859c50fE728c\";\nconst LZ_ENDPOINT_B = \"0x6F475642a6e85809B1c36Fa62763669b1b48DD5B\";\n\nconst c = (chainId: number, eid: number, name: string, token: string, oft: string, endpoint: string, adapter = false): Usdt0Chain => ({\n caip2: `eip155:${chainId}`, chainId, eid, name, token, oft, endpoint, adapter,\n});\n\n/** CAIP-2 → USDT0 deployment. */\nexport const USDT0_CHAINS: Readonly<Record<string, Usdt0Chain>> = Object.freeze(Object.fromEntries([\n c(1, 30101, \"Ethereum\", \"0xdAC17F958D2ee523a2206206994597C13D831ec7\", \"0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee\", LZ_ENDPOINT_A, true),\n c(42161, 30110, \"Arbitrum\", \"0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9\", \"0x14E4A1B13bf7F943c8ff7C51fb60FA964A298D92\", LZ_ENDPOINT_A),\n c(10, 30111, \"OP Mainnet\", \"0x01bFF41798a0BcF287b996046Ca68b395DbC1071\", \"0xF03b4d9AC1D5d1E7c4cEf54C2A313b9fe051A0aD\", LZ_ENDPOINT_A),\n c(137, 30109, \"Polygon PoS\", \"0xc2132D05D31c914a87C6611C10748AEb04B58e8F\", \"0x6BA10300f0DC58B7a1e4c0e41f5daBb7D7829e13\", LZ_ENDPOINT_A),\n c(999, 30367, \"HyperEVM\", \"0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb\", \"0x904861a24F30EC96ea7CFC3bE9EA4B476d237e98\", \"0x3A73033C0b1407574C76BdBAc67f126f6b4a9AA9\"),\n c(57073, 30339, \"Ink\", \"0x0200C29006150606B650577BBE7B6248F58470c1\", \"0x1cB6De532588fCA4a21B7209DE7C456AF8434A65\", \"0xca29f3A6f966Cb2fc0dE625F8f325c0C46dbE958\"),\n c(80094, 30362, \"Berachain\", \"0x779Ded0c9e1022225f8E0630b35a9b54bE713736\", \"0x3Dc96399109df5ceb2C226664A086140bD0379cB\", LZ_ENDPOINT_B),\n c(130, 30320, \"Unichain\", \"0x9151434b16b9763660705744891fA906F660EcC5\", \"0xc07bE8994D035631c36fb4a89C918CeFB2f03EC3\", LZ_ENDPOINT_B),\n c(1329, 30280, \"Sei\", \"0x9151434b16b9763660705744891fA906F660EcC5\", \"0x56Fe74A2e3b484b921c447357203431a3485CC60\", LZ_ENDPOINT_A),\n c(9745, 30383, \"Plasma\", \"0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb\", \"0x02ca37966753bDdDf11216B73B16C1dE756A7CF9\", LZ_ENDPOINT_B),\n c(143, 30390, \"Monad\", \"0xe7cd86e13AC4309349F30B3435a9d337750fC82D\", \"0x9151434b16b9763660705744891fA906F660EcC5\", LZ_ENDPOINT_B),\n c(4326, 30398, \"MegaETH\", \"0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb\", \"0x9151434b16b9763660705744891fA906F660EcC5\", LZ_ENDPOINT_B),\n c(196, 30274, \"X Layer\", \"0x779Ded0c9e1022225f8E0630b35a9b54bE713736\", \"0x94bcCa6BdfD6a61817aB0E960BfedE4984505554\", LZ_ENDPOINT_A),\n // 2026-10-20 (evm-chains-6 batch): docs.usdt0.to deployments, re-read on-chain (token(), approvalRequired() = false,\n // sharedDecimals() = 6, endpoint(), peers(30110) = the Arbitrum OFT).\n c(5000, 30181, \"Mantle\", \"0x779Ded0c9e1022225f8E0630b35a9b54bE713736\", \"0xcb768e263FB1C62214E7cab4AA8d036D76dc59CC\", LZ_ENDPOINT_A),\n c(2818, 30322, \"Morph\", \"0xe7cd86e13AC4309349F30B3435a9d337750fC82D\", \"0xcb768e263FB1C62214E7cab4AA8d036D76dc59CC\", LZ_ENDPOINT_B),\n c(14, 30295, \"Flare\", \"0xe7cd86e13AC4309349F30B3435a9d337750fC82D\", \"0x567287d2A9829215a37e3B88843d32f9221E7588\", LZ_ENDPOINT_A),\n].map((x) => [x.caip2, x])));\n\n/** LayerZero eid → deployment. */\nexport const USDT0_BY_EID: Readonly<Record<number, Usdt0Chain>> = Object.freeze(\n Object.fromEntries(Object.values(USDT0_CHAINS).map((x) => [x.eid, x])),\n);\n\n/** LayerZero Scan — message status by source transaction hash. */\nexport const LZ_SCAN_API = \"https://scan.layerzero-api.com\";\n\n/** Token symbols the catalog uses for the same asset across chains. */\nexport const USDT0_SYMBOLS: readonly string[] = Object.freeze([\"USDT0\", \"USDT\", \"USD₮0\", \"USD₮\"]);\n\nexport const usdt0 = {\n supportsChain(chain: string): boolean {\n return isEvm(chain) && !!USDT0_CHAINS[`eip155:${evmRef(chain)}`];\n },\n supports(from: string, to: string): boolean {\n return from !== to && this.supportsChain(from) && this.supportsChain(to);\n },\n};\n\n/**\n * The USDT0 OFT a send calls on a chain, and the adapter it approves where one locks USDT (Ethereum) —\n * the app's pins for `remoteProvider({name: \"usdt0\", baseUrl, ...usdt0Pins})` (scan 2026-10-27 modules, F8/F16).\n */\nexport const usdt0Pins: RemotePins = Object.freeze({\n callTargets: (chain: string): readonly string[] => { const c = usdt0ChainFor(chain); return c ? [c.oft] : []; },\n spenders: (chain: string): readonly string[] => { const c = usdt0ChainFor(chain); return c?.adapter ? [c.oft] : []; },\n});\nfunction usdt0ChainFor(chain: string): Usdt0Chain | undefined {\n return Object.prototype.hasOwnProperty.call(USDT0_CHAINS, chain) ? USDT0_CHAINS[chain] : undefined;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAcA,kBAA4B;AAuB5B,IAAM,gBAAgB;AACtB,IAAM,gBAAgB;AAEtB,IAAM,IAAI,CAAC,SAAiB,KAAa,MAAc,OAAe,KAAa,UAAkB,UAAU,WAAuB;AAAA,EACpI,OAAO,UAAU,OAAO;AAAA,EAAI;AAAA,EAAS;AAAA,EAAK;AAAA,EAAM;AAAA,EAAO;AAAA,EAAK;AAAA,EAAU;AACxE;AAGO,IAAM,eAAqD,OAAO,OAAO,OAAO,YAAY;AAAA,EACjG,EAAE,GAAG,OAAO,YAAY,8CAA8C,8CAA8C,eAAe,IAAI;AAAA,EACvI,EAAE,OAAO,OAAO,YAAY,8CAA8C,8CAA8C,aAAa;AAAA,EACrI,EAAE,IAAI,OAAO,cAAc,8CAA8C,8CAA8C,aAAa;AAAA,EACpI,EAAE,KAAK,OAAO,eAAe,8CAA8C,8CAA8C,aAAa;AAAA,EACtI,EAAE,KAAK,OAAO,YAAY,8CAA8C,8CAA8C,4CAA4C;AAAA,EAClK,EAAE,OAAO,OAAO,OAAO,8CAA8C,8CAA8C,4CAA4C;AAAA,EAC/J,EAAE,OAAO,OAAO,aAAa,8CAA8C,8CAA8C,aAAa;AAAA,EACtI,EAAE,KAAK,OAAO,YAAY,8CAA8C,8CAA8C,aAAa;AAAA,EACnI,EAAE,MAAM,OAAO,OAAO,8CAA8C,8CAA8C,aAAa;AAAA,EAC/H,EAAE,MAAM,OAAO,UAAU,8CAA8C,8CAA8C,aAAa;AAAA,EAClI,EAAE,KAAK,OAAO,SAAS,8CAA8C,8CAA8C,aAAa;AAAA,EAChI,EAAE,MAAM,OAAO,WAAW,8CAA8C,8CAA8C,aAAa;AAAA,EACnI,EAAE,KAAK,OAAO,WAAW,8CAA8C,8CAA8C,aAAa;AAAA;AAAA;AAAA,EAGlI,EAAE,KAAM,OAAO,UAAU,8CAA8C,8CAA8C,aAAa;AAAA,EAClI,EAAE,MAAM,OAAO,SAAS,8CAA8C,8CAA8C,aAAa;AAAA,EACjI,EAAE,IAAI,OAAO,SAAS,8CAA8C,8CAA8C,aAAa;AACjI,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;AAGpB,IAAM,eAAqD,OAAO;AAAA,EACvE,OAAO,YAAY,OAAO,OAAO,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;AACvE;AAGO,IAAM,cAAc;AAGpB,IAAM,gBAAmC,OAAO,OAAO,CAAC,SAAS,QAAQ,cAAS,WAAM,CAAC;AAEzF,IAAM,QAAQ;AAAA,EACnB,cAAc,OAAwB;AACpC,eAAO,mBAAM,KAAK,KAAK,CAAC,CAAC,aAAa,cAAU,oBAAO,KAAK,CAAC,EAAE;AAAA,EACjE;AAAA,EACA,SAAS,MAAc,IAAqB;AAC1C,WAAO,SAAS,MAAM,KAAK,cAAc,IAAI,KAAK,KAAK,cAAc,EAAE;AAAA,EACzE;AACF;AAMO,IAAM,YAAwB,OAAO,OAAO;AAAA,EACjD,aAAa,CAAC,UAAqC;AAAE,UAAMA,KAAI,cAAc,KAAK;AAAG,WAAOA,KAAI,CAACA,GAAE,GAAG,IAAI,CAAC;AAAA,EAAG;AAAA,EAC9G,UAAU,CAAC,UAAqC;AAAE,UAAMA,KAAI,cAAc,KAAK;AAAG,WAAOA,IAAG,UAAU,CAACA,GAAE,GAAG,IAAI,CAAC;AAAA,EAAG;AACtH,CAAC;AACD,SAAS,cAAc,OAAuC;AAC5D,SAAO,OAAO,UAAU,eAAe,KAAK,cAAc,KAAK,IAAI,aAAa,KAAK,IAAI;AAC3F;","names":["c"]}
|
|
1
|
+
{"version":3,"sources":["../src/config.ts"],"sourcesContent":["// USDT0 — Tether's LayerZero OFT for USDT. Source: docs.usdt0.to\n// (technical-documentation/deployments, read 2026-10-20) — every row below was\n// then re-read on-chain over public RPCs the same day: the OFT's `token()` is\n// the listed token, `endpoint()` is the listed endpoint (and reports the\n// listed eid), `approvalRequired()` is true only for the Ethereum adapter,\n// `sharedDecimals()` = 6 everywhere, and `peers(eid)` are wired between the\n// chains. `oftVersion()` = (0x02e49c2c, 1) on all of them.\n//\n// Only chains the SDK ships (swap2-assets / wallet-evm) are listed. USDT0 is\n// also deployed on Conflux eSpace (1030, eid 30212), Flare (14, 30295),\n// Hedera (295, 30316), Mantle (5000, 30181), Morph (2818, 30322), Rootstock\n// (30, 30333), Stable (988, 30396), Tempo (4217, 30410), plus HyperCore, IOTA\n// and Stellar (non-EVM) — not wallet chains here, so not routable here.\nimport {decodeFunctionData, getAddress, isAddress, pad, parseAbi, type Hex} from \"viem\";\nimport type {Quote, QuoteParams, RemotePins} from \"@anywalletkit/protocol-swap2-core/types\";\nimport {evmRef, isEvm} from \"@anywalletkit/protocol-swap2-core/util\";\nimport {ccAsset} from \"@anywalletkit/swap2-assets\";\n\nexport interface Usdt0Chain {\n caip2: string;\n chainId: number;\n /** LayerZero V2 endpoint id. */\n eid: number;\n name: string;\n /** The ERC-20 the user holds: USDT on Ethereum, USDT0 elsewhere (6 decimals everywhere). */\n token: string;\n /**\n * The OFT contract `quoteSend` / `send` are called on. On Ethereum it is the\n * OFT *adapter* that locks USDT (`approvalRequired()` = true, approve the\n * token to it); elsewhere it is the OUpgradeable OFT that burns the caller's\n * USDT0 through the token's mint/burn role — no approval.\n */\n oft: string;\n /** LayerZero V2 EndpointV2 on that chain (`lzReceive` retries of a stored payload go here). */\n endpoint: string;\n /** True on Ethereum: the OFT is a lockbox adapter in front of plain USDT. */\n adapter: boolean;\n}\n\nconst LZ_ENDPOINT_A = \"0x1a44076050125825900e736c501f859c50fE728c\";\nconst LZ_ENDPOINT_B = \"0x6F475642a6e85809B1c36Fa62763669b1b48DD5B\";\n\nconst c = (chainId: number, eid: number, name: string, token: string, oft: string, endpoint: string, adapter = false): Usdt0Chain => ({\n caip2: `eip155:${chainId}`, chainId, eid, name, token, oft, endpoint, adapter,\n});\n\n/** CAIP-2 → USDT0 deployment. */\nexport const USDT0_CHAINS: Readonly<Record<string, Usdt0Chain>> = Object.freeze(Object.fromEntries([\n c(1, 30101, \"Ethereum\", \"0xdAC17F958D2ee523a2206206994597C13D831ec7\", \"0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee\", LZ_ENDPOINT_A, true),\n c(42161, 30110, \"Arbitrum\", \"0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9\", \"0x14E4A1B13bf7F943c8ff7C51fb60FA964A298D92\", LZ_ENDPOINT_A),\n c(10, 30111, \"OP Mainnet\", \"0x01bFF41798a0BcF287b996046Ca68b395DbC1071\", \"0xF03b4d9AC1D5d1E7c4cEf54C2A313b9fe051A0aD\", LZ_ENDPOINT_A),\n c(137, 30109, \"Polygon PoS\", \"0xc2132D05D31c914a87C6611C10748AEb04B58e8F\", \"0x6BA10300f0DC58B7a1e4c0e41f5daBb7D7829e13\", LZ_ENDPOINT_A),\n c(999, 30367, \"HyperEVM\", \"0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb\", \"0x904861a24F30EC96ea7CFC3bE9EA4B476d237e98\", \"0x3A73033C0b1407574C76BdBAc67f126f6b4a9AA9\"),\n c(57073, 30339, \"Ink\", \"0x0200C29006150606B650577BBE7B6248F58470c1\", \"0x1cB6De532588fCA4a21B7209DE7C456AF8434A65\", \"0xca29f3A6f966Cb2fc0dE625F8f325c0C46dbE958\"),\n c(80094, 30362, \"Berachain\", \"0x779Ded0c9e1022225f8E0630b35a9b54bE713736\", \"0x3Dc96399109df5ceb2C226664A086140bD0379cB\", LZ_ENDPOINT_B),\n c(130, 30320, \"Unichain\", \"0x9151434b16b9763660705744891fA906F660EcC5\", \"0xc07bE8994D035631c36fb4a89C918CeFB2f03EC3\", LZ_ENDPOINT_B),\n c(1329, 30280, \"Sei\", \"0x9151434b16b9763660705744891fA906F660EcC5\", \"0x56Fe74A2e3b484b921c447357203431a3485CC60\", LZ_ENDPOINT_A),\n c(9745, 30383, \"Plasma\", \"0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb\", \"0x02ca37966753bDdDf11216B73B16C1dE756A7CF9\", LZ_ENDPOINT_B),\n c(143, 30390, \"Monad\", \"0xe7cd86e13AC4309349F30B3435a9d337750fC82D\", \"0x9151434b16b9763660705744891fA906F660EcC5\", LZ_ENDPOINT_B),\n c(4326, 30398, \"MegaETH\", \"0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb\", \"0x9151434b16b9763660705744891fA906F660EcC5\", LZ_ENDPOINT_B),\n c(196, 30274, \"X Layer\", \"0x779Ded0c9e1022225f8E0630b35a9b54bE713736\", \"0x94bcCa6BdfD6a61817aB0E960BfedE4984505554\", LZ_ENDPOINT_A),\n // 2026-10-20 (evm-chains-6 batch): docs.usdt0.to deployments, re-read on-chain (token(), approvalRequired() = false,\n // sharedDecimals() = 6, endpoint(), peers(30110) = the Arbitrum OFT).\n c(5000, 30181, \"Mantle\", \"0x779Ded0c9e1022225f8E0630b35a9b54bE713736\", \"0xcb768e263FB1C62214E7cab4AA8d036D76dc59CC\", LZ_ENDPOINT_A),\n c(2818, 30322, \"Morph\", \"0xe7cd86e13AC4309349F30B3435a9d337750fC82D\", \"0xcb768e263FB1C62214E7cab4AA8d036D76dc59CC\", LZ_ENDPOINT_B),\n c(14, 30295, \"Flare\", \"0xe7cd86e13AC4309349F30B3435a9d337750fC82D\", \"0x567287d2A9829215a37e3B88843d32f9221E7588\", LZ_ENDPOINT_A),\n].map((x) => [x.caip2, x])));\n\n/** LayerZero eid → deployment. */\nexport const USDT0_BY_EID: Readonly<Record<number, Usdt0Chain>> = Object.freeze(\n Object.fromEntries(Object.values(USDT0_CHAINS).map((x) => [x.eid, x])),\n);\n\n/** LayerZero Scan — message status by source transaction hash. */\nexport const LZ_SCAN_API = \"https://scan.layerzero-api.com\";\n\n/** Token symbols the catalog uses for the same asset across chains. */\nexport const USDT0_SYMBOLS: readonly string[] = Object.freeze([\"USDT0\", \"USDT\", \"USD₮0\", \"USD₮\"]);\n\nexport const usdt0 = {\n supportsChain(chain: string): boolean {\n return isEvm(chain) && !!USDT0_CHAINS[`eip155:${evmRef(chain)}`];\n },\n supports(from: string, to: string): boolean {\n return from !== to && this.supportsChain(from) && this.supportsChain(to);\n },\n};\n\nconst OFT_SEND_ABI = parseAbi([\n \"struct SendParam { uint32 dstEid; bytes32 to; uint256 amountLD; uint256 minAmountLD; bytes extraOptions; bytes composeMsg; bytes oftCmd; }\",\n \"struct MessagingFee { uint256 nativeFee; uint256 lzTokenFee; }\",\n \"function send(SendParam s, MessagingFee fee, address refundAddress) payable\",\n]);\n\n/**\n * Does `symbolOrAddress` name this deployment's token on `chain`? The contract itself, or a USDT / USDT0 symbol the\n * catalog does not bind to another contract (OP's legacy bridged USDT is not the OFT token). The provider and\n * {@link usdt0VerifyQuote} both check the request with it.\n */\nexport function tokenMatches(chain: Usdt0Chain, symbolOrAddress: string): boolean {\n const same = (a: string | undefined | null, b: string) => !!a && a.toLowerCase() === b.toLowerCase();\n if (symbolOrAddress.startsWith(\"0x\")) return same(symbolOrAddress, chain.token);\n if (!USDT0_SYMBOLS.includes(symbolOrAddress.toUpperCase().replace(\"USD₮\", \"USDT\"))) return false;\n const known = ccAsset(chain.caip2, symbolOrAddress);\n return !known || same(known.contract ?? \"\", chain.token);\n}\n\n/**\n * A remote USDT0 quote's `send`, decoded on the device against the app's own request (scan 2026-10-30, F3): exactly\n * `send(SendParam, MessagingFee, refund)` on the source OFT; the destination chain's eid; the recipient as `to`; the\n * input as `amountLD`; a `minAmountLD` no larger than the input that guarantees the quoted minimum and the caller's\n * floor; empty `extraOptions`, `composeMsg` and `oftCmd` (what the provider sends); no LZ-token fee; the trader as\n * refund; `msg.value` = the declared native fee = the call's `nativeFee`; the adapter as spender on Ethereum only.\n * The reason it fails, or null. The engine's native-fee ceiling bounds the fee itself.\n */\nexport function usdt0VerifyQuote(params: QuoteParams, quote: Quote): string | null {\n const src = usdt0ChainFor(params.sourceChain);\n const dst = usdt0ChainFor(params.destChain);\n if (!src || !dst || src === dst) return \"not a USDT0 route between two deployments\";\n // The OFT burns or locks its own token whatever the request named, so both sides are bound here (reaudit 2026-10-30, #1).\n if (!tokenMatches(src, params.sourceToken)) return `source token ${params.sourceToken} is not USDT0 (${src.token}) on ${src.name}`;\n if (!tokenMatches(dst, params.destToken)) return `destination token ${params.destToken} is not USDT0 (${dst.token}) on ${dst.name}`;\n const tx = quote.txRequest;\n if (!tx?.data || !tx.to || tx.serializedTx !== undefined) return \"the quote carries no OFT call\";\n if (tx.to.toLowerCase() !== src.oft.toLowerCase()) return `the call targets ${tx.to}, not the ${src.name} OFT`;\n if (tx.chainId !== undefined && tx.chainId !== src.chainId) return `the call is for chain ${tx.chainId}, not ${src.chainId}`;\n if (src.adapter ? !tx.approvalAddress || tx.approvalAddress.toLowerCase() !== src.oft.toLowerCase() : tx.approvalAddress !== undefined) {\n return src.adapter ? \"the Ethereum send approves another spender than the adapter\" : \"an OFT send needs no approval\";\n }\n let sp: {dstEid: number; to: Hex; amountLD: bigint; minAmountLD: bigint; extraOptions: Hex; composeMsg: Hex; oftCmd: Hex};\n let fee: {nativeFee: bigint; lzTokenFee: bigint};\n let refund: string;\n try {\n const d = decodeFunctionData({abi: OFT_SEND_ABI, data: tx.data as Hex});\n [sp, fee, refund] = d.args as unknown as [typeof sp, typeof fee, string];\n } catch {\n return \"the call is not send(SendParam, MessagingFee, refund)\";\n }\n let amountIn: bigint, floor: bigint, value: bigint;\n try {\n amountIn = BigInt(params.amountIn);\n floor = BigInt(quote.minDestAmount);\n if (params.minAmountOut !== undefined && BigInt(params.minAmountOut) > floor) floor = BigInt(params.minAmountOut);\n value = BigInt(tx.value ?? \"0\");\n } catch {\n return \"the request does not name amounts\";\n }\n if (sp.dstEid !== dst.eid) return `the send goes to eid ${sp.dstEid}, not ${dst.name} (${dst.eid})`;\n if (sp.amountLD !== amountIn || BigInt(quote.sourceAmount) !== amountIn) return `the send moves ${sp.amountLD}, not the ${amountIn} input`;\n if (sp.minAmountLD > sp.amountLD || sp.minAmountLD <= 0n || sp.minAmountLD < floor) return `the send's minAmountLD ${sp.minAmountLD} is below the ${floor} minimum`;\n if (sp.extraOptions !== \"0x\" || sp.composeMsg !== \"0x\" || sp.oftCmd !== \"0x\") return \"the send carries options, a compose message or an OFT command\";\n // A dry quote without addresses prices the provider's placeholders; it is never built (the engine re-quotes live).\n const live = params.dry === false;\n if (params.destAddress || live) {\n if (!isAddress(params.destAddress)) return \"the recipient is not an EVM address\";\n if (sp.to.toLowerCase() !== pad(getAddress(params.destAddress), {size: 32}).toLowerCase()) return `the send delivers to ${sp.to}, not the recipient`;\n }\n if (params.refundAddress || live) {\n if (!isAddress(params.refundAddress ?? \"\")) return \"the refund address is not an EVM address\";\n if (getAddress(refund) !== getAddress(params.refundAddress!)) return `the send refunds ${refund}, not the trader`;\n }\n if (fee.lzTokenFee !== 0n) return \"the send pays an LZ-token fee\";\n if (value !== fee.nativeFee || (tx.nativeFee ?? \"0\") !== fee.nativeFee.toString()) return \"msg.value is not the declared native fee of the send\";\n if (quote.intentId.replace(/~[0-9a-f]{32}$/, \"\") !== `usdt0:${src.eid}:${dst.eid}`) return \"the intent id does not name this send\";\n return null;\n}\n\n/**\n * The contracts a USDT0 claim may call on `chain`: its LayerZero EndpointV2 (`lzReceive`, retrying a stored\n * payload). Empty for a chain USDT0 is not listed on.\n */\nexport function usdt0ClaimTargets(chain: string): readonly string[] {\n const c = usdt0ChainFor(chain);\n return c ? [c.endpoint] : [];\n}\n\n/**\n * The USDT0 OFT a send calls on a chain, the adapter it approves where one locks USDT (Ethereum), the endpoint\n * a stored-payload claim calls, and {@link usdt0VerifyQuote} — the app's pins for\n * `remoteProvider({name: \"usdt0\", baseUrl, ...usdt0Pins})` (scan 2026-10-27 modules, F8/F16; the send's\n * arguments since scan 2026-10-30, F3).\n */\nexport const usdt0Pins: RemotePins & {claimTargets(chain: string): readonly string[]} = Object.freeze({\n callTargets: (chain: string): readonly string[] => { const c = usdt0ChainFor(chain); return c ? [c.oft] : []; },\n spenders: (chain: string): readonly string[] => { const c = usdt0ChainFor(chain); return c?.adapter ? [c.oft] : []; },\n claimTargets: usdt0ClaimTargets,\n verifyQuote: usdt0VerifyQuote,\n});\nfunction usdt0ChainFor(chain: string): Usdt0Chain | undefined {\n return Object.prototype.hasOwnProperty.call(USDT0_CHAINS, chain) ? USDT0_CHAINS[chain] : undefined;\n}\n"],"mappings":"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","names":["c"]}
|
package/dist/config.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RemotePins } from '@anywalletkit/protocol-swap2-core/types';
|
|
1
|
+
import { RemotePins, QuoteParams, Quote } from '@anywalletkit/protocol-swap2-core/types';
|
|
2
2
|
|
|
3
3
|
interface Usdt0Chain {
|
|
4
4
|
caip2: string;
|
|
@@ -33,9 +33,33 @@ declare const usdt0: {
|
|
|
33
33
|
supports(from: string, to: string): boolean;
|
|
34
34
|
};
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
36
|
+
* Does `symbolOrAddress` name this deployment's token on `chain`? The contract itself, or a USDT / USDT0 symbol the
|
|
37
|
+
* catalog does not bind to another contract (OP's legacy bridged USDT is not the OFT token). The provider and
|
|
38
|
+
* {@link usdt0VerifyQuote} both check the request with it.
|
|
38
39
|
*/
|
|
39
|
-
declare
|
|
40
|
+
declare function tokenMatches(chain: Usdt0Chain, symbolOrAddress: string): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* A remote USDT0 quote's `send`, decoded on the device against the app's own request (scan 2026-10-30, F3): exactly
|
|
43
|
+
* `send(SendParam, MessagingFee, refund)` on the source OFT; the destination chain's eid; the recipient as `to`; the
|
|
44
|
+
* input as `amountLD`; a `minAmountLD` no larger than the input that guarantees the quoted minimum and the caller's
|
|
45
|
+
* floor; empty `extraOptions`, `composeMsg` and `oftCmd` (what the provider sends); no LZ-token fee; the trader as
|
|
46
|
+
* refund; `msg.value` = the declared native fee = the call's `nativeFee`; the adapter as spender on Ethereum only.
|
|
47
|
+
* The reason it fails, or null. The engine's native-fee ceiling bounds the fee itself.
|
|
48
|
+
*/
|
|
49
|
+
declare function usdt0VerifyQuote(params: QuoteParams, quote: Quote): string | null;
|
|
50
|
+
/**
|
|
51
|
+
* The contracts a USDT0 claim may call on `chain`: its LayerZero EndpointV2 (`lzReceive`, retrying a stored
|
|
52
|
+
* payload). Empty for a chain USDT0 is not listed on.
|
|
53
|
+
*/
|
|
54
|
+
declare function usdt0ClaimTargets(chain: string): readonly string[];
|
|
55
|
+
/**
|
|
56
|
+
* The USDT0 OFT a send calls on a chain, the adapter it approves where one locks USDT (Ethereum), the endpoint
|
|
57
|
+
* a stored-payload claim calls, and {@link usdt0VerifyQuote} — the app's pins for
|
|
58
|
+
* `remoteProvider({name: "usdt0", baseUrl, ...usdt0Pins})` (scan 2026-10-27 modules, F8/F16; the send's
|
|
59
|
+
* arguments since scan 2026-10-30, F3).
|
|
60
|
+
*/
|
|
61
|
+
declare const usdt0Pins: RemotePins & {
|
|
62
|
+
claimTargets(chain: string): readonly string[];
|
|
63
|
+
};
|
|
40
64
|
|
|
41
|
-
export { LZ_SCAN_API, USDT0_BY_EID, USDT0_CHAINS, USDT0_SYMBOLS, type Usdt0Chain, usdt0, usdt0Pins };
|
|
65
|
+
export { LZ_SCAN_API, USDT0_BY_EID, USDT0_CHAINS, USDT0_SYMBOLS, type Usdt0Chain, tokenMatches, usdt0, usdt0ClaimTargets, usdt0Pins, usdt0VerifyQuote };
|
package/dist/config.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RemotePins } from '@anywalletkit/protocol-swap2-core/types';
|
|
1
|
+
import { RemotePins, QuoteParams, Quote } from '@anywalletkit/protocol-swap2-core/types';
|
|
2
2
|
|
|
3
3
|
interface Usdt0Chain {
|
|
4
4
|
caip2: string;
|
|
@@ -33,9 +33,33 @@ declare const usdt0: {
|
|
|
33
33
|
supports(from: string, to: string): boolean;
|
|
34
34
|
};
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
36
|
+
* Does `symbolOrAddress` name this deployment's token on `chain`? The contract itself, or a USDT / USDT0 symbol the
|
|
37
|
+
* catalog does not bind to another contract (OP's legacy bridged USDT is not the OFT token). The provider and
|
|
38
|
+
* {@link usdt0VerifyQuote} both check the request with it.
|
|
38
39
|
*/
|
|
39
|
-
declare
|
|
40
|
+
declare function tokenMatches(chain: Usdt0Chain, symbolOrAddress: string): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* A remote USDT0 quote's `send`, decoded on the device against the app's own request (scan 2026-10-30, F3): exactly
|
|
43
|
+
* `send(SendParam, MessagingFee, refund)` on the source OFT; the destination chain's eid; the recipient as `to`; the
|
|
44
|
+
* input as `amountLD`; a `minAmountLD` no larger than the input that guarantees the quoted minimum and the caller's
|
|
45
|
+
* floor; empty `extraOptions`, `composeMsg` and `oftCmd` (what the provider sends); no LZ-token fee; the trader as
|
|
46
|
+
* refund; `msg.value` = the declared native fee = the call's `nativeFee`; the adapter as spender on Ethereum only.
|
|
47
|
+
* The reason it fails, or null. The engine's native-fee ceiling bounds the fee itself.
|
|
48
|
+
*/
|
|
49
|
+
declare function usdt0VerifyQuote(params: QuoteParams, quote: Quote): string | null;
|
|
50
|
+
/**
|
|
51
|
+
* The contracts a USDT0 claim may call on `chain`: its LayerZero EndpointV2 (`lzReceive`, retrying a stored
|
|
52
|
+
* payload). Empty for a chain USDT0 is not listed on.
|
|
53
|
+
*/
|
|
54
|
+
declare function usdt0ClaimTargets(chain: string): readonly string[];
|
|
55
|
+
/**
|
|
56
|
+
* The USDT0 OFT a send calls on a chain, the adapter it approves where one locks USDT (Ethereum), the endpoint
|
|
57
|
+
* a stored-payload claim calls, and {@link usdt0VerifyQuote} — the app's pins for
|
|
58
|
+
* `remoteProvider({name: "usdt0", baseUrl, ...usdt0Pins})` (scan 2026-10-27 modules, F8/F16; the send's
|
|
59
|
+
* arguments since scan 2026-10-30, F3).
|
|
60
|
+
*/
|
|
61
|
+
declare const usdt0Pins: RemotePins & {
|
|
62
|
+
claimTargets(chain: string): readonly string[];
|
|
63
|
+
};
|
|
40
64
|
|
|
41
|
-
export { LZ_SCAN_API, USDT0_BY_EID, USDT0_CHAINS, USDT0_SYMBOLS, type Usdt0Chain, usdt0, usdt0Pins };
|
|
65
|
+
export { LZ_SCAN_API, USDT0_BY_EID, USDT0_CHAINS, USDT0_SYMBOLS, type Usdt0Chain, tokenMatches, usdt0, usdt0ClaimTargets, usdt0Pins, usdt0VerifyQuote };
|
package/dist/config.js
CHANGED
|
@@ -3,15 +3,21 @@ import {
|
|
|
3
3
|
USDT0_BY_EID,
|
|
4
4
|
USDT0_CHAINS,
|
|
5
5
|
USDT0_SYMBOLS,
|
|
6
|
+
tokenMatches,
|
|
6
7
|
usdt0,
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
usdt0ClaimTargets,
|
|
9
|
+
usdt0Pins,
|
|
10
|
+
usdt0VerifyQuote
|
|
11
|
+
} from "./chunk-M5SCA7EC.js";
|
|
9
12
|
export {
|
|
10
13
|
LZ_SCAN_API,
|
|
11
14
|
USDT0_BY_EID,
|
|
12
15
|
USDT0_CHAINS,
|
|
13
16
|
USDT0_SYMBOLS,
|
|
17
|
+
tokenMatches,
|
|
14
18
|
usdt0,
|
|
15
|
-
|
|
19
|
+
usdt0ClaimTargets,
|
|
20
|
+
usdt0Pins,
|
|
21
|
+
usdt0VerifyQuote
|
|
16
22
|
};
|
|
17
23
|
//# sourceMappingURL=config.js.map
|
package/dist/index.cjs
CHANGED
|
@@ -32,14 +32,19 @@ __export(src_exports, {
|
|
|
32
32
|
Usdt0Swap2Protocol: () => Usdt0Swap2Protocol,
|
|
33
33
|
default: () => Usdt0Swap2Protocol,
|
|
34
34
|
describeUsdt0Activity: () => describeUsdt0Activity,
|
|
35
|
+
tokenMatches: () => tokenMatches,
|
|
35
36
|
usdt0: () => usdt0,
|
|
37
|
+
usdt0ClaimTargets: () => usdt0ClaimTargets,
|
|
36
38
|
usdt0Pins: () => usdt0Pins,
|
|
37
|
-
usdt0Provider: () => usdt0Provider
|
|
39
|
+
usdt0Provider: () => usdt0Provider,
|
|
40
|
+
usdt0VerifyQuote: () => usdt0VerifyQuote
|
|
38
41
|
});
|
|
39
42
|
module.exports = __toCommonJS(src_exports);
|
|
40
43
|
|
|
41
44
|
// src/config.ts
|
|
45
|
+
var import_viem = require("viem");
|
|
42
46
|
var import_util = require("@anywalletkit/protocol-swap2-core/util");
|
|
47
|
+
var import_swap2_assets = require("@anywalletkit/swap2-assets");
|
|
43
48
|
var LZ_ENDPOINT_A = "0x1a44076050125825900e736c501f859c50fE728c";
|
|
44
49
|
var LZ_ENDPOINT_B = "0x6F475642a6e85809B1c36Fa62763669b1b48DD5B";
|
|
45
50
|
var c = (chainId, eid, name, token, oft, endpoint, adapter = false) => ({
|
|
@@ -85,6 +90,71 @@ var usdt0 = {
|
|
|
85
90
|
return from !== to && this.supportsChain(from) && this.supportsChain(to);
|
|
86
91
|
}
|
|
87
92
|
};
|
|
93
|
+
var OFT_SEND_ABI = (0, import_viem.parseAbi)([
|
|
94
|
+
"struct SendParam { uint32 dstEid; bytes32 to; uint256 amountLD; uint256 minAmountLD; bytes extraOptions; bytes composeMsg; bytes oftCmd; }",
|
|
95
|
+
"struct MessagingFee { uint256 nativeFee; uint256 lzTokenFee; }",
|
|
96
|
+
"function send(SendParam s, MessagingFee fee, address refundAddress) payable"
|
|
97
|
+
]);
|
|
98
|
+
function tokenMatches(chain, symbolOrAddress) {
|
|
99
|
+
const same2 = (a, b) => !!a && a.toLowerCase() === b.toLowerCase();
|
|
100
|
+
if (symbolOrAddress.startsWith("0x")) return same2(symbolOrAddress, chain.token);
|
|
101
|
+
if (!USDT0_SYMBOLS.includes(symbolOrAddress.toUpperCase().replace("USD\u20AE", "USDT"))) return false;
|
|
102
|
+
const known = (0, import_swap2_assets.ccAsset)(chain.caip2, symbolOrAddress);
|
|
103
|
+
return !known || same2(known.contract ?? "", chain.token);
|
|
104
|
+
}
|
|
105
|
+
function usdt0VerifyQuote(params, quote) {
|
|
106
|
+
const src = usdt0ChainFor(params.sourceChain);
|
|
107
|
+
const dst = usdt0ChainFor(params.destChain);
|
|
108
|
+
if (!src || !dst || src === dst) return "not a USDT0 route between two deployments";
|
|
109
|
+
if (!tokenMatches(src, params.sourceToken)) return `source token ${params.sourceToken} is not USDT0 (${src.token}) on ${src.name}`;
|
|
110
|
+
if (!tokenMatches(dst, params.destToken)) return `destination token ${params.destToken} is not USDT0 (${dst.token}) on ${dst.name}`;
|
|
111
|
+
const tx = quote.txRequest;
|
|
112
|
+
if (!tx?.data || !tx.to || tx.serializedTx !== void 0) return "the quote carries no OFT call";
|
|
113
|
+
if (tx.to.toLowerCase() !== src.oft.toLowerCase()) return `the call targets ${tx.to}, not the ${src.name} OFT`;
|
|
114
|
+
if (tx.chainId !== void 0 && tx.chainId !== src.chainId) return `the call is for chain ${tx.chainId}, not ${src.chainId}`;
|
|
115
|
+
if (src.adapter ? !tx.approvalAddress || tx.approvalAddress.toLowerCase() !== src.oft.toLowerCase() : tx.approvalAddress !== void 0) {
|
|
116
|
+
return src.adapter ? "the Ethereum send approves another spender than the adapter" : "an OFT send needs no approval";
|
|
117
|
+
}
|
|
118
|
+
let sp;
|
|
119
|
+
let fee;
|
|
120
|
+
let refund;
|
|
121
|
+
try {
|
|
122
|
+
const d = (0, import_viem.decodeFunctionData)({ abi: OFT_SEND_ABI, data: tx.data });
|
|
123
|
+
[sp, fee, refund] = d.args;
|
|
124
|
+
} catch {
|
|
125
|
+
return "the call is not send(SendParam, MessagingFee, refund)";
|
|
126
|
+
}
|
|
127
|
+
let amountIn, floor, value;
|
|
128
|
+
try {
|
|
129
|
+
amountIn = BigInt(params.amountIn);
|
|
130
|
+
floor = BigInt(quote.minDestAmount);
|
|
131
|
+
if (params.minAmountOut !== void 0 && BigInt(params.minAmountOut) > floor) floor = BigInt(params.minAmountOut);
|
|
132
|
+
value = BigInt(tx.value ?? "0");
|
|
133
|
+
} catch {
|
|
134
|
+
return "the request does not name amounts";
|
|
135
|
+
}
|
|
136
|
+
if (sp.dstEid !== dst.eid) return `the send goes to eid ${sp.dstEid}, not ${dst.name} (${dst.eid})`;
|
|
137
|
+
if (sp.amountLD !== amountIn || BigInt(quote.sourceAmount) !== amountIn) return `the send moves ${sp.amountLD}, not the ${amountIn} input`;
|
|
138
|
+
if (sp.minAmountLD > sp.amountLD || sp.minAmountLD <= 0n || sp.minAmountLD < floor) return `the send's minAmountLD ${sp.minAmountLD} is below the ${floor} minimum`;
|
|
139
|
+
if (sp.extraOptions !== "0x" || sp.composeMsg !== "0x" || sp.oftCmd !== "0x") return "the send carries options, a compose message or an OFT command";
|
|
140
|
+
const live = params.dry === false;
|
|
141
|
+
if (params.destAddress || live) {
|
|
142
|
+
if (!(0, import_viem.isAddress)(params.destAddress)) return "the recipient is not an EVM address";
|
|
143
|
+
if (sp.to.toLowerCase() !== (0, import_viem.pad)((0, import_viem.getAddress)(params.destAddress), { size: 32 }).toLowerCase()) return `the send delivers to ${sp.to}, not the recipient`;
|
|
144
|
+
}
|
|
145
|
+
if (params.refundAddress || live) {
|
|
146
|
+
if (!(0, import_viem.isAddress)(params.refundAddress ?? "")) return "the refund address is not an EVM address";
|
|
147
|
+
if ((0, import_viem.getAddress)(refund) !== (0, import_viem.getAddress)(params.refundAddress)) return `the send refunds ${refund}, not the trader`;
|
|
148
|
+
}
|
|
149
|
+
if (fee.lzTokenFee !== 0n) return "the send pays an LZ-token fee";
|
|
150
|
+
if (value !== fee.nativeFee || (tx.nativeFee ?? "0") !== fee.nativeFee.toString()) return "msg.value is not the declared native fee of the send";
|
|
151
|
+
if (quote.intentId.replace(/~[0-9a-f]{32}$/, "") !== `usdt0:${src.eid}:${dst.eid}`) return "the intent id does not name this send";
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
function usdt0ClaimTargets(chain) {
|
|
155
|
+
const c2 = usdt0ChainFor(chain);
|
|
156
|
+
return c2 ? [c2.endpoint] : [];
|
|
157
|
+
}
|
|
88
158
|
var usdt0Pins = Object.freeze({
|
|
89
159
|
callTargets: (chain) => {
|
|
90
160
|
const c2 = usdt0ChainFor(chain);
|
|
@@ -93,19 +163,21 @@ var usdt0Pins = Object.freeze({
|
|
|
93
163
|
spenders: (chain) => {
|
|
94
164
|
const c2 = usdt0ChainFor(chain);
|
|
95
165
|
return c2?.adapter ? [c2.oft] : [];
|
|
96
|
-
}
|
|
166
|
+
},
|
|
167
|
+
claimTargets: usdt0ClaimTargets,
|
|
168
|
+
verifyQuote: usdt0VerifyQuote
|
|
97
169
|
});
|
|
98
170
|
function usdt0ChainFor(chain) {
|
|
99
171
|
return Object.prototype.hasOwnProperty.call(USDT0_CHAINS, chain) ? USDT0_CHAINS[chain] : void 0;
|
|
100
172
|
}
|
|
101
173
|
|
|
102
174
|
// src/provider.ts
|
|
103
|
-
var
|
|
175
|
+
var import_viem3 = require("viem");
|
|
104
176
|
|
|
105
177
|
// src/activity.ts
|
|
106
178
|
var import_activity = require("@anywalletkit/protocol-swap2-core/activity");
|
|
107
|
-
var
|
|
108
|
-
var SEND_ABI = (0,
|
|
179
|
+
var import_viem2 = require("viem");
|
|
180
|
+
var SEND_ABI = (0, import_viem2.parseAbi)([
|
|
109
181
|
"struct SendParam { uint32 dstEid; bytes32 to; uint256 amountLD; uint256 minAmountLD; bytes extraOptions; bytes composeMsg; bytes oftCmd; }",
|
|
110
182
|
"struct MessagingFee { uint256 nativeFee; uint256 lzTokenFee; }",
|
|
111
183
|
"function send(SendParam sp, MessagingFee fee, address refundAddress)"
|
|
@@ -121,7 +193,7 @@ function sendDestination(tx) {
|
|
|
121
193
|
const data = tx.call?.data;
|
|
122
194
|
if (!data || data.length < 10 + 64 * 2) return null;
|
|
123
195
|
try {
|
|
124
|
-
const [sp] = (0,
|
|
196
|
+
const [sp] = (0, import_viem2.decodeFunctionData)({ abi: SEND_ABI, data }).args;
|
|
125
197
|
return USDT0_BY_EID[sp.dstEid] ?? null;
|
|
126
198
|
} catch {
|
|
127
199
|
return null;
|
|
@@ -159,9 +231,9 @@ function describeUsdt0Activity(tx, ctx) {
|
|
|
159
231
|
}
|
|
160
232
|
|
|
161
233
|
// src/provider.ts
|
|
162
|
-
var
|
|
234
|
+
var import_swap2_assets2 = require("@anywalletkit/swap2-assets");
|
|
163
235
|
var import_util2 = require("@anywalletkit/protocol-swap2-core/util");
|
|
164
|
-
var OFT_ABI = (0,
|
|
236
|
+
var OFT_ABI = (0, import_viem3.parseAbi)([
|
|
165
237
|
"struct SendParam { uint32 dstEid; bytes32 to; uint256 amountLD; uint256 minAmountLD; bytes extraOptions; bytes composeMsg; bytes oftCmd; }",
|
|
166
238
|
"struct MessagingFee { uint256 nativeFee; uint256 lzTokenFee; }",
|
|
167
239
|
"struct OFTLimit { uint256 minAmountLD; uint256 maxAmountLD; }",
|
|
@@ -175,7 +247,7 @@ var OFT_ABI = (0, import_viem2.parseAbi)([
|
|
|
175
247
|
"function approvalRequired() view returns (bool)",
|
|
176
248
|
"function peers(uint32 eid) view returns (bytes32)"
|
|
177
249
|
]);
|
|
178
|
-
var ENDPOINT_ABI = (0,
|
|
250
|
+
var ENDPOINT_ABI = (0, import_viem3.parseAbi)([
|
|
179
251
|
"struct Origin { uint32 srcEid; bytes32 sender; uint64 nonce; }",
|
|
180
252
|
"function lzReceive(Origin origin, address receiver, bytes32 guid, bytes message, bytes extraData) payable"
|
|
181
253
|
]);
|
|
@@ -189,12 +261,6 @@ var GAS_CLAIM = "300000";
|
|
|
189
261
|
var ZERO_B32 = `0x${"00".repeat(32)}`;
|
|
190
262
|
var same = (a, b) => !!a && a.toLowerCase() === b.toLowerCase();
|
|
191
263
|
var isHexData = (v) => typeof v === "string" && /^0x(?:[0-9a-fA-F]{2})*$/.test(v);
|
|
192
|
-
function tokenMatches(chain, symbolOrAddress) {
|
|
193
|
-
if (symbolOrAddress.startsWith("0x")) return same(symbolOrAddress, chain.token);
|
|
194
|
-
if (!USDT0_SYMBOLS.includes(symbolOrAddress.toUpperCase().replace("USD\u20AE", "USDT"))) return false;
|
|
195
|
-
const known = (0, import_swap2_assets.ccAsset)(chain.caip2, symbolOrAddress);
|
|
196
|
-
return !known || same(known.contract ?? "", chain.token);
|
|
197
|
-
}
|
|
198
264
|
function rpcSafeReason(err, chainName) {
|
|
199
265
|
const e = err;
|
|
200
266
|
const text = `${e?.shortMessage ?? ""}
|
|
@@ -217,7 +283,7 @@ function usdt0Provider(config = {}) {
|
|
|
217
283
|
const urls = typeof raw === "string" ? [raw] : [...raw ?? []];
|
|
218
284
|
if (urls.length === 0) return null;
|
|
219
285
|
const opts = { timeout: RPC_TIMEOUT_MS, ...config.fetchImpl ? { fetchFn: config.fetchImpl } : {} };
|
|
220
|
-
return (0,
|
|
286
|
+
return (0, import_viem3.createPublicClient)({ transport: urls.length === 1 ? (0, import_viem3.http)(urls[0], opts) : (0, import_viem3.fallback)(urls.map((u) => (0, import_viem3.http)(u, opts))), ccipRead: false });
|
|
221
287
|
};
|
|
222
288
|
const verified = /* @__PURE__ */ new Map();
|
|
223
289
|
function verifyChain(chain) {
|
|
@@ -227,8 +293,8 @@ function usdt0Provider(config = {}) {
|
|
|
227
293
|
const c2 = client(chain.caip2);
|
|
228
294
|
if (!c2) throw new Error(`usdt0: no RPC for ${chain.caip2}`);
|
|
229
295
|
const [token, approval] = await Promise.all([
|
|
230
|
-
c2.readContract({ address: (0,
|
|
231
|
-
c2.readContract({ address: (0,
|
|
296
|
+
c2.readContract({ address: (0, import_viem3.getAddress)(chain.oft), abi: OFT_ABI, functionName: "token" }),
|
|
297
|
+
c2.readContract({ address: (0, import_viem3.getAddress)(chain.oft), abi: OFT_ABI, functionName: "approvalRequired" })
|
|
232
298
|
]);
|
|
233
299
|
if (!same(token, chain.token)) throw new Error(`usdt0: OFT ${chain.oft} on ${chain.name} reports token ${token}, expected ${chain.token}`);
|
|
234
300
|
if (approval !== chain.adapter) throw new Error(`usdt0: OFT ${chain.oft} on ${chain.name} reports approvalRequired=${String(approval)}, expected ${String(chain.adapter)}`);
|
|
@@ -246,8 +312,8 @@ function usdt0Provider(config = {}) {
|
|
|
246
312
|
p = (async () => {
|
|
247
313
|
const c2 = client(src.caip2);
|
|
248
314
|
if (!c2) throw new Error(`usdt0: no RPC for ${src.caip2}`);
|
|
249
|
-
const peer = await c2.readContract({ address: (0,
|
|
250
|
-
if (!same(peer, (0,
|
|
315
|
+
const peer = await c2.readContract({ address: (0, import_viem3.getAddress)(src.oft), abi: OFT_ABI, functionName: "peers", args: [dst.eid] });
|
|
316
|
+
if (!same(peer, (0, import_viem3.pad)((0, import_viem3.getAddress)(dst.oft), { size: 32 }))) throw new Error(`usdt0: ${src.name} OFT has no peer for ${dst.name} (eid ${dst.eid}) \u2014 route not wired`);
|
|
251
317
|
})();
|
|
252
318
|
p.catch(() => peers.delete(key));
|
|
253
319
|
peers.set(key, p);
|
|
@@ -263,7 +329,7 @@ function usdt0Provider(config = {}) {
|
|
|
263
329
|
}
|
|
264
330
|
const sendParam = (dstEid, recipient, amountLD, minAmountLD) => ({
|
|
265
331
|
dstEid,
|
|
266
|
-
to: (0,
|
|
332
|
+
to: (0, import_viem3.pad)((0, import_viem3.getAddress)(recipient), { size: 32 }),
|
|
267
333
|
amountLD,
|
|
268
334
|
minAmountLD,
|
|
269
335
|
extraOptions: "0x",
|
|
@@ -283,6 +349,7 @@ function usdt0Provider(config = {}) {
|
|
|
283
349
|
return {
|
|
284
350
|
name: "usdt0",
|
|
285
351
|
describeActivity: describeUsdt0Activity,
|
|
352
|
+
claimTargets: usdt0ClaimTargets,
|
|
286
353
|
enabled: () => config.enabled ?? true,
|
|
287
354
|
supportsChain: (chain) => usdt0.supportsChain(chain) && client(chain) !== null,
|
|
288
355
|
supportsChainPair: (from, to) => usdt0.supports(from, to) && client(from) !== null,
|
|
@@ -310,16 +377,16 @@ function usdt0Provider(config = {}) {
|
|
|
310
377
|
const dry = params.dry !== false;
|
|
311
378
|
const recipient = params.destAddress || (dry ? EVM_PLACEHOLDER : "");
|
|
312
379
|
if (!recipient) return refuse("a live quote needs a recipient");
|
|
313
|
-
if (!(0,
|
|
380
|
+
if (!(0, import_viem3.isAddress)(recipient)) return refuse("destination must be an EVM address");
|
|
314
381
|
const refund = params.refundAddress || (dry ? EVM_PLACEHOLDER : "");
|
|
315
382
|
if (!refund) return refuse("a live quote needs a refund address");
|
|
316
|
-
if (!(0,
|
|
383
|
+
if (!(0, import_viem3.isAddress)(refund)) return refuse("refund address must be an EVM address");
|
|
317
384
|
const c2 = client(src.caip2);
|
|
318
385
|
if (!c2) return refuse(`no RPC configured for ${src.caip2}`);
|
|
319
386
|
try {
|
|
320
387
|
await Promise.all([verifyChain(src), verifyPeer(src, dst)]);
|
|
321
388
|
const probe = sendParam(dst.eid, recipient, amount, 0n);
|
|
322
|
-
const [limit, feeDetails, receipt] = await c2.readContract({ address: (0,
|
|
389
|
+
const [limit, feeDetails, receipt] = await c2.readContract({ address: (0, import_viem3.getAddress)(src.oft), abi: OFT_ABI, functionName: "quoteOFT", args: [probe] });
|
|
323
390
|
if (amount < limit.minAmountLD) return refuse(`amount ${amount} is below the OFT minimum ${limit.minAmountLD}`);
|
|
324
391
|
if (amount > limit.maxAmountLD) return refuse(`amount ${amount} exceeds the OFT maximum ${limit.maxAmountLD}`);
|
|
325
392
|
const received = receipt.amountReceivedLD;
|
|
@@ -327,10 +394,10 @@ function usdt0Provider(config = {}) {
|
|
|
327
394
|
if (received > amount) return refuse("OFT reports more received than sent \u2014 refusing");
|
|
328
395
|
if (params.minAmountOut !== void 0 && received < BigInt(params.minAmountOut)) return refuse(`received ${received} is below the caller's floor ${params.minAmountOut}`);
|
|
329
396
|
const sp = sendParam(dst.eid, recipient, amount, received);
|
|
330
|
-
const fee = await c2.readContract({ address: (0,
|
|
397
|
+
const fee = await c2.readContract({ address: (0, import_viem3.getAddress)(src.oft), abi: OFT_ABI, functionName: "quoteSend", args: [sp, false] });
|
|
331
398
|
if (fee.lzTokenFee !== 0n) return refuse("OFT asks for a fee in LZ token \u2014 unsupported");
|
|
332
399
|
const nativeFee = fee.nativeFee + fee.nativeFee * FEE_HEADROOM_BPS / 10000n;
|
|
333
|
-
const data = (0,
|
|
400
|
+
const data = (0, import_viem3.encodeFunctionData)({ abi: OFT_ABI, functionName: "send", args: [sp, { nativeFee, lzTokenFee: 0n }, (0, import_viem3.getAddress)(refund)] });
|
|
334
401
|
const protocolFees = feeDetails.filter((f) => f.feeAmountLD > 0n).map((f) => ({ type: "protocol", amount: f.feeAmountLD.toString(), token: src.token, included: true, description: f.description || "OFT fee" }));
|
|
335
402
|
const feeUsd = protocolFees.reduce((a, f) => a + Number(f.amount) / 1e6, 0);
|
|
336
403
|
const eta = src.chainId === 1 ? 1e3 : 240;
|
|
@@ -361,8 +428,8 @@ function usdt0Provider(config = {}) {
|
|
|
361
428
|
dstEid: String(dst.eid),
|
|
362
429
|
oft: src.oft,
|
|
363
430
|
token: src.token,
|
|
364
|
-
recipient: (0,
|
|
365
|
-
refundAddress: (0,
|
|
431
|
+
recipient: (0, import_viem3.getAddress)(recipient),
|
|
432
|
+
refundAddress: (0, import_viem3.getAddress)(refund),
|
|
366
433
|
amountLD: amount.toString(),
|
|
367
434
|
minAmountLD: received.toString(),
|
|
368
435
|
nativeFee: nativeFee.toString()
|
|
@@ -399,7 +466,7 @@ function usdt0Provider(config = {}) {
|
|
|
399
466
|
const { src, dst } = parsed;
|
|
400
467
|
if (!same(tx.to, src.oft)) throw new Error(`usdt0: deposit target ${tx.to} is not the ${src.name} OFT`);
|
|
401
468
|
const pd = quote.providerData ?? {};
|
|
402
|
-
if (!/^\d+$/.test(pd.amountLD ?? "") || !/^\d+$/.test(pd.minAmountLD ?? "") || !/^\d+$/.test(pd.nativeFee ?? "") || !(0,
|
|
469
|
+
if (!/^\d+$/.test(pd.amountLD ?? "") || !/^\d+$/.test(pd.minAmountLD ?? "") || !/^\d+$/.test(pd.nativeFee ?? "") || !(0, import_viem3.isAddress)(pd.recipient ?? "")) {
|
|
403
470
|
throw new Error("usdt0: quote carries no sealed send parameters");
|
|
404
471
|
}
|
|
405
472
|
const c2 = client(src.caip2);
|
|
@@ -408,7 +475,7 @@ function usdt0Provider(config = {}) {
|
|
|
408
475
|
let fee;
|
|
409
476
|
try {
|
|
410
477
|
await Promise.all([verifyChain(src), verifyPeer(src, dst)]);
|
|
411
|
-
fee = await c2.readContract({ address: (0,
|
|
478
|
+
fee = await c2.readContract({ address: (0, import_viem3.getAddress)(src.oft), abi: OFT_ABI, functionName: "quoteSend", args: [sp, false] });
|
|
412
479
|
} catch (err) {
|
|
413
480
|
throw new Error(rpcSafeReason(err, src.name));
|
|
414
481
|
}
|
|
@@ -460,10 +527,10 @@ function usdt0Provider(config = {}) {
|
|
|
460
527
|
chain: dst.caip2,
|
|
461
528
|
to: dst.endpoint,
|
|
462
529
|
value,
|
|
463
|
-
data: (0,
|
|
530
|
+
data: (0, import_viem3.encodeFunctionData)({
|
|
464
531
|
abi: ENDPOINT_ABI,
|
|
465
532
|
functionName: "lzReceive",
|
|
466
|
-
args: [{ srcEid: src.eid, sender: (0,
|
|
533
|
+
args: [{ srcEid: src.eid, sender: (0, import_viem3.pad)((0, import_viem3.getAddress)(src.oft), { size: 32 }), nonce: BigInt(nonce) }, (0, import_viem3.getAddress)(dst.oft), guid, message, "0x"]
|
|
467
534
|
}),
|
|
468
535
|
chainId: dst.chainId,
|
|
469
536
|
gasLimit: GAS_CLAIM
|