@anywalletkit/protocol-swap2-lifi 0.4.0 → 0.6.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.
@@ -26,144 +26,162 @@ var GENERIC_SWAPS = {
26
26
  var BRIDGE_INPUT_FEE_MAX_BPS = 100n;
27
27
  var kindOf = (asset) => /^0x0{40}$/i.test(asset) ? "native" : "erc20";
28
28
  var BRIDGE_CALLS = {
29
- "0x1fd8010c": { sourceSwaps: false },
29
+ "0x1fd8010c": { sourceSwaps: false, facet: "Across" },
30
30
  // startBridgeTokensViaAcross (AcrossFacet)
31
- "0x3a3f7332": { sourceSwaps: true },
31
+ "0x3a3f7332": { sourceSwaps: true, facet: "Across" },
32
32
  // swapAndStartBridgeTokensViaAcross (AcrossFacet)
33
- "0xa1f1ce43": { sourceSwaps: false },
33
+ "0xa1f1ce43": { sourceSwaps: false, facet: "AcrossV4" },
34
34
  // startBridgeTokensViaAcrossV4 (AcrossFacetV4)
35
- "0x1794958f": { sourceSwaps: true },
35
+ "0x1794958f": { sourceSwaps: true, facet: "AcrossV4" },
36
36
  // swapAndStartBridgeTokensViaAcrossV4 (AcrossFacetV4)
37
- "0x6a90d66e": { sourceSwaps: false },
37
+ "0x6a90d66e": { sourceSwaps: false, facet: "AcrossV4Swap" },
38
38
  // startBridgeTokensViaAcrossV4Swap (AcrossV4SwapFacet)
39
- "0x9b054bc4": { sourceSwaps: true },
39
+ "0x9b054bc4": { sourceSwaps: true, facet: "AcrossV4Swap" },
40
40
  // swapAndStartBridgeTokensViaAcrossV4Swap (AcrossV4SwapFacet)
41
- "0x6a51e9a9": { sourceSwaps: false },
41
+ "0x6a51e9a9": { sourceSwaps: false, facet: "AllBridge" },
42
42
  // startBridgeTokensViaAllBridge (AllBridgeFacet)
43
- "0x63267469": { sourceSwaps: true },
43
+ "0x63267469": { sourceSwaps: true, facet: "AllBridge" },
44
44
  // swapAndStartBridgeTokensViaAllBridge (AllBridgeFacet)
45
- "0xc9851d0b": { sourceSwaps: false },
45
+ "0xc9851d0b": { sourceSwaps: false, facet: "ArbitrumBridge" },
46
46
  // startBridgeTokensViaArbitrumBridge (ArbitrumBridgeFacet)
47
- "0x3cc9517b": { sourceSwaps: true },
47
+ "0x3cc9517b": { sourceSwaps: true, facet: "ArbitrumBridge" },
48
48
  // swapAndStartBridgeTokensViaArbitrumBridge (ArbitrumBridgeFacet)
49
- "0xe0a4201c": { sourceSwaps: false },
49
+ "0xe0a4201c": { sourceSwaps: false, facet: "CelerCircleBridge" },
50
50
  // startBridgeTokensViaCelerCircleBridge (CelerCircleBridgeFacet)
51
- "0x4f3b0759": { sourceSwaps: true },
51
+ "0x4f3b0759": { sourceSwaps: true, facet: "CelerCircleBridge" },
52
52
  // swapAndStartBridgeTokensViaCelerCircleBridge (CelerCircleBridgeFacet)
53
- "0x0315138f": { sourceSwaps: false },
53
+ "0x0315138f": { sourceSwaps: false, facet: "Centrifuge" },
54
54
  // startBridgeTokensViaCentrifuge (CentrifugeFacet)
55
- "0x8fd4d4dd": { sourceSwaps: true },
55
+ "0x8fd4d4dd": { sourceSwaps: true, facet: "Centrifuge" },
56
56
  // swapAndStartBridgeTokensViaCentrifuge (CentrifugeFacet)
57
- "0x0ad553b3": { sourceSwaps: false },
57
+ "0x0ad553b3": { sourceSwaps: false, facet: "Chainflip" },
58
58
  // startBridgeTokensViaChainflip (ChainflipFacet)
59
- "0xee3314a1": { sourceSwaps: true },
59
+ "0xee3314a1": { sourceSwaps: true, facet: "Chainflip" },
60
60
  // swapAndStartBridgeTokensViaChainflip (ChainflipFacet)
61
- "0x4004633e": { sourceSwaps: false },
62
- // startBridgeTokensViaDeBridgeDln (DeBridgeDlnFacet)
63
- "0x2c7d2db0": { sourceSwaps: true },
64
- // swapAndStartBridgeTokensViaDeBridgeDln (DeBridgeDlnFacet)
65
- "0xbff90b61": { sourceSwaps: false },
66
- // startBridgeTokensViaEco (EcoFacet)
67
- "0x762aea18": { sourceSwaps: true },
68
- // swapAndStartBridgeTokensViaEco (EcoFacet)
69
- "0x678786ea": { sourceSwaps: false },
61
+ "0x05c54990": { sourceSwaps: false, facet: "Eco" },
62
+ // startBridgeTokensViaEco (EcoFacet v3.0.0, 9001792)
63
+ "0x8352664c": { sourceSwaps: true, facet: "Eco" },
64
+ // swapAndStartBridgeTokensViaEco (EcoFacet v3.0.0, 9001792)
65
+ "0x678786ea": { sourceSwaps: false, facet: "Frax" },
70
66
  // startBridgeTokensViaFrax (FraxFacet)
71
- "0x07b8e4c7": { sourceSwaps: true },
67
+ "0x07b8e4c7": { sourceSwaps: true, facet: "Frax" },
72
68
  // swapAndStartBridgeTokensViaFrax (FraxFacet)
73
- "0x5f9af35d": { sourceSwaps: false },
74
- // startBridgeTokensViaGarden (GardenFacet)
75
- "0x76ad76fe": { sourceSwaps: true },
76
- // swapAndStartBridgeTokensViaGarden (GardenFacet)
77
- "0xfc5f1003": { sourceSwaps: false },
69
+ "0xfc5f1003": { sourceSwaps: false, facet: "GasZip" },
78
70
  // startBridgeTokensViaGasZip (GasZipFacet)
79
- "0x606326ff": { sourceSwaps: true },
71
+ "0x606326ff": { sourceSwaps: true, facet: "GasZip" },
80
72
  // swapAndStartBridgeTokensViaGasZip (GasZipFacet)
81
- "0x6f9206ba": { sourceSwaps: false },
73
+ "0x6f9206ba": { sourceSwaps: false, facet: "Glacis" },
82
74
  // startBridgeTokensViaGlacis (GlacisFacet)
83
- "0x9c4b6dd9": { sourceSwaps: true },
75
+ "0x9c4b6dd9": { sourceSwaps: true, facet: "Glacis" },
84
76
  // swapAndStartBridgeTokensViaGlacis (GlacisFacet)
85
- "0xf66fe519": { sourceSwaps: false },
77
+ "0xf66fe519": { sourceSwaps: false, facet: "GnosisBridge" },
86
78
  // startBridgeTokensViaGnosisBridge (GnosisBridgeFacet)
87
- "0x7bf96e0a": { sourceSwaps: true },
79
+ "0x7bf96e0a": { sourceSwaps: true, facet: "GnosisBridge" },
88
80
  // swapAndStartBridgeTokensViaGnosisBridge (GnosisBridgeFacet)
89
- "0xee9e98e0": { sourceSwaps: false },
81
+ "0xee9e98e0": { sourceSwaps: false, facet: "LayerSwap" },
90
82
  // startBridgeTokensViaLayerSwap (LayerSwapFacet)
91
- "0x4c279d6b": { sourceSwaps: true },
83
+ "0x4c279d6b": { sourceSwaps: true, facet: "LayerSwap" },
92
84
  // swapAndStartBridgeTokensViaLayerSwap (LayerSwapFacet)
93
- "0x7dbcf1d9": { sourceSwaps: false },
85
+ "0x7dbcf1d9": { sourceSwaps: false, facet: "LiFiIntentEscrowV2" },
94
86
  // startBridgeTokensViaLiFiIntentEscrowV2 (LiFiIntentEscrowFacetV2)
95
- "0x6d21c5df": { sourceSwaps: true },
87
+ "0x6d21c5df": { sourceSwaps: true, facet: "LiFiIntentEscrowV2" },
96
88
  // swapAndStartBridgeTokensViaLiFiIntentEscrowV2 (LiFiIntentEscrowFacetV2)
97
- "0x65782c4b": { sourceSwaps: false },
89
+ "0x65782c4b": { sourceSwaps: false, facet: "M0" },
98
90
  // startBridgeTokensViaM0 (M0Facet)
99
- "0xb9eaf86d": { sourceSwaps: true },
91
+ "0xb9eaf86d": { sourceSwaps: true, facet: "M0" },
100
92
  // swapAndStartBridgeTokensViaM0 (M0Facet)
101
- "0xb4875292": { sourceSwaps: false },
93
+ "0xb4875292": { sourceSwaps: false, facet: "Mayan" },
102
94
  // startBridgeTokensViaMayan (MayanFacet)
103
- "0x80c65808": { sourceSwaps: true },
95
+ "0x80c65808": { sourceSwaps: true, facet: "Mayan" },
104
96
  // swapAndStartBridgeTokensViaMayan (MayanFacet)
105
- "0x4213dfff": { sourceSwaps: false },
97
+ "0x4213dfff": { sourceSwaps: false, facet: "MegaETHBridge" },
106
98
  // startBridgeTokensViaMegaETHBridge (MegaETHBridgeFacet)
107
- "0x22256e89": { sourceSwaps: true },
99
+ "0x22256e89": { sourceSwaps: true, facet: "MegaETHBridge" },
108
100
  // swapAndStartBridgeTokensViaMegaETHBridge (MegaETHBridgeFacet)
109
- "0x4698f032": { sourceSwaps: false },
101
+ "0x4698f032": { sourceSwaps: false, facet: "NEARIntents" },
110
102
  // startBridgeTokensViaNEARIntents (NEARIntentsFacet)
111
- "0x02631b09": { sourceSwaps: true },
103
+ "0x02631b09": { sourceSwaps: true, facet: "NEARIntents" },
112
104
  // swapAndStartBridgeTokensViaNEARIntents (NEARIntentsFacet)
113
- "0x782621d8": { sourceSwaps: false },
105
+ "0x782621d8": { sourceSwaps: false, facet: "OmniBridge" },
114
106
  // startBridgeTokensViaOmniBridge (OmniBridgeFacet)
115
- "0x95726782": { sourceSwaps: true },
107
+ "0x95726782": { sourceSwaps: true, facet: "OmniBridge" },
116
108
  // swapAndStartBridgeTokensViaOmniBridge (OmniBridgeFacet)
117
- "0xce8a97a5": { sourceSwaps: false },
109
+ "0xce8a97a5": { sourceSwaps: false, facet: "OptimismBridge" },
118
110
  // startBridgeTokensViaOptimismBridge (OptimismBridgeFacet)
119
- "0x5bb5d448": { sourceSwaps: true },
111
+ "0x5bb5d448": { sourceSwaps: true, facet: "OptimismBridge" },
120
112
  // swapAndStartBridgeTokensViaOptimismBridge (OptimismBridgeFacet)
121
- "0xaf62c7d6": { sourceSwaps: false },
113
+ "0xaf62c7d6": { sourceSwaps: false, facet: "PolygonBridge" },
122
114
  // startBridgeTokensViaPolygonBridge (PolygonBridgeFacet)
123
- "0xb4f37581": { sourceSwaps: true },
115
+ "0xb4f37581": { sourceSwaps: true, facet: "PolygonBridge" },
124
116
  // swapAndStartBridgeTokensViaPolygonBridge (PolygonBridgeFacet)
125
- "0xf434d6ca": { sourceSwaps: false },
117
+ "0xf434d6ca": { sourceSwaps: false, facet: "PolymerCCTP" },
126
118
  // startBridgeTokensViaPolymerCCTP (PolymerCCTPFacet)
127
- "0x17917a4e": { sourceSwaps: true },
119
+ "0x17917a4e": { sourceSwaps: true, facet: "PolymerCCTP" },
128
120
  // swapAndStartBridgeTokensViaPolymerCCTP (PolymerCCTPFacet)
129
- "0x092e8fa4": { sourceSwaps: false },
130
- // startBridgeTokensViaRelayDepository (RelayDepositoryFacet)
131
- "0xa3443faa": { sourceSwaps: true },
132
- // swapAndStartBridgeTokensViaRelayDepository (RelayDepositoryFacet)
133
- "0x3f313808": { sourceSwaps: false },
134
- // startBridgeTokensViaSquid (SquidFacet)
135
- "0xa8f66666": { sourceSwaps: true },
136
- // swapAndStartBridgeTokensViaSquid (SquidFacet)
137
- "0x14d53077": { sourceSwaps: false },
121
+ "0x14d53077": { sourceSwaps: false, facet: "Stargate" },
138
122
  // startBridgeTokensViaStargate (StargateFacetV2)
139
- "0xa6010a66": { sourceSwaps: true },
123
+ "0xa6010a66": { sourceSwaps: true, facet: "Stargate" },
140
124
  // swapAndStartBridgeTokensViaStargate (StargateFacetV2)
141
- "0x0691ff78": { sourceSwaps: false },
125
+ "0x0691ff78": { sourceSwaps: false, facet: "Superset" },
142
126
  // startBridgeTokensViaSuperset (SupersetFacet)
143
- "0xf26e657f": { sourceSwaps: true },
127
+ "0xf26e657f": { sourceSwaps: true, facet: "Superset" },
144
128
  // swapAndStartBridgeTokensViaSuperset (SupersetFacet)
145
- "0xe23b7a08": { sourceSwaps: false },
146
- // startBridgeTokensViaSymbiosis (SymbiosisFacet)
147
- "0xc46059b2": { sourceSwaps: true },
148
- // swapAndStartBridgeTokensViaSymbiosis (SymbiosisFacet)
149
- "0x2541ec57": { sourceSwaps: false },
150
- // startBridgeTokensViaThorSwap (ThorSwapFacet)
151
- "0xad673d88": { sourceSwaps: true },
152
- // swapAndStartBridgeTokensViaThorSwap (ThorSwapFacet)
153
- "0x64261d58": { sourceSwaps: false },
129
+ "0x64261d58": { sourceSwaps: false, facet: "Unit" },
154
130
  // startBridgeTokensViaUnit (UnitFacet)
155
- "0x21a3af52": { sourceSwaps: true }
131
+ "0x21a3af52": { sourceSwaps: true, facet: "Unit" }
156
132
  // swapAndStartBridgeTokensViaUnit (UnitFacet)
157
133
  };
158
- var Reader = class {
134
+ var UNBOUND_BRIDGES = {
135
+ "0x092e8fa4": "Relay",
136
+ // startBridgeTokensViaRelayDepository
137
+ "0xa3443faa": "Relay",
138
+ // swapAndStartBridgeTokensViaRelayDepository
139
+ "0x5f9af35d": "Garden",
140
+ // startBridgeTokensViaGarden
141
+ "0x76ad76fe": "Garden",
142
+ // swapAndStartBridgeTokensViaGarden
143
+ "0x4004633e": "deBridge DLN",
144
+ // startBridgeTokensViaDeBridgeDln
145
+ "0x2c7d2db0": "deBridge DLN",
146
+ // swapAndStartBridgeTokensViaDeBridgeDln
147
+ "0x2541ec57": "ThorSwap",
148
+ // startBridgeTokensViaThorSwap
149
+ "0xad673d88": "ThorSwap",
150
+ // swapAndStartBridgeTokensViaThorSwap
151
+ "0x3f313808": "Squid",
152
+ // startBridgeTokensViaSquid
153
+ "0xa8f66666": "Squid",
154
+ // swapAndStartBridgeTokensViaSquid
155
+ "0xe23b7a08": "Symbiosis",
156
+ // startBridgeTokensViaSymbiosis
157
+ "0xc46059b2": "Symbiosis"
158
+ // swapAndStartBridgeTokensViaSymbiosis
159
+ };
160
+ var Reader = class _Reader {
159
161
  b;
160
- constructor(hex) {
161
- if (!/^0x([0-9a-fA-F]{2})*$/.test(hex)) throw new Error("not hex");
162
- this.b = Uint8Array.from(hex.slice(2).match(/../g) ?? [], (h) => Number.parseInt(h, 16));
162
+ constructor(src) {
163
+ if (typeof src !== "string") {
164
+ this.b = src;
165
+ return;
166
+ }
167
+ if (!/^0x([0-9a-fA-F]{2})*$/.test(src)) throw new Error("not hex");
168
+ this.b = Uint8Array.from(src.slice(2).match(/../g) ?? [], (h) => Number.parseInt(h, 16));
163
169
  }
164
170
  get length() {
165
171
  return this.b.length;
166
172
  }
173
+ /** `n` raw bytes at `at`, as 0x-hex. */
174
+ raw(at, n) {
175
+ if (at < 0 || at + n > this.b.length) throw new Error("out of bounds");
176
+ return `0x${Array.from(this.b.subarray(at, at + n), (x) => x.toString(16).padStart(2, "0")).join("")}`;
177
+ }
178
+ /** The `bytes` whose offset word sits at `head` (relative to `base`), as a reader over its contents only. */
179
+ bytes(base, head) {
180
+ const at = base + this.small(head);
181
+ const n = this.small(at);
182
+ if (at + 32 + n > this.b.length) throw new Error("bytes out of bounds");
183
+ return new _Reader(this.b.subarray(at + 32, at + 32 + n));
184
+ }
167
185
  word(at) {
168
186
  if (at < 0 || at + 32 > this.b.length) throw new Error("out of bounds");
169
187
  let v = 0n;
@@ -221,6 +239,112 @@ function checkSteps(steps, e, finalAsset) {
221
239
  if (!same(last.receivingAssetId, finalAsset)) return `delivers ${last.receivingAssetId}, not ${finalAsset}`;
222
240
  return null;
223
241
  }
242
+ var MAYAN_REFUND_AT = {
243
+ "0xa3a30834": 4 + 32 * 3,
244
+ // Swift v2 createOrderWithToken(tokenIn, amountIn, (payloadType, trader, destAddr, …), customPayload)
245
+ "0x6147435b": 4 + 32 * 3,
246
+ // Swift v2 createOrderWithSig: the same order tuple
247
+ "0xb866e173": 4,
248
+ // Swift createOrderWithEth((trader, tokenOut, …))
249
+ "0x8e8d142b": 4 + 32 * 2,
250
+ // Swift createOrderWithToken(tokenIn, amountIn, (trader, tokenOut, …))
251
+ "0x6111ad25": 4 + 32 * 9,
252
+ // MayanSwap swap(relayerFees, (mayanAddr, mayanChainId, auctionAddr, destAddr, destChainId, referrer, refundAddr), …)
253
+ "0x1eb1cff0": 4 + 32 * 9
254
+ // MayanSwap wrapAndSwapETH: the same recipient tuple
255
+ };
256
+ function mayanRefunds(r, at) {
257
+ const p = r.bytes(at, at + 32 * 2);
258
+ const pos = MAYAN_REFUND_AT[p.raw(0, 4)];
259
+ return pos === void 0 ? [] : [["the Mayan order's refund address", p.address(pos)]];
260
+ }
261
+ function chainflipRefunds(r, at) {
262
+ const p = r.bytes(at, at + 32 * 5);
263
+ if (p.length < 25 || p.raw(0, 1) !== "0x01") throw new Error("unknown Chainflip parameters");
264
+ return [["the Chainflip refund address", p.raw(5, 20)]];
265
+ }
266
+ function acrossSwapRefunds(r, at) {
267
+ const target = r.word(at);
268
+ const c = r.bytes(at, at + 32);
269
+ if (target === 0n) return [["the Across deposit's depositor", c.address(c.small(0)), true]];
270
+ if (target === 1n) {
271
+ const swap = c.small(0);
272
+ return [["the Across deposit's depositor", c.address(swap + c.small(swap + 64) + 96), true]];
273
+ }
274
+ if (target === 2n) return [["the sponsored OFT refund recipient", c.address(c.small(0) + 32), true]];
275
+ if (target === 3n) return [["the sponsored CCTP refund recipient", c.address(64), true]];
276
+ throw new Error("unknown Across Swap API target");
277
+ }
278
+ var ERC20_TRANSFER = "0xa9059cbb";
279
+ function ecoDelivery(r, at, e) {
280
+ if (kindOf(e.toToken) === "native") return "an Eco route to the native token cannot be checked here";
281
+ const destination = r.word(at + 32 * 3);
282
+ if (destination !== e.destChainId) return `the Eco intent executes on chain ${destination}, not the destination ${e.destChainId} \u2014 a route whose delivery cannot be checked here`;
283
+ const route = r.bytes(at, at + 32 * 4);
284
+ const tuple = route.small(0);
285
+ const calls = tuple + route.small(tuple + 32 * 5);
286
+ const n = route.small(calls);
287
+ if (n === 0) return "the Eco route makes no calls on the destination chain";
288
+ if (n > 16) return `the Eco route makes ${n} calls on the destination chain, more than the 16 read here`;
289
+ const last = calls + 32 + route.small(calls + 32 + 32 * (n - 1));
290
+ const target = route.address(last);
291
+ const data = route.bytes(last, last + 32);
292
+ if (route.word(last + 64) !== 0n || data.length !== 68 || data.raw(0, 4) !== ERC20_TRANSFER) return "the Eco route does not end in a token transfer";
293
+ const to = data.address(4);
294
+ const amount = data.word(36);
295
+ if (!same(target, e.toToken)) return `the Eco route delivers ${target}, not ${e.toToken}`;
296
+ if (!same(to, e.receiver)) return `the Eco route pays ${to}, not ${e.receiver}`;
297
+ if (amount < e.minOut) return `the Eco route delivers ${amount}, below the ${e.minOut} shown`;
298
+ return null;
299
+ }
300
+ function m0Delivery(r, at, e) {
301
+ if (kindOf(e.toToken) === "native") return "an M0 order cannot deliver the native token";
302
+ const tokenOut = r.address(at + 32 * 3);
303
+ const amountOut = r.word(at + 32 * 5);
304
+ if (!same(tokenOut, e.toToken)) return `the M0 order asks for ${tokenOut}, not ${e.toToken}`;
305
+ if (amountOut < e.minOut) return `the M0 order asks for ${amountOut}, below the ${e.minOut} shown`;
306
+ return null;
307
+ }
308
+ var FACET_DATA = {
309
+ AcrossV4: { refunds: [["refundAddress", 1]], zeroRefused: true },
310
+ // the SpokePool depositor: an expired deposit refunds the input there
311
+ AcrossV4Swap: { nested: acrossSwapRefunds },
312
+ Centrifuge: { inline: 2, refunds: [["refundRecipient", 1]], zeroRefused: true },
313
+ Chainflip: { nested: chainflipRefunds },
314
+ Eco: { refunds: [["refundRecipient", 6]], zeroRefused: true, delivery: ecoDelivery },
315
+ // the intent's creator, refunded its reward (the input) on expiry
316
+ Frax: { inline: 5, refunds: [["refundRecipient", 3]], zeroRefused: true },
317
+ Glacis: { inline: 4, refunds: [["refundAddress", 1]], zeroRefused: true },
318
+ LayerSwap: { refunds: [["refundRecipient", 2]], zeroRefused: true },
319
+ LiFiIntentEscrowV2: { refunds: [["depositAndRefundAddress", 2]], zeroRefused: true },
320
+ // the escrow order's user
321
+ M0: { inline: 7, refunds: [["refundRecipient", 1], ["orderOwner", 2]], zeroRefused: true, delivery: m0Delivery },
322
+ // orderOwner gets a cancelled order back
323
+ Mayan: { refunds: [["refundRecipient", 7]], nested: mayanRefunds, zeroRefused: true },
324
+ NEARIntents: { refunds: [["refundRecipient", 6]], zeroRefused: true },
325
+ // also the 1Click quote's refundTo
326
+ PolymerCCTP: { refunds: [["refundRecipient", 5]], zeroRefused: true },
327
+ Stargate: { refunds: [["refundAddress", 4]] },
328
+ // (assetId, sendParams, fee.nativeFee, fee.lzTokenFee, refundAddress): LayerZero's fee refund
329
+ Superset: { refunds: [["refundAddress", 2], ["fallbackEoA", 3]], zeroRefused: true }
330
+ };
331
+ var NO_REFUND_FIELDS = /* @__PURE__ */ new Set(["Across", "AllBridge", "ArbitrumBridge", "CelerCircleBridge", "GasZip", "GnosisBridge", "MegaETHBridge", "OmniBridge", "OptimismBridge", "PolygonBridge", "Unit"]);
332
+ function facetDataAt(r, bridge, spec) {
333
+ const slot = 4 + (bridge.sourceSwaps ? 64 : 32);
334
+ return spec.inline ? slot : 4 + r.small(slot);
335
+ }
336
+ function refundMismatch(r, bridge, e) {
337
+ const spec = FACET_DATA[bridge.facet];
338
+ if (!spec) return NO_REFUND_FIELDS.has(bridge.facet) ? null : `no known refund layout for ${bridge.facet}`;
339
+ const at = facetDataAt(r, bridge, spec);
340
+ const paid = (spec.refunds ?? []).map(([field, word]) => [`${bridge.facet} ${field}`, r.address(at + 32 * word), spec.zeroRefused === true]);
341
+ paid.push(...spec.nested?.(r, at) ?? []);
342
+ for (const [what, to, zeroRefused] of paid) {
343
+ if (zeroRefused && /^0x0{40}$/.test(to)) continue;
344
+ if (!same(to, e.sender) && !same(to, e.receiver)) return `${what} pays ${to}, neither the sender ${e.sender} nor the recipient`;
345
+ }
346
+ return null;
347
+ }
224
348
  function diamondCallMismatch(data, e) {
225
349
  let r;
226
350
  try {
@@ -245,6 +369,8 @@ function diamondCallMismatch(data, e) {
245
369
  }
246
370
  return checkSteps(steps, e, e.toToken);
247
371
  }
372
+ const unbound = UNBOUND_BRIDGES[selector];
373
+ if (unbound) return `${unbound} delivers to a recipient the transaction does not bind (an off-chain order or unchecked facet data)`;
248
374
  const bridge = BRIDGE_CALLS[selector];
249
375
  if (!bridge) return `unknown Diamond call ${selector}`;
250
376
  const head = r.small(4);
@@ -263,6 +389,11 @@ function diamondCallMismatch(data, e) {
263
389
  if (!e.evmDestination) return "the recipient on a non-EVM destination sits in bridge-specific data and cannot be checked here";
264
390
  if (!same(receiver, e.receiver)) return `pays ${receiver}, not ${e.receiver}`;
265
391
  if (hasSourceSwaps !== bridge.sourceSwaps) return `unknown Diamond call ${selector}`;
392
+ const refund = refundMismatch(r, bridge, e);
393
+ if (refund) return refund;
394
+ const spec = FACET_DATA[bridge.facet];
395
+ const delivery = spec?.delivery?.(r, facetDataAt(r, bridge, spec), e);
396
+ if (delivery) return delivery;
266
397
  if (hasSourceSwaps) {
267
398
  if (minAmount === 0n) return "bridges nothing after the source swaps";
268
399
  return checkSteps(swapsAt(r, 4, 4 + 32), e, sendingAssetId);
@@ -274,8 +405,98 @@ function diamondCallMismatch(data, e) {
274
405
  }
275
406
  }
276
407
 
408
+ // src/solana.ts
409
+ import { base64Bytes } from "@anywalletkit/protocol-swap2-core/util";
410
+ var ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
411
+ var MAYAN_ENTRY_PROGRAMS = /* @__PURE__ */ new Set([
412
+ "D8C8iW6zmoKg5TRr8nQ7h14TMWqQX8FiBdj2ju5MF3wa",
413
+ // CPI_PROXY_PROGRAM_ID
414
+ "BLZRi6frs4X4DNLw56V4EXai1b6QVESN1BhHBTYM9VcY",
415
+ // SWIFT_PROGRAM_ID
416
+ "mayan34VedncxdK2XobtvWFDXQASUTBXhUVzt2kKgny",
417
+ // SWIFT_V2_PROGRAM_ID
418
+ "dkpZqrxHFrhziEMQ931GLtfy11nFkCsfMftH9u6QwBU",
419
+ // MCTP_PROGRAM_ID
420
+ "Gx9rivpS3YR8pBFwMuP6omYqVxunpLvLkNn7ubNyuZZ5"
421
+ // FAST_MCTP_PROGRAM_ID
422
+ ]);
423
+ var SYSTEM_PROGRAM = "11111111111111111111111111111111";
424
+ var LIFI_SOLANA_FEE_WALLET = "34FKjAdVcTax2DHqV2XnbXa9J3zmyKcFuFKWbcmgxjgm";
425
+ function base58(bytes) {
426
+ let n = 0n;
427
+ for (const b of bytes) n = n << 8n | BigInt(b);
428
+ let out = "";
429
+ while (n > 0n) {
430
+ out = ALPHABET[Number(n % 58n)] + out;
431
+ n /= 58n;
432
+ }
433
+ for (const b of bytes) {
434
+ if (b !== 0) break;
435
+ out = `1${out}`;
436
+ }
437
+ return out;
438
+ }
439
+ var Cursor = class {
440
+ constructor(b) {
441
+ this.b = b;
442
+ }
443
+ b;
444
+ at = 0;
445
+ byte() {
446
+ if (this.at >= this.b.length) throw new Error("truncated");
447
+ return this.b[this.at++];
448
+ }
449
+ bytes(n) {
450
+ if (n < 0 || this.at + n > this.b.length) throw new Error("truncated");
451
+ const out = this.b.subarray(this.at, this.at + n);
452
+ this.at += n;
453
+ return out;
454
+ }
455
+ /** compact-u16 */
456
+ short() {
457
+ let v = 0;
458
+ for (let shift = 0; shift < 21; shift += 7) {
459
+ const b = this.byte();
460
+ v |= (b & 127) << shift;
461
+ if (!(b & 128)) return v;
462
+ }
463
+ throw new Error("bad length");
464
+ }
465
+ };
466
+ function solanaBridgeTxMismatch(base64, sender) {
467
+ const bytes = base64Bytes(base64);
468
+ if (!bytes) return "the Solana transaction is not base64";
469
+ const c = new Cursor(bytes);
470
+ try {
471
+ c.bytes(64 * c.short());
472
+ const first = c.byte();
473
+ if ((first & 128) === 0) c.at--;
474
+ c.bytes(3);
475
+ const keys = Array.from({ length: c.short() }, () => base58(c.bytes(32)));
476
+ if (keys[0] !== sender) return `the Solana transaction is paid by ${keys[0] ?? "nobody"}, not the sender ${sender}`;
477
+ c.bytes(32);
478
+ let mayan = false;
479
+ const payees = [];
480
+ const n = c.short();
481
+ for (let i = 0; i < n; i++) {
482
+ const program = keys[c.byte()];
483
+ const accounts = Array.from(c.bytes(c.short()));
484
+ const data = c.bytes(c.short());
485
+ if (program !== void 0 && MAYAN_ENTRY_PROGRAMS.has(program)) mayan = true;
486
+ if (program === SYSTEM_PROGRAM && data.length === 12 && data[0] === 2 && data[1] === 0 && data[2] === 0 && data[3] === 0) {
487
+ const to = keys[accounts[1] ?? -1];
488
+ if (to !== void 0 && to !== sender && to !== LIFI_SOLANA_FEE_WALLET) payees.push(to);
489
+ }
490
+ }
491
+ return mayan && payees.length ? `the Solana transaction transfers SOL to ${payees[0]}, which is neither the sender nor LI.FI's fee wallet` : null;
492
+ } catch (err) {
493
+ return `the Solana transaction does not decode (${err instanceof Error ? err.message : String(err)})`;
494
+ }
495
+ }
496
+
277
497
  // src/provider.ts
278
498
  import { timeoutSignal, sourceAssetOf } from "@anywalletkit/protocol-swap2-core/util";
499
+ import { slippageFloor } from "@anywalletkit/protocol-swap2-core/validate";
279
500
  import { bigintToNumber } from "@anywalletkit/core";
280
501
  var DEFAULT_LIFI_BASE = "https://li.quest/v1";
281
502
  var LIFI_CHAIN = {
@@ -412,7 +633,8 @@ function headers(key) {
412
633
  return h;
413
634
  }
414
635
  var TOOL_KEY = /^[A-Za-z0-9_-]{1,64}$/;
415
- function routeOptions(body) {
636
+ var UNBOUND_TOOLS = Object.freeze(["relaydepository", "garden", "squid", "symbiosis"]);
637
+ function routeOptions(body, excluded) {
416
638
  const routes = body?.routes;
417
639
  if (!Array.isArray(routes)) return [];
418
640
  const out = [];
@@ -422,7 +644,7 @@ function routeOptions(body) {
422
644
  const cross = Array.isArray(step.includedSteps) ? step.includedSteps.find((i) => i?.type === "cross")?.tool : void 0;
423
645
  const tool = typeof cross === "string" ? cross : step.tool;
424
646
  const eta = step.estimate?.executionDuration;
425
- if (typeof tool !== "string" || !TOOL_KEY.test(tool) || typeof eta !== "number" || !Number.isFinite(eta) || eta < 0) continue;
647
+ if (typeof tool !== "string" || !TOOL_KEY.test(tool) || excluded.includes(tool) || typeof eta !== "number" || !Number.isFinite(eta) || eta < 0) continue;
426
648
  if (typeof r.toAmount !== "string" || typeof r.toAmountMin !== "string" || !/^\d{1,60}$/.test(r.toAmount) || !/^\d{1,60}$/.test(r.toAmountMin)) continue;
427
649
  out.push({
428
650
  tool,
@@ -486,6 +708,9 @@ function lifiProvider(config = {}) {
486
708
  const fromToken = lifiToken(params);
487
709
  if (!fromToken) return null;
488
710
  const solanaSource = isSolana(params.sourceChain);
711
+ const crossChain = params.sourceChain !== params.destChain;
712
+ const noDestinationCall = crossChain && !solanaSource;
713
+ let routeScope = config.routeWideSlippage === true && !solanaSource && !isSolana(params.destChain);
489
714
  const dry = params.dry !== false;
490
715
  const fromAddress = params.refundAddress || (dry ? solanaSource ? SOLANA_PLACEHOLDER : EVM_PLACEHOLDER : "");
491
716
  const toAddress = params.destAddress || (dry ? fromAddress : "");
@@ -495,7 +720,7 @@ function lifiProvider(config = {}) {
495
720
  let sentSlippage = slippageBps / 1e4;
496
721
  let deadline;
497
722
  let choice = null;
498
- if (config.chooseRoute && params.sourceChain !== params.destChain) {
723
+ if (config.chooseRoute && crossChain) {
499
724
  try {
500
725
  const res = await fetch(`${apiBase}/advanced/routes`, {
501
726
  method: "POST",
@@ -508,12 +733,18 @@ function lifiProvider(config = {}) {
508
733
  fromAmount: params.amountIn,
509
734
  fromAddress,
510
735
  toAddress,
511
- options: { slippage: slippageBps / 1e4, ...integrator().trim() ? { integrator: integrator().trim() } : {} }
736
+ options: {
737
+ slippage: slippageBps / 1e4,
738
+ ...routeScope ? { slippageScope: "route" } : {},
739
+ ...solanaSource ? {} : { bridges: { deny: UNBOUND_TOOLS } },
740
+ ...noDestinationCall ? { allowDestinationCall: false } : {},
741
+ ...integrator().trim() ? { integrator: integrator().trim() } : {}
742
+ }
512
743
  }),
513
744
  signal: signal ?? timeoutSignal(8e3)
514
745
  });
515
746
  if (res.ok) {
516
- const options = routeOptions(await res.json());
747
+ const options = routeOptions(await res.json(), solanaSource ? [] : UNBOUND_TOOLS);
517
748
  if (options.length) choice = cleanChoice(config.chooseRoute(options, params));
518
749
  }
519
750
  } catch {
@@ -533,8 +764,11 @@ function lifiProvider(config = {}) {
533
764
  if (sentSlippage !== void 0) {
534
765
  url.searchParams.set("slippage", String(sentSlippage));
535
766
  }
536
- for (const t of choice?.allowBridges ?? []) url.searchParams.append("allowBridges", t);
537
- for (const t of choice?.denyBridges ?? []) url.searchParams.append("denyBridges", t);
767
+ if (routeScope) url.searchParams.set("slippageScope", "route");
768
+ if (noDestinationCall) url.searchParams.set("allowDestinationCall", "false");
769
+ const unbound = crossChain && !solanaSource ? UNBOUND_TOOLS : [];
770
+ for (const t of (choice?.allowBridges ?? []).filter((b) => solanaSource || !UNBOUND_TOOLS.includes(b))) url.searchParams.append("allowBridges", t);
771
+ for (const t of /* @__PURE__ */ new Set([...choice?.denyBridges ?? [], ...unbound])) url.searchParams.append("denyBridges", t);
538
772
  if (choice?.order) url.searchParams.set("order", choice.order);
539
773
  deadline = signal ? void 0 : timeoutSignal(12e3);
540
774
  const res = await fetch(url, { headers: headers(apiKey()), signal: signal ?? deadline });
@@ -546,6 +780,11 @@ function lifiProvider(config = {}) {
546
780
  if (!res.ok) {
547
781
  const body = await res.json().catch(() => null);
548
782
  const reasons = [...new Set((body?.errors?.filteredOut ?? []).map((f) => f.reason).filter((r) => typeof r === "string"))];
783
+ if (routeScope && [typeof body?.message === "string" ? body.message : "", ...reasons].some((r) => /^ROUTE_SLIPPAGE_(UNSUPPORTED|UNAVAILABLE)/.test(r))) {
784
+ routeScope = false;
785
+ attempt--;
786
+ continue;
787
+ }
549
788
  const why = reasons.length === 1 ? ` (${reasons[0].slice(0, 200)})` : "";
550
789
  params.onRefusal?.(`lifi: ${typeof body?.message === "string" ? body.message.slice(0, 200) : `HTTP ${res.status}`}${why}`);
551
790
  return null;
@@ -578,6 +817,10 @@ function lifiProvider(config = {}) {
578
817
  if (solanaSource) {
579
818
  if (!tr.data || tr.data.length < 64 || tr.data.length % 4 !== 0 || !BASE64.test(tr.data)) return reject("the Solana transaction is not base64");
580
819
  if (tr.to !== void 0 || tr.value !== void 0 || tr.gasLimit !== void 0) return reject("the Solana transaction carries EVM fields");
820
+ if (fromChain !== toChain) {
821
+ const why = solanaBridgeTxMismatch(tr.data, fromAddress);
822
+ if (why) return reject(why);
823
+ }
581
824
  } else if (!isHexData(tr.data) || !normalizeAddress(tr.to) || !normalizeAddress(tr.from) || tr.chainId !== expectedFromChain || !tr.value || !tr.gasLimit || !trustedAddress(params.sourceChain, tr.to, trustedTransactionTargets) || !sameAddress(tr.from, fromAddress)) return reject("the transaction is malformed or targets an untrusted contract");
582
825
  if (sentSlippage !== void 0 && (typeof action.slippage !== "number" || Math.abs(action.slippage - sentSlippage) > 1e-12)) return reject("the quote ignored the requested slippage");
583
826
  const sourceIsToken = fromToken !== EVM_ZERO && fromToken !== SOLANA_NATIVE;
@@ -615,7 +858,7 @@ function lifiProvider(config = {}) {
615
858
  if (sentSlippage !== void 0) {
616
859
  const dest = BigInt(est.toAmount);
617
860
  const min = BigInt(minDestAmount);
618
- if (dest > 0n && min < dest * BigInt(1e4 - slippageBps) / 10000n) {
861
+ if (dest > 0n && min < slippageFloor(dest, slippageBps)) {
619
862
  const effective = bigintToNumber((dest - min) * 1000000n / dest) / 1e6;
620
863
  const requested = slippageBps / 1e4;
621
864
  const scaled = Math.floor(sentSlippage * requested / effective * 0.995 * 1e6) / 1e6;
@@ -644,6 +887,7 @@ function lifiProvider(config = {}) {
644
887
  toToken: expectedToToken,
645
888
  amountIn: BigInt(params.amountIn),
646
889
  receiver: toAddress,
890
+ sender: fromAddress,
647
891
  minOut: BigInt(minDestAmount),
648
892
  destChainId: BigInt(toChain),
649
893
  sameChain: fromChain === toChain,
@@ -662,6 +906,7 @@ function lifiProvider(config = {}) {
662
906
  sourceAmount: params.amountIn,
663
907
  destAmount: est.toAmount,
664
908
  minDestAmount,
909
+ ...Number.isInteger(action.toToken?.decimals) && action.toToken.decimals >= 0 && action.toToken.decimals <= 36 ? { destDecimals: action.toToken.decimals } : {},
665
910
  feeUsd,
666
911
  etaSeconds: est.executionDuration ?? 120,
667
912
  reliability: 0.97,
@@ -729,7 +974,10 @@ function lifiVerifyQuote(params, quote) {
729
974
  if (!fromChain || !toChain) return "not a LI.FI chain pair";
730
975
  const tx = quote.txRequest;
731
976
  if (!tx) return "the quote carries no transaction";
732
- if (isSolana(params.sourceChain)) return tx.serializedTx && tx.data === void 0 && tx.to === void 0 ? null : "a Solana source carries a serialized transaction only";
977
+ if (isSolana(params.sourceChain)) {
978
+ if (!tx.serializedTx || tx.data !== void 0 || tx.to !== void 0) return "a Solana source carries a serialized transaction only";
979
+ return fromChain === toChain ? null : solanaBridgeTxMismatch(tx.serializedTx, params.refundAddress || (params.dry !== false ? SOLANA_PLACEHOLDER : ""));
980
+ }
733
981
  if (!isHexData(tx.data) || !tx.to || tx.serializedTx !== void 0) return "the quote carries no Diamond call";
734
982
  if (!sameAddress(tx.to, diamondFor(params.sourceChain))) return `the call targets ${tx.to}, not the LI.FI Diamond`;
735
983
  if (tx.chainId !== void 0 && String(tx.chainId) !== fromChain) return `the call is for chain ${tx.chainId}, not ${fromChain}`;
@@ -764,6 +1012,7 @@ function lifiVerifyQuote(params, quote) {
764
1012
  toToken: nativeFor(params.destChain, params.destToken),
765
1013
  amountIn,
766
1014
  receiver: toAddress,
1015
+ sender: fromAddress,
767
1016
  minOut,
768
1017
  destChainId: BigInt(toChain),
769
1018
  sameChain: fromChain === toChain,
@@ -782,4 +1031,4 @@ export {
782
1031
  lifiVerifyQuote,
783
1032
  lifiPins
784
1033
  };
785
- //# sourceMappingURL=chunk-TQYG23FS.js.map
1034
+ //# sourceMappingURL=chunk-IZ7TP4KT.js.map