@anywalletkit/protocol-swap2-usdt0 0.0.0-stage → 0.1.2

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.
@@ -0,0 +1,34 @@
1
+ interface Usdt0Chain {
2
+ caip2: string;
3
+ chainId: number;
4
+ /** LayerZero V2 endpoint id. */
5
+ eid: number;
6
+ name: string;
7
+ /** The ERC-20 the user holds: USDT on Ethereum, USDT0 elsewhere (6 decimals everywhere). */
8
+ token: string;
9
+ /**
10
+ * The OFT contract `quoteSend` / `send` are called on. On Ethereum it is the
11
+ * OFT *adapter* that locks USDT (`approvalRequired()` = true, approve the
12
+ * token to it); elsewhere it is the OUpgradeable OFT that burns the caller's
13
+ * USDT0 through the token's mint/burn role — no approval.
14
+ */
15
+ oft: string;
16
+ /** LayerZero V2 EndpointV2 on that chain (`lzReceive` retries of a stored payload go here). */
17
+ endpoint: string;
18
+ /** True on Ethereum: the OFT is a lockbox adapter in front of plain USDT. */
19
+ adapter: boolean;
20
+ }
21
+ /** CAIP-2 → USDT0 deployment. */
22
+ declare const USDT0_CHAINS: Readonly<Record<string, Usdt0Chain>>;
23
+ /** LayerZero eid → deployment. */
24
+ declare const USDT0_BY_EID: Readonly<Record<number, Usdt0Chain>>;
25
+ /** LayerZero Scan — message status by source transaction hash. */
26
+ declare const LZ_SCAN_API = "https://scan.layerzero-api.com";
27
+ /** Token symbols the catalog uses for the same asset across chains. */
28
+ declare const USDT0_SYMBOLS: readonly string[];
29
+ declare const usdt0: {
30
+ supportsChain(chain: string): boolean;
31
+ supports(from: string, to: string): boolean;
32
+ };
33
+
34
+ export { LZ_SCAN_API, USDT0_BY_EID, USDT0_CHAINS, USDT0_SYMBOLS, type Usdt0Chain, usdt0 };
@@ -0,0 +1,34 @@
1
+ interface Usdt0Chain {
2
+ caip2: string;
3
+ chainId: number;
4
+ /** LayerZero V2 endpoint id. */
5
+ eid: number;
6
+ name: string;
7
+ /** The ERC-20 the user holds: USDT on Ethereum, USDT0 elsewhere (6 decimals everywhere). */
8
+ token: string;
9
+ /**
10
+ * The OFT contract `quoteSend` / `send` are called on. On Ethereum it is the
11
+ * OFT *adapter* that locks USDT (`approvalRequired()` = true, approve the
12
+ * token to it); elsewhere it is the OUpgradeable OFT that burns the caller's
13
+ * USDT0 through the token's mint/burn role — no approval.
14
+ */
15
+ oft: string;
16
+ /** LayerZero V2 EndpointV2 on that chain (`lzReceive` retries of a stored payload go here). */
17
+ endpoint: string;
18
+ /** True on Ethereum: the OFT is a lockbox adapter in front of plain USDT. */
19
+ adapter: boolean;
20
+ }
21
+ /** CAIP-2 → USDT0 deployment. */
22
+ declare const USDT0_CHAINS: Readonly<Record<string, Usdt0Chain>>;
23
+ /** LayerZero eid → deployment. */
24
+ declare const USDT0_BY_EID: Readonly<Record<number, Usdt0Chain>>;
25
+ /** LayerZero Scan — message status by source transaction hash. */
26
+ declare const LZ_SCAN_API = "https://scan.layerzero-api.com";
27
+ /** Token symbols the catalog uses for the same asset across chains. */
28
+ declare const USDT0_SYMBOLS: readonly string[];
29
+ declare const usdt0: {
30
+ supportsChain(chain: string): boolean;
31
+ supports(from: string, to: string): boolean;
32
+ };
33
+
34
+ export { LZ_SCAN_API, USDT0_BY_EID, USDT0_CHAINS, USDT0_SYMBOLS, type Usdt0Chain, usdt0 };
package/dist/config.js ADDED
@@ -0,0 +1,15 @@
1
+ import {
2
+ LZ_SCAN_API,
3
+ USDT0_BY_EID,
4
+ USDT0_CHAINS,
5
+ USDT0_SYMBOLS,
6
+ usdt0
7
+ } from "./chunk-AGN4M7OO.js";
8
+ export {
9
+ LZ_SCAN_API,
10
+ USDT0_BY_EID,
11
+ USDT0_CHAINS,
12
+ USDT0_SYMBOLS,
13
+ usdt0
14
+ };
15
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/dist/index.cjs ADDED
@@ -0,0 +1,485 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ LZ_RECEIVE: () => LZ_RECEIVE,
24
+ LZ_SCAN_API: () => LZ_SCAN_API,
25
+ USDT0_BY_EID: () => USDT0_BY_EID,
26
+ USDT0_CHAINS: () => USDT0_CHAINS,
27
+ USDT0_MODULE: () => USDT0_MODULE,
28
+ USDT0_SEND: () => USDT0_SEND,
29
+ USDT0_STATUS_MAP: () => USDT0_STATUS_MAP,
30
+ USDT0_SYMBOLS: () => USDT0_SYMBOLS,
31
+ USDT0_ZERO_PEER: () => ZERO_B32,
32
+ Usdt0Swap2Protocol: () => Usdt0Swap2Protocol,
33
+ default: () => Usdt0Swap2Protocol,
34
+ describeUsdt0Activity: () => describeUsdt0Activity,
35
+ usdt0: () => usdt0,
36
+ usdt0Provider: () => usdt0Provider
37
+ });
38
+ module.exports = __toCommonJS(src_exports);
39
+
40
+ // src/config.ts
41
+ var import_util = require("@anywalletkit/protocol-swap2-core/util");
42
+ var LZ_ENDPOINT_A = "0x1a44076050125825900e736c501f859c50fE728c";
43
+ var LZ_ENDPOINT_B = "0x6F475642a6e85809B1c36Fa62763669b1b48DD5B";
44
+ var c = (chainId, eid, name, token, oft, endpoint, adapter = false) => ({
45
+ caip2: `eip155:${chainId}`,
46
+ chainId,
47
+ eid,
48
+ name,
49
+ token,
50
+ oft,
51
+ endpoint,
52
+ adapter
53
+ });
54
+ var USDT0_CHAINS = Object.freeze(Object.fromEntries([
55
+ c(1, 30101, "Ethereum", "0xdAC17F958D2ee523a2206206994597C13D831ec7", "0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee", LZ_ENDPOINT_A, true),
56
+ c(42161, 30110, "Arbitrum", "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9", "0x14E4A1B13bf7F943c8ff7C51fb60FA964A298D92", LZ_ENDPOINT_A),
57
+ c(10, 30111, "OP Mainnet", "0x01bFF41798a0BcF287b996046Ca68b395DbC1071", "0xF03b4d9AC1D5d1E7c4cEf54C2A313b9fe051A0aD", LZ_ENDPOINT_A),
58
+ c(137, 30109, "Polygon PoS", "0xc2132D05D31c914a87C6611C10748AEb04B58e8F", "0x6BA10300f0DC58B7a1e4c0e41f5daBb7D7829e13", LZ_ENDPOINT_A),
59
+ c(999, 30367, "HyperEVM", "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb", "0x904861a24F30EC96ea7CFC3bE9EA4B476d237e98", "0x3A73033C0b1407574C76BdBAc67f126f6b4a9AA9"),
60
+ c(57073, 30339, "Ink", "0x0200C29006150606B650577BBE7B6248F58470c1", "0x1cB6De532588fCA4a21B7209DE7C456AF8434A65", "0xca29f3A6f966Cb2fc0dE625F8f325c0C46dbE958"),
61
+ c(80094, 30362, "Berachain", "0x779Ded0c9e1022225f8E0630b35a9b54bE713736", "0x3Dc96399109df5ceb2C226664A086140bD0379cB", LZ_ENDPOINT_B),
62
+ c(130, 30320, "Unichain", "0x9151434b16b9763660705744891fA906F660EcC5", "0xc07bE8994D035631c36fb4a89C918CeFB2f03EC3", LZ_ENDPOINT_B),
63
+ c(1329, 30280, "Sei", "0x9151434b16b9763660705744891fA906F660EcC5", "0x56Fe74A2e3b484b921c447357203431a3485CC60", LZ_ENDPOINT_A),
64
+ c(9745, 30383, "Plasma", "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb", "0x02ca37966753bDdDf11216B73B16C1dE756A7CF9", LZ_ENDPOINT_B),
65
+ c(143, 30390, "Monad", "0xe7cd86e13AC4309349F30B3435a9d337750fC82D", "0x9151434b16b9763660705744891fA906F660EcC5", LZ_ENDPOINT_B),
66
+ c(4326, 30398, "MegaETH", "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb", "0x9151434b16b9763660705744891fA906F660EcC5", LZ_ENDPOINT_B),
67
+ c(196, 30274, "X Layer", "0x779Ded0c9e1022225f8E0630b35a9b54bE713736", "0x94bcCa6BdfD6a61817aB0E960BfedE4984505554", LZ_ENDPOINT_A),
68
+ // 2026-10-20 (evm-chains-6 batch): docs.usdt0.to deployments, re-read on-chain (token(), approvalRequired() = false,
69
+ // sharedDecimals() = 6, endpoint(), peers(30110) = the Arbitrum OFT).
70
+ c(5e3, 30181, "Mantle", "0x779Ded0c9e1022225f8E0630b35a9b54bE713736", "0xcb768e263FB1C62214E7cab4AA8d036D76dc59CC", LZ_ENDPOINT_A),
71
+ c(2818, 30322, "Morph", "0xe7cd86e13AC4309349F30B3435a9d337750fC82D", "0xcb768e263FB1C62214E7cab4AA8d036D76dc59CC", LZ_ENDPOINT_B),
72
+ c(14, 30295, "Flare", "0xe7cd86e13AC4309349F30B3435a9d337750fC82D", "0x567287d2A9829215a37e3B88843d32f9221E7588", LZ_ENDPOINT_A)
73
+ ].map((x) => [x.caip2, x])));
74
+ var USDT0_BY_EID = Object.freeze(
75
+ Object.fromEntries(Object.values(USDT0_CHAINS).map((x) => [x.eid, x]))
76
+ );
77
+ var LZ_SCAN_API = "https://scan.layerzero-api.com";
78
+ var USDT0_SYMBOLS = Object.freeze(["USDT0", "USDT", "USD\u20AE0", "USD\u20AE"]);
79
+ var usdt0 = {
80
+ supportsChain(chain) {
81
+ return (0, import_util.isEvm)(chain) && !!USDT0_CHAINS[`eip155:${(0, import_util.evmRef)(chain)}`];
82
+ },
83
+ supports(from, to) {
84
+ return from !== to && this.supportsChain(from) && this.supportsChain(to);
85
+ }
86
+ };
87
+
88
+ // src/provider.ts
89
+ var import_viem2 = require("viem");
90
+
91
+ // src/activity.ts
92
+ var import_activity = require("@anywalletkit/protocol-swap2-core/activity");
93
+ var import_viem = require("viem");
94
+ var SEND_ABI = (0, import_viem.parseAbi)([
95
+ "struct SendParam { uint32 dstEid; bytes32 to; uint256 amountLD; uint256 minAmountLD; bytes extraOptions; bytes composeMsg; bytes oftCmd; }",
96
+ "struct MessagingFee { uint256 nativeFee; uint256 lzTokenFee; }",
97
+ "function send(SendParam sp, MessagingFee fee, address refundAddress)"
98
+ ]);
99
+ var USDT0_MODULE = "@anywalletkit/protocol-swap2-usdt0";
100
+ var USDT0_SEND = "0xc7c7f5b3";
101
+ var LZ_RECEIVE = "0x0c0c389e";
102
+ var ZERO = "0x0000000000000000000000000000000000000000";
103
+ function symbolFor(chain) {
104
+ return chain.adapter || [42161, 10, 137].includes(chain.chainId) ? "USDT" : "USDT0";
105
+ }
106
+ function sendDestination(tx) {
107
+ const data = tx.call?.data;
108
+ if (!data || data.length < 10 + 64 * 2) return null;
109
+ try {
110
+ const [sp] = (0, import_viem.decodeFunctionData)({ abi: SEND_ABI, data }).args;
111
+ return USDT0_BY_EID[sp.dstEid] ?? null;
112
+ } catch {
113
+ return null;
114
+ }
115
+ }
116
+ var lc = (a) => a.toLowerCase();
117
+ function describeUsdt0Activity(tx, ctx) {
118
+ const chain = USDT0_CHAINS[tx.chain];
119
+ if (!chain) return null;
120
+ const subtitle = `USDT0 \xB7 ${(0, import_activity.chainLabel)(tx.chain)}`;
121
+ if ((0, import_activity.matchesCall)(tx, [chain.oft], [USDT0_SEND])) {
122
+ const dst = sendDestination(tx);
123
+ const item = (0, import_activity.describeSwapLike)(tx, ctx, { protocol: USDT0_MODULE, subtitle, kind: "bridge", outVerb: "Bridged" });
124
+ if (!item) return null;
125
+ const native = item.assetsOut.filter((a) => a.token === null).reduce((sum, a) => sum + a.amount, 0n);
126
+ item.assetsOut = item.assetsOut.filter((a) => a.token !== null);
127
+ if (native > 0n) item.fee = (item.fee ?? 0n) + native;
128
+ const srcSym = item.assetsOut[0]?.symbol ?? symbolFor(chain);
129
+ if (dst) {
130
+ item.title = `Bridged ${srcSym} \u2192 ${symbolFor(dst)}`;
131
+ item.subtitle = `USDT0 \xB7 ${(0, import_activity.chainLabel)(tx.chain)} \u2192 ${dst.name}`;
132
+ } else item.title = `Bridged ${srcSym}`;
133
+ return item;
134
+ }
135
+ if ((0, import_activity.matchesCall)(tx, [chain.endpoint], [LZ_RECEIVE])) {
136
+ return (0, import_activity.describeSwapLike)(tx, ctx, { protocol: USDT0_MODULE, subtitle, kind: "bridge", inSuffix: "via LayerZero" });
137
+ }
138
+ const called = (0, import_activity.calledContract)(tx);
139
+ if (called && called === lc(chain.token)) return null;
140
+ const own = new Set([...ctx.own].map(lc));
141
+ const origin = chain.adapter ? lc(chain.oft) : ZERO;
142
+ const delivered = tx.transfers.some((t) => t.token !== null && lc(t.token) === lc(chain.token) && lc(t.from) === origin && own.has(lc(t.to)));
143
+ if (!delivered || own.has(lc(tx.from))) return null;
144
+ return (0, import_activity.describeSwapLike)(tx, ctx, { protocol: USDT0_MODULE, subtitle, kind: "bridge", inSuffix: "via LayerZero", counterparty: chain.oft });
145
+ }
146
+
147
+ // src/provider.ts
148
+ var import_swap2_assets = require("@anywalletkit/swap2-assets");
149
+ var import_util2 = require("@anywalletkit/protocol-swap2-core/util");
150
+ var OFT_ABI = (0, import_viem2.parseAbi)([
151
+ "struct SendParam { uint32 dstEid; bytes32 to; uint256 amountLD; uint256 minAmountLD; bytes extraOptions; bytes composeMsg; bytes oftCmd; }",
152
+ "struct MessagingFee { uint256 nativeFee; uint256 lzTokenFee; }",
153
+ "struct OFTLimit { uint256 minAmountLD; uint256 maxAmountLD; }",
154
+ "struct OFTFeeDetail { int256 feeAmountLD; string description; }",
155
+ "struct OFTReceipt { uint256 amountSentLD; uint256 amountReceivedLD; }",
156
+ "struct MessagingReceipt { bytes32 guid; uint64 nonce; MessagingFee fee; }",
157
+ "function quoteSend(SendParam s, bool payInLzToken) view returns (MessagingFee)",
158
+ "function quoteOFT(SendParam s) view returns (OFTLimit, OFTFeeDetail[], OFTReceipt)",
159
+ "function send(SendParam s, MessagingFee fee, address refundAddress) payable returns (MessagingReceipt, OFTReceipt)",
160
+ "function token() view returns (address)",
161
+ "function approvalRequired() view returns (bool)",
162
+ "function peers(uint32 eid) view returns (bytes32)"
163
+ ]);
164
+ var ENDPOINT_ABI = (0, import_viem2.parseAbi)([
165
+ "struct Origin { uint32 srcEid; bytes32 sender; uint64 nonce; }",
166
+ "function lzReceive(Origin origin, address receiver, bytes32 guid, bytes message, bytes extraData) payable"
167
+ ]);
168
+ var EVM_PLACEHOLDER = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045";
169
+ var DEFAULT_QUOTE_TTL_MS = 6e4;
170
+ var RPC_TIMEOUT_MS = 12e3;
171
+ var SCAN_TIMEOUT_MS = 12e3;
172
+ var GAS_SEND = "450000";
173
+ var FEE_HEADROOM_BPS = 2500n;
174
+ var GAS_CLAIM = "300000";
175
+ var ZERO_B32 = `0x${"00".repeat(32)}`;
176
+ var same = (a, b) => !!a && a.toLowerCase() === b.toLowerCase();
177
+ var isHexData = (v) => typeof v === "string" && /^0x(?:[0-9a-fA-F]{2})*$/.test(v);
178
+ function tokenMatches(chain, symbolOrAddress) {
179
+ if (symbolOrAddress.startsWith("0x")) return same(symbolOrAddress, chain.token);
180
+ if (!USDT0_SYMBOLS.includes(symbolOrAddress.toUpperCase().replace("USD\u20AE", "USDT"))) return false;
181
+ const known = (0, import_swap2_assets.ccAsset)(chain.caip2, symbolOrAddress);
182
+ return !known || same(known.contract ?? "", chain.token);
183
+ }
184
+ function rpcSafeReason(err, chainName) {
185
+ const e = err;
186
+ const text = `${e?.shortMessage ?? ""}
187
+ ${e?.message ?? String(err)}`;
188
+ if (/HttpRequestError|TimeoutError|RpcRequestError|WebSocketRequestError/.test(String(e?.name)) || /\bURL: |HTTP request failed|request timed out|took too long to respond/i.test(text)) {
189
+ return `usdt0: RPC for ${chainName} unavailable \u2014 try again`;
190
+ }
191
+ return (0, import_util2.redactUrls)(err instanceof Error ? err.message : String(err));
192
+ }
193
+ function usdt0Provider(config = {}) {
194
+ config = Object.freeze({ ...config });
195
+ const scan = (config.scanUrl ?? LZ_SCAN_API).replace(/\/+$/, "");
196
+ if (!/^https?:\/\//.test(scan)) throw new Error("usdt0: scanUrl must use http or https");
197
+ const quoteTtlMs = config.quoteTtlMs ?? DEFAULT_QUOTE_TTL_MS;
198
+ if (!Number.isInteger(quoteTtlMs) || quoteTtlMs <= 0 || quoteTtlMs > 3e5) throw new Error("usdt0: quoteTtlMs must be an integer between 1 and 300000");
199
+ const rpcUrls = Object.freeze({ ...config.rpcUrls ?? {} });
200
+ const doFetch = (input, init) => (config.fetchImpl ?? fetch)(input, init);
201
+ const client = (caip2) => {
202
+ const raw = rpcUrls[caip2];
203
+ const urls = typeof raw === "string" ? [raw] : [...raw ?? []];
204
+ if (urls.length === 0) return null;
205
+ const opts = { timeout: RPC_TIMEOUT_MS, ...config.fetchImpl ? { fetchFn: config.fetchImpl } : {} };
206
+ return (0, import_viem2.createPublicClient)({ transport: urls.length === 1 ? (0, import_viem2.http)(urls[0], opts) : (0, import_viem2.fallback)(urls.map((u) => (0, import_viem2.http)(u, opts))) });
207
+ };
208
+ const verified = /* @__PURE__ */ new Map();
209
+ function verifyChain(chain) {
210
+ let p = verified.get(chain.caip2);
211
+ if (!p) {
212
+ p = (async () => {
213
+ const c2 = client(chain.caip2);
214
+ if (!c2) throw new Error(`usdt0: no RPC for ${chain.caip2}`);
215
+ const [token, approval] = await Promise.all([
216
+ c2.readContract({ address: (0, import_viem2.getAddress)(chain.oft), abi: OFT_ABI, functionName: "token" }),
217
+ c2.readContract({ address: (0, import_viem2.getAddress)(chain.oft), abi: OFT_ABI, functionName: "approvalRequired" })
218
+ ]);
219
+ if (!same(token, chain.token)) throw new Error(`usdt0: OFT ${chain.oft} on ${chain.name} reports token ${token}, expected ${chain.token}`);
220
+ if (approval !== chain.adapter) throw new Error(`usdt0: OFT ${chain.oft} on ${chain.name} reports approvalRequired=${String(approval)}, expected ${String(chain.adapter)}`);
221
+ })();
222
+ p.catch(() => verified.delete(chain.caip2));
223
+ verified.set(chain.caip2, p);
224
+ }
225
+ return p;
226
+ }
227
+ const peers = /* @__PURE__ */ new Map();
228
+ function verifyPeer(src, dst) {
229
+ const key = `${src.eid}>${dst.eid}`;
230
+ let p = peers.get(key);
231
+ if (!p) {
232
+ p = (async () => {
233
+ const c2 = client(src.caip2);
234
+ if (!c2) throw new Error(`usdt0: no RPC for ${src.caip2}`);
235
+ const peer = await c2.readContract({ address: (0, import_viem2.getAddress)(src.oft), abi: OFT_ABI, functionName: "peers", args: [dst.eid] });
236
+ if (!same(peer, (0, import_viem2.pad)((0, import_viem2.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`);
237
+ })();
238
+ p.catch(() => peers.delete(key));
239
+ peers.set(key, p);
240
+ }
241
+ return p;
242
+ }
243
+ function parseIntent(intentId) {
244
+ const [tag, s, d] = intentId.split(":");
245
+ if (tag !== "usdt0") return null;
246
+ const src = USDT0_BY_EID[Number(s)];
247
+ const dst = USDT0_BY_EID[Number(d)];
248
+ return src && dst && src !== dst ? { src, dst } : null;
249
+ }
250
+ const sendParam = (dstEid, recipient, amountLD, minAmountLD) => ({
251
+ dstEid,
252
+ to: (0, import_viem2.pad)((0, import_viem2.getAddress)(recipient), { size: 32 }),
253
+ amountLD,
254
+ minAmountLD,
255
+ extraOptions: "0x",
256
+ composeMsg: "0x",
257
+ oftCmd: "0x"
258
+ });
259
+ async function scanMessage(src, dst, sourceTxHash) {
260
+ const res = await doFetch(`${scan}/v1/messages/tx/${encodeURIComponent(sourceTxHash)}`, { headers: { accept: "application/json" }, signal: (0, import_util2.timeoutSignal)(SCAN_TIMEOUT_MS) });
261
+ if (res.status === 404) return null;
262
+ if (!res.ok) throw new Error(`usdt0: LayerZero Scan \u2192 ${res.status}`);
263
+ const body = await res.json();
264
+ const list = Array.isArray(body?.data) ? body.data : [];
265
+ if (list.length === 0) return null;
266
+ const m = list.find((x) => x.pathway?.srcEid === src.eid && x.pathway?.dstEid === dst.eid && same(x.pathway?.sender?.address, src.oft));
267
+ return m ?? "foreign";
268
+ }
269
+ return {
270
+ name: "usdt0",
271
+ describeActivity: describeUsdt0Activity,
272
+ enabled: () => config.enabled ?? true,
273
+ supportsChain: (chain) => usdt0.supportsChain(chain) && client(chain) !== null,
274
+ supportsChainPair: (from, to) => usdt0.supports(from, to) && client(from) !== null,
275
+ supportsRoute(from, to) {
276
+ const src = USDT0_CHAINS[from.chain];
277
+ const dst = USDT0_CHAINS[to.chain];
278
+ if (!src || !dst || src === dst || client(from.chain) === null) return false;
279
+ return tokenMatches(src, from.token) && tokenMatches(dst, to.token);
280
+ },
281
+ async getQuote(params, signal) {
282
+ const refuse = (reason) => {
283
+ params.onRefusal?.(reason);
284
+ return null;
285
+ };
286
+ if (params.flex) return refuse("the amount is sealed into the calldata (no flexible deposits)");
287
+ const src = USDT0_CHAINS[params.sourceChain];
288
+ const dst = USDT0_CHAINS[params.destChain];
289
+ if (!src || !dst) return refuse("USDT0 is not deployed on this chain");
290
+ if (src === dst) return refuse("same-chain transfers are not a bridge");
291
+ if (!tokenMatches(src, params.sourceToken)) return refuse(`source token ${params.sourceToken} is not USDT0 (${src.token}) on ${src.name}`);
292
+ if (!tokenMatches(dst, params.destToken)) return refuse(`destination token ${params.destToken} is not USDT0 (${dst.token}) on ${dst.name}`);
293
+ if (!/^\d+$/.test(params.amountIn)) return refuse("amount must be a base-unit integer");
294
+ const amount = BigInt(params.amountIn);
295
+ if (amount <= 0n) return refuse("amount must be positive");
296
+ const recipient = params.destAddress || EVM_PLACEHOLDER;
297
+ if (!(0, import_viem2.isAddress)(recipient)) return refuse("destination must be an EVM address");
298
+ const refund = params.refundAddress || EVM_PLACEHOLDER;
299
+ if (!(0, import_viem2.isAddress)(refund)) return refuse("refund address must be an EVM address");
300
+ const c2 = client(src.caip2);
301
+ if (!c2) return refuse(`no RPC configured for ${src.caip2}`);
302
+ try {
303
+ await Promise.all([verifyChain(src), verifyPeer(src, dst)]);
304
+ const probe = sendParam(dst.eid, recipient, amount, 0n);
305
+ const [limit, feeDetails, receipt] = await c2.readContract({ address: (0, import_viem2.getAddress)(src.oft), abi: OFT_ABI, functionName: "quoteOFT", args: [probe] });
306
+ if (amount < limit.minAmountLD) return refuse(`amount ${amount} is below the OFT minimum ${limit.minAmountLD}`);
307
+ if (amount > limit.maxAmountLD) return refuse(`amount ${amount} exceeds the OFT maximum ${limit.maxAmountLD}`);
308
+ const received = receipt.amountReceivedLD;
309
+ if (received <= 0n) return refuse("amount is below the OFT dust threshold \u2014 nothing would arrive");
310
+ if (received > amount) return refuse("OFT reports more received than sent \u2014 refusing");
311
+ if (params.minAmountOut !== void 0 && received < BigInt(params.minAmountOut)) return refuse(`received ${received} is below the caller's floor ${params.minAmountOut}`);
312
+ const sp = sendParam(dst.eid, recipient, amount, received);
313
+ const fee = await c2.readContract({ address: (0, import_viem2.getAddress)(src.oft), abi: OFT_ABI, functionName: "quoteSend", args: [sp, false] });
314
+ if (fee.lzTokenFee !== 0n) return refuse("OFT asks for a fee in LZ token \u2014 unsupported");
315
+ const nativeFee = fee.nativeFee + fee.nativeFee * FEE_HEADROOM_BPS / 10000n;
316
+ const data = (0, import_viem2.encodeFunctionData)({ abi: OFT_ABI, functionName: "send", args: [sp, { nativeFee, lzTokenFee: 0n }, (0, import_viem2.getAddress)(refund)] });
317
+ 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" }));
318
+ const feeUsd = protocolFees.reduce((a, f) => a + Number(f.amount) / 1e6, 0);
319
+ const eta = src.chainId === 1 ? 1e3 : 240;
320
+ return {
321
+ provider: "usdt0",
322
+ intentId: `usdt0:${src.eid}:${dst.eid}`,
323
+ depositAddress: "",
324
+ depositMemo: null,
325
+ txRequest: {
326
+ to: src.oft,
327
+ data,
328
+ value: nativeFee.toString(),
329
+ chainId: src.chainId,
330
+ gasLimit: GAS_SEND,
331
+ ...src.adapter ? { approvalAddress: src.oft } : {},
332
+ ...nativeFee > 0n ? { nativeFee: nativeFee.toString() } : {}
333
+ },
334
+ sourceAmount: amount.toString(),
335
+ destAmount: received.toString(),
336
+ minDestAmount: received.toString(),
337
+ feeUsd,
338
+ fees: protocolFees,
339
+ etaSeconds: eta,
340
+ reliability: 0.98,
341
+ validUntilMs: Date.now() + quoteTtlMs,
342
+ providerData: {
343
+ srcEid: String(src.eid),
344
+ dstEid: String(dst.eid),
345
+ oft: src.oft,
346
+ token: src.token,
347
+ recipient: (0, import_viem2.getAddress)(recipient),
348
+ refundAddress: (0, import_viem2.getAddress)(refund),
349
+ amountLD: amount.toString(),
350
+ minAmountLD: received.toString(),
351
+ nativeFee: nativeFee.toString()
352
+ }
353
+ };
354
+ } catch (err) {
355
+ return refuse(rpcSafeReason(err, src.name));
356
+ }
357
+ },
358
+ async buildDeposit(quote) {
359
+ const tx = quote.txRequest;
360
+ const parsed = parseIntent(quote.intentId);
361
+ if (!tx || !parsed) throw new Error("swap2: USDT0 quote has no transaction request");
362
+ return {
363
+ chain: parsed.src.caip2,
364
+ to: tx.to ?? "",
365
+ value: tx.value ?? "0",
366
+ ...tx.data ? { data: tx.data } : {},
367
+ ...tx.chainId !== void 0 ? { chainId: tx.chainId } : {},
368
+ ...tx.gasLimit ? { gasLimit: tx.gasLimit } : {},
369
+ ...tx.approvalAddress ? { approvalAddress: tx.approvalAddress } : {}
370
+ };
371
+ },
372
+ /**
373
+ * Right before signing: the target is the source OFT, the peer is still the
374
+ * destination OFT, and the CURRENT LayerZero fee still fits the sealed
375
+ * `msg.value` (quote + headroom) — the endpoint reverts a send that
376
+ * underpays, so a fee past the headroom means re-quote rather than a
377
+ * wasted broadcast.
378
+ */
379
+ async preflightDeposit(quote, tx) {
380
+ const parsed = parseIntent(quote.intentId);
381
+ if (!parsed) throw new Error("usdt0: malformed intent id");
382
+ const { src, dst } = parsed;
383
+ if (!same(tx.to, src.oft)) throw new Error(`usdt0: deposit target ${tx.to} is not the ${src.name} OFT`);
384
+ const pd = quote.providerData ?? {};
385
+ if (!/^\d+$/.test(pd.amountLD ?? "") || !/^\d+$/.test(pd.minAmountLD ?? "") || !/^\d+$/.test(pd.nativeFee ?? "") || !(0, import_viem2.isAddress)(pd.recipient ?? "")) {
386
+ throw new Error("usdt0: quote carries no sealed send parameters");
387
+ }
388
+ const c2 = client(src.caip2);
389
+ if (!c2) throw new Error(`usdt0: no RPC for ${src.caip2}`);
390
+ const sp = sendParam(dst.eid, pd.recipient, BigInt(pd.amountLD), BigInt(pd.minAmountLD));
391
+ let fee;
392
+ try {
393
+ await Promise.all([verifyChain(src), verifyPeer(src, dst)]);
394
+ fee = await c2.readContract({ address: (0, import_viem2.getAddress)(src.oft), abi: OFT_ABI, functionName: "quoteSend", args: [sp, false] });
395
+ } catch (err) {
396
+ throw new Error(rpcSafeReason(err, src.name));
397
+ }
398
+ const sealed = BigInt(pd.nativeFee);
399
+ if (fee.nativeFee > sealed) throw new Error(`usdt0: LayerZero fee rose to ${fee.nativeFee} above the sealed ${sealed} (quote + ${FEE_HEADROOM_BPS / 100n} % headroom) \u2014 re-quote`);
400
+ if (BigInt(tx.value) !== sealed) throw new Error(`usdt0: transaction value ${tx.value} is not the quoted fee ${sealed}`);
401
+ },
402
+ async trackIntent(ref) {
403
+ const parsed = parseIntent(ref.intentId);
404
+ if (!parsed) return { state: "failed" };
405
+ const { src, dst } = parsed;
406
+ if (!ref.sourceTxHash) return { state: "pending" };
407
+ try {
408
+ const m = await scanMessage(src, dst, ref.sourceTxHash);
409
+ if (m === null) return { state: "pending", depositReceived: false };
410
+ if (m === "foreign") return { state: "failed" };
411
+ const name = (m.status?.name ?? "").toUpperCase();
412
+ const destHash = m.destination?.tx?.txHash;
413
+ if (name === "DELIVERED") return { state: "completed", depositReceived: true, ...destHash ? { destTxHash: destHash } : ref.destTxHash ? { destTxHash: ref.destTxHash } : {} };
414
+ if (name === "PAYLOAD_STORED") return { state: "action-required", depositReceived: true, claim: { chain: dst.caip2, ready: true } };
415
+ if (["FAILED", "APPLICATION_BURNED", "APPLICATION_SKIPPED", "UNRESOLVABLE_COMMAND", "MALFORMED_COMMAND"].includes(name)) return { state: "failed", depositReceived: true };
416
+ return { state: "pending", depositReceived: true };
417
+ } catch {
418
+ return { state: "pending" };
419
+ }
420
+ },
421
+ /**
422
+ * A delivery that reverted on the destination (PAYLOAD_STORED — out of
423
+ * executor gas, paused token) is retried by anyone through the endpoint:
424
+ * `lzReceive(origin, receiver, guid, message, extraData)` with the exact
425
+ * packet LayerZero Scan recorded. Null while nothing is stored.
426
+ */
427
+ async buildClaim(ref) {
428
+ const parsed = parseIntent(ref.intentId);
429
+ if (!parsed || !ref.sourceTxHash) return null;
430
+ const { src, dst } = parsed;
431
+ const m = await scanMessage(src, dst, ref.sourceTxHash);
432
+ if (!m || m === "foreign" || (m.status?.name ?? "").toUpperCase() !== "PAYLOAD_STORED") return null;
433
+ const nonce = m.pathway?.nonce;
434
+ const guid = m.guid;
435
+ const message = m.source?.tx?.payload;
436
+ if (!Number.isInteger(nonce) || !isHexData(guid) || guid.length !== 66 || !isHexData(message) || message.length <= 2) {
437
+ throw new Error("usdt0: LayerZero Scan has no complete packet for the stored payload");
438
+ }
439
+ if (!same(m.pathway?.receiver?.address, dst.oft)) throw new Error("usdt0: stored payload is addressed to another receiver");
440
+ const valueRaw = m.source?.tx?.options?.lzReceive?.value ?? "0";
441
+ const value = /^\d+$/.test(valueRaw) ? valueRaw : "0";
442
+ return {
443
+ chain: dst.caip2,
444
+ to: dst.endpoint,
445
+ value,
446
+ data: (0, import_viem2.encodeFunctionData)({
447
+ abi: ENDPOINT_ABI,
448
+ functionName: "lzReceive",
449
+ args: [{ srcEid: src.eid, sender: (0, import_viem2.pad)((0, import_viem2.getAddress)(src.oft), { size: 32 }), nonce: BigInt(nonce) }, (0, import_viem2.getAddress)(dst.oft), guid, message, "0x"]
450
+ }),
451
+ chainId: dst.chainId,
452
+ gasLimit: GAS_CLAIM
453
+ };
454
+ }
455
+ };
456
+ }
457
+ var USDT0_STATUS_MAP = Object.freeze({
458
+ INFLIGHT: "pending",
459
+ CONFIRMING: "pending",
460
+ BLOCKED: "pending",
461
+ DELIVERED: "completed",
462
+ PAYLOAD_STORED: "action-required",
463
+ FAILED: "failed",
464
+ APPLICATION_BURNED: "failed",
465
+ APPLICATION_SKIPPED: "failed",
466
+ UNRESOLVABLE_COMMAND: "failed",
467
+ MALFORMED_COMMAND: "failed"
468
+ });
469
+
470
+ // src/protocol.ts
471
+ var import_single = require("@anywalletkit/protocol-swap2-core/swap2/single");
472
+ var Usdt0Swap2Protocol = class extends import_single.SingleProviderSwap2 {
473
+ provider;
474
+ constructor(account, config) {
475
+ super(account, config);
476
+ this.provider = usdt0Provider(config);
477
+ }
478
+ chainSupported(caip2) {
479
+ return usdt0.supportsChain(caip2);
480
+ }
481
+ pairSupported(fromChain, toChain) {
482
+ return usdt0.supports(fromChain, toChain);
483
+ }
484
+ };
485
+ //# sourceMappingURL=index.cjs.map