@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.
package/dist/index.cjs CHANGED
@@ -101,144 +101,162 @@ var GENERIC_SWAPS = {
101
101
  var BRIDGE_INPUT_FEE_MAX_BPS = 100n;
102
102
  var kindOf = (asset) => /^0x0{40}$/i.test(asset) ? "native" : "erc20";
103
103
  var BRIDGE_CALLS = {
104
- "0x1fd8010c": { sourceSwaps: false },
104
+ "0x1fd8010c": { sourceSwaps: false, facet: "Across" },
105
105
  // startBridgeTokensViaAcross (AcrossFacet)
106
- "0x3a3f7332": { sourceSwaps: true },
106
+ "0x3a3f7332": { sourceSwaps: true, facet: "Across" },
107
107
  // swapAndStartBridgeTokensViaAcross (AcrossFacet)
108
- "0xa1f1ce43": { sourceSwaps: false },
108
+ "0xa1f1ce43": { sourceSwaps: false, facet: "AcrossV4" },
109
109
  // startBridgeTokensViaAcrossV4 (AcrossFacetV4)
110
- "0x1794958f": { sourceSwaps: true },
110
+ "0x1794958f": { sourceSwaps: true, facet: "AcrossV4" },
111
111
  // swapAndStartBridgeTokensViaAcrossV4 (AcrossFacetV4)
112
- "0x6a90d66e": { sourceSwaps: false },
112
+ "0x6a90d66e": { sourceSwaps: false, facet: "AcrossV4Swap" },
113
113
  // startBridgeTokensViaAcrossV4Swap (AcrossV4SwapFacet)
114
- "0x9b054bc4": { sourceSwaps: true },
114
+ "0x9b054bc4": { sourceSwaps: true, facet: "AcrossV4Swap" },
115
115
  // swapAndStartBridgeTokensViaAcrossV4Swap (AcrossV4SwapFacet)
116
- "0x6a51e9a9": { sourceSwaps: false },
116
+ "0x6a51e9a9": { sourceSwaps: false, facet: "AllBridge" },
117
117
  // startBridgeTokensViaAllBridge (AllBridgeFacet)
118
- "0x63267469": { sourceSwaps: true },
118
+ "0x63267469": { sourceSwaps: true, facet: "AllBridge" },
119
119
  // swapAndStartBridgeTokensViaAllBridge (AllBridgeFacet)
120
- "0xc9851d0b": { sourceSwaps: false },
120
+ "0xc9851d0b": { sourceSwaps: false, facet: "ArbitrumBridge" },
121
121
  // startBridgeTokensViaArbitrumBridge (ArbitrumBridgeFacet)
122
- "0x3cc9517b": { sourceSwaps: true },
122
+ "0x3cc9517b": { sourceSwaps: true, facet: "ArbitrumBridge" },
123
123
  // swapAndStartBridgeTokensViaArbitrumBridge (ArbitrumBridgeFacet)
124
- "0xe0a4201c": { sourceSwaps: false },
124
+ "0xe0a4201c": { sourceSwaps: false, facet: "CelerCircleBridge" },
125
125
  // startBridgeTokensViaCelerCircleBridge (CelerCircleBridgeFacet)
126
- "0x4f3b0759": { sourceSwaps: true },
126
+ "0x4f3b0759": { sourceSwaps: true, facet: "CelerCircleBridge" },
127
127
  // swapAndStartBridgeTokensViaCelerCircleBridge (CelerCircleBridgeFacet)
128
- "0x0315138f": { sourceSwaps: false },
128
+ "0x0315138f": { sourceSwaps: false, facet: "Centrifuge" },
129
129
  // startBridgeTokensViaCentrifuge (CentrifugeFacet)
130
- "0x8fd4d4dd": { sourceSwaps: true },
130
+ "0x8fd4d4dd": { sourceSwaps: true, facet: "Centrifuge" },
131
131
  // swapAndStartBridgeTokensViaCentrifuge (CentrifugeFacet)
132
- "0x0ad553b3": { sourceSwaps: false },
132
+ "0x0ad553b3": { sourceSwaps: false, facet: "Chainflip" },
133
133
  // startBridgeTokensViaChainflip (ChainflipFacet)
134
- "0xee3314a1": { sourceSwaps: true },
134
+ "0xee3314a1": { sourceSwaps: true, facet: "Chainflip" },
135
135
  // swapAndStartBridgeTokensViaChainflip (ChainflipFacet)
136
- "0x4004633e": { sourceSwaps: false },
137
- // startBridgeTokensViaDeBridgeDln (DeBridgeDlnFacet)
138
- "0x2c7d2db0": { sourceSwaps: true },
139
- // swapAndStartBridgeTokensViaDeBridgeDln (DeBridgeDlnFacet)
140
- "0xbff90b61": { sourceSwaps: false },
141
- // startBridgeTokensViaEco (EcoFacet)
142
- "0x762aea18": { sourceSwaps: true },
143
- // swapAndStartBridgeTokensViaEco (EcoFacet)
144
- "0x678786ea": { sourceSwaps: false },
136
+ "0x05c54990": { sourceSwaps: false, facet: "Eco" },
137
+ // startBridgeTokensViaEco (EcoFacet v3.0.0, 9001792)
138
+ "0x8352664c": { sourceSwaps: true, facet: "Eco" },
139
+ // swapAndStartBridgeTokensViaEco (EcoFacet v3.0.0, 9001792)
140
+ "0x678786ea": { sourceSwaps: false, facet: "Frax" },
145
141
  // startBridgeTokensViaFrax (FraxFacet)
146
- "0x07b8e4c7": { sourceSwaps: true },
142
+ "0x07b8e4c7": { sourceSwaps: true, facet: "Frax" },
147
143
  // swapAndStartBridgeTokensViaFrax (FraxFacet)
148
- "0x5f9af35d": { sourceSwaps: false },
149
- // startBridgeTokensViaGarden (GardenFacet)
150
- "0x76ad76fe": { sourceSwaps: true },
151
- // swapAndStartBridgeTokensViaGarden (GardenFacet)
152
- "0xfc5f1003": { sourceSwaps: false },
144
+ "0xfc5f1003": { sourceSwaps: false, facet: "GasZip" },
153
145
  // startBridgeTokensViaGasZip (GasZipFacet)
154
- "0x606326ff": { sourceSwaps: true },
146
+ "0x606326ff": { sourceSwaps: true, facet: "GasZip" },
155
147
  // swapAndStartBridgeTokensViaGasZip (GasZipFacet)
156
- "0x6f9206ba": { sourceSwaps: false },
148
+ "0x6f9206ba": { sourceSwaps: false, facet: "Glacis" },
157
149
  // startBridgeTokensViaGlacis (GlacisFacet)
158
- "0x9c4b6dd9": { sourceSwaps: true },
150
+ "0x9c4b6dd9": { sourceSwaps: true, facet: "Glacis" },
159
151
  // swapAndStartBridgeTokensViaGlacis (GlacisFacet)
160
- "0xf66fe519": { sourceSwaps: false },
152
+ "0xf66fe519": { sourceSwaps: false, facet: "GnosisBridge" },
161
153
  // startBridgeTokensViaGnosisBridge (GnosisBridgeFacet)
162
- "0x7bf96e0a": { sourceSwaps: true },
154
+ "0x7bf96e0a": { sourceSwaps: true, facet: "GnosisBridge" },
163
155
  // swapAndStartBridgeTokensViaGnosisBridge (GnosisBridgeFacet)
164
- "0xee9e98e0": { sourceSwaps: false },
156
+ "0xee9e98e0": { sourceSwaps: false, facet: "LayerSwap" },
165
157
  // startBridgeTokensViaLayerSwap (LayerSwapFacet)
166
- "0x4c279d6b": { sourceSwaps: true },
158
+ "0x4c279d6b": { sourceSwaps: true, facet: "LayerSwap" },
167
159
  // swapAndStartBridgeTokensViaLayerSwap (LayerSwapFacet)
168
- "0x7dbcf1d9": { sourceSwaps: false },
160
+ "0x7dbcf1d9": { sourceSwaps: false, facet: "LiFiIntentEscrowV2" },
169
161
  // startBridgeTokensViaLiFiIntentEscrowV2 (LiFiIntentEscrowFacetV2)
170
- "0x6d21c5df": { sourceSwaps: true },
162
+ "0x6d21c5df": { sourceSwaps: true, facet: "LiFiIntentEscrowV2" },
171
163
  // swapAndStartBridgeTokensViaLiFiIntentEscrowV2 (LiFiIntentEscrowFacetV2)
172
- "0x65782c4b": { sourceSwaps: false },
164
+ "0x65782c4b": { sourceSwaps: false, facet: "M0" },
173
165
  // startBridgeTokensViaM0 (M0Facet)
174
- "0xb9eaf86d": { sourceSwaps: true },
166
+ "0xb9eaf86d": { sourceSwaps: true, facet: "M0" },
175
167
  // swapAndStartBridgeTokensViaM0 (M0Facet)
176
- "0xb4875292": { sourceSwaps: false },
168
+ "0xb4875292": { sourceSwaps: false, facet: "Mayan" },
177
169
  // startBridgeTokensViaMayan (MayanFacet)
178
- "0x80c65808": { sourceSwaps: true },
170
+ "0x80c65808": { sourceSwaps: true, facet: "Mayan" },
179
171
  // swapAndStartBridgeTokensViaMayan (MayanFacet)
180
- "0x4213dfff": { sourceSwaps: false },
172
+ "0x4213dfff": { sourceSwaps: false, facet: "MegaETHBridge" },
181
173
  // startBridgeTokensViaMegaETHBridge (MegaETHBridgeFacet)
182
- "0x22256e89": { sourceSwaps: true },
174
+ "0x22256e89": { sourceSwaps: true, facet: "MegaETHBridge" },
183
175
  // swapAndStartBridgeTokensViaMegaETHBridge (MegaETHBridgeFacet)
184
- "0x4698f032": { sourceSwaps: false },
176
+ "0x4698f032": { sourceSwaps: false, facet: "NEARIntents" },
185
177
  // startBridgeTokensViaNEARIntents (NEARIntentsFacet)
186
- "0x02631b09": { sourceSwaps: true },
178
+ "0x02631b09": { sourceSwaps: true, facet: "NEARIntents" },
187
179
  // swapAndStartBridgeTokensViaNEARIntents (NEARIntentsFacet)
188
- "0x782621d8": { sourceSwaps: false },
180
+ "0x782621d8": { sourceSwaps: false, facet: "OmniBridge" },
189
181
  // startBridgeTokensViaOmniBridge (OmniBridgeFacet)
190
- "0x95726782": { sourceSwaps: true },
182
+ "0x95726782": { sourceSwaps: true, facet: "OmniBridge" },
191
183
  // swapAndStartBridgeTokensViaOmniBridge (OmniBridgeFacet)
192
- "0xce8a97a5": { sourceSwaps: false },
184
+ "0xce8a97a5": { sourceSwaps: false, facet: "OptimismBridge" },
193
185
  // startBridgeTokensViaOptimismBridge (OptimismBridgeFacet)
194
- "0x5bb5d448": { sourceSwaps: true },
186
+ "0x5bb5d448": { sourceSwaps: true, facet: "OptimismBridge" },
195
187
  // swapAndStartBridgeTokensViaOptimismBridge (OptimismBridgeFacet)
196
- "0xaf62c7d6": { sourceSwaps: false },
188
+ "0xaf62c7d6": { sourceSwaps: false, facet: "PolygonBridge" },
197
189
  // startBridgeTokensViaPolygonBridge (PolygonBridgeFacet)
198
- "0xb4f37581": { sourceSwaps: true },
190
+ "0xb4f37581": { sourceSwaps: true, facet: "PolygonBridge" },
199
191
  // swapAndStartBridgeTokensViaPolygonBridge (PolygonBridgeFacet)
200
- "0xf434d6ca": { sourceSwaps: false },
192
+ "0xf434d6ca": { sourceSwaps: false, facet: "PolymerCCTP" },
201
193
  // startBridgeTokensViaPolymerCCTP (PolymerCCTPFacet)
202
- "0x17917a4e": { sourceSwaps: true },
194
+ "0x17917a4e": { sourceSwaps: true, facet: "PolymerCCTP" },
203
195
  // swapAndStartBridgeTokensViaPolymerCCTP (PolymerCCTPFacet)
204
- "0x092e8fa4": { sourceSwaps: false },
205
- // startBridgeTokensViaRelayDepository (RelayDepositoryFacet)
206
- "0xa3443faa": { sourceSwaps: true },
207
- // swapAndStartBridgeTokensViaRelayDepository (RelayDepositoryFacet)
208
- "0x3f313808": { sourceSwaps: false },
209
- // startBridgeTokensViaSquid (SquidFacet)
210
- "0xa8f66666": { sourceSwaps: true },
211
- // swapAndStartBridgeTokensViaSquid (SquidFacet)
212
- "0x14d53077": { sourceSwaps: false },
196
+ "0x14d53077": { sourceSwaps: false, facet: "Stargate" },
213
197
  // startBridgeTokensViaStargate (StargateFacetV2)
214
- "0xa6010a66": { sourceSwaps: true },
198
+ "0xa6010a66": { sourceSwaps: true, facet: "Stargate" },
215
199
  // swapAndStartBridgeTokensViaStargate (StargateFacetV2)
216
- "0x0691ff78": { sourceSwaps: false },
200
+ "0x0691ff78": { sourceSwaps: false, facet: "Superset" },
217
201
  // startBridgeTokensViaSuperset (SupersetFacet)
218
- "0xf26e657f": { sourceSwaps: true },
202
+ "0xf26e657f": { sourceSwaps: true, facet: "Superset" },
219
203
  // swapAndStartBridgeTokensViaSuperset (SupersetFacet)
220
- "0xe23b7a08": { sourceSwaps: false },
221
- // startBridgeTokensViaSymbiosis (SymbiosisFacet)
222
- "0xc46059b2": { sourceSwaps: true },
223
- // swapAndStartBridgeTokensViaSymbiosis (SymbiosisFacet)
224
- "0x2541ec57": { sourceSwaps: false },
225
- // startBridgeTokensViaThorSwap (ThorSwapFacet)
226
- "0xad673d88": { sourceSwaps: true },
227
- // swapAndStartBridgeTokensViaThorSwap (ThorSwapFacet)
228
- "0x64261d58": { sourceSwaps: false },
204
+ "0x64261d58": { sourceSwaps: false, facet: "Unit" },
229
205
  // startBridgeTokensViaUnit (UnitFacet)
230
- "0x21a3af52": { sourceSwaps: true }
206
+ "0x21a3af52": { sourceSwaps: true, facet: "Unit" }
231
207
  // swapAndStartBridgeTokensViaUnit (UnitFacet)
232
208
  };
233
- var Reader = class {
209
+ var UNBOUND_BRIDGES = {
210
+ "0x092e8fa4": "Relay",
211
+ // startBridgeTokensViaRelayDepository
212
+ "0xa3443faa": "Relay",
213
+ // swapAndStartBridgeTokensViaRelayDepository
214
+ "0x5f9af35d": "Garden",
215
+ // startBridgeTokensViaGarden
216
+ "0x76ad76fe": "Garden",
217
+ // swapAndStartBridgeTokensViaGarden
218
+ "0x4004633e": "deBridge DLN",
219
+ // startBridgeTokensViaDeBridgeDln
220
+ "0x2c7d2db0": "deBridge DLN",
221
+ // swapAndStartBridgeTokensViaDeBridgeDln
222
+ "0x2541ec57": "ThorSwap",
223
+ // startBridgeTokensViaThorSwap
224
+ "0xad673d88": "ThorSwap",
225
+ // swapAndStartBridgeTokensViaThorSwap
226
+ "0x3f313808": "Squid",
227
+ // startBridgeTokensViaSquid
228
+ "0xa8f66666": "Squid",
229
+ // swapAndStartBridgeTokensViaSquid
230
+ "0xe23b7a08": "Symbiosis",
231
+ // startBridgeTokensViaSymbiosis
232
+ "0xc46059b2": "Symbiosis"
233
+ // swapAndStartBridgeTokensViaSymbiosis
234
+ };
235
+ var Reader = class _Reader {
234
236
  b;
235
- constructor(hex) {
236
- if (!/^0x([0-9a-fA-F]{2})*$/.test(hex)) throw new Error("not hex");
237
- this.b = Uint8Array.from(hex.slice(2).match(/../g) ?? [], (h) => Number.parseInt(h, 16));
237
+ constructor(src) {
238
+ if (typeof src !== "string") {
239
+ this.b = src;
240
+ return;
241
+ }
242
+ if (!/^0x([0-9a-fA-F]{2})*$/.test(src)) throw new Error("not hex");
243
+ this.b = Uint8Array.from(src.slice(2).match(/../g) ?? [], (h) => Number.parseInt(h, 16));
238
244
  }
239
245
  get length() {
240
246
  return this.b.length;
241
247
  }
248
+ /** `n` raw bytes at `at`, as 0x-hex. */
249
+ raw(at, n) {
250
+ if (at < 0 || at + n > this.b.length) throw new Error("out of bounds");
251
+ return `0x${Array.from(this.b.subarray(at, at + n), (x) => x.toString(16).padStart(2, "0")).join("")}`;
252
+ }
253
+ /** The `bytes` whose offset word sits at `head` (relative to `base`), as a reader over its contents only. */
254
+ bytes(base, head) {
255
+ const at = base + this.small(head);
256
+ const n = this.small(at);
257
+ if (at + 32 + n > this.b.length) throw new Error("bytes out of bounds");
258
+ return new _Reader(this.b.subarray(at + 32, at + 32 + n));
259
+ }
242
260
  word(at) {
243
261
  if (at < 0 || at + 32 > this.b.length) throw new Error("out of bounds");
244
262
  let v = 0n;
@@ -296,6 +314,112 @@ function checkSteps(steps, e, finalAsset) {
296
314
  if (!same(last.receivingAssetId, finalAsset)) return `delivers ${last.receivingAssetId}, not ${finalAsset}`;
297
315
  return null;
298
316
  }
317
+ var MAYAN_REFUND_AT = {
318
+ "0xa3a30834": 4 + 32 * 3,
319
+ // Swift v2 createOrderWithToken(tokenIn, amountIn, (payloadType, trader, destAddr, …), customPayload)
320
+ "0x6147435b": 4 + 32 * 3,
321
+ // Swift v2 createOrderWithSig: the same order tuple
322
+ "0xb866e173": 4,
323
+ // Swift createOrderWithEth((trader, tokenOut, …))
324
+ "0x8e8d142b": 4 + 32 * 2,
325
+ // Swift createOrderWithToken(tokenIn, amountIn, (trader, tokenOut, …))
326
+ "0x6111ad25": 4 + 32 * 9,
327
+ // MayanSwap swap(relayerFees, (mayanAddr, mayanChainId, auctionAddr, destAddr, destChainId, referrer, refundAddr), …)
328
+ "0x1eb1cff0": 4 + 32 * 9
329
+ // MayanSwap wrapAndSwapETH: the same recipient tuple
330
+ };
331
+ function mayanRefunds(r, at) {
332
+ const p = r.bytes(at, at + 32 * 2);
333
+ const pos = MAYAN_REFUND_AT[p.raw(0, 4)];
334
+ return pos === void 0 ? [] : [["the Mayan order's refund address", p.address(pos)]];
335
+ }
336
+ function chainflipRefunds(r, at) {
337
+ const p = r.bytes(at, at + 32 * 5);
338
+ if (p.length < 25 || p.raw(0, 1) !== "0x01") throw new Error("unknown Chainflip parameters");
339
+ return [["the Chainflip refund address", p.raw(5, 20)]];
340
+ }
341
+ function acrossSwapRefunds(r, at) {
342
+ const target = r.word(at);
343
+ const c = r.bytes(at, at + 32);
344
+ if (target === 0n) return [["the Across deposit's depositor", c.address(c.small(0)), true]];
345
+ if (target === 1n) {
346
+ const swap = c.small(0);
347
+ return [["the Across deposit's depositor", c.address(swap + c.small(swap + 64) + 96), true]];
348
+ }
349
+ if (target === 2n) return [["the sponsored OFT refund recipient", c.address(c.small(0) + 32), true]];
350
+ if (target === 3n) return [["the sponsored CCTP refund recipient", c.address(64), true]];
351
+ throw new Error("unknown Across Swap API target");
352
+ }
353
+ var ERC20_TRANSFER = "0xa9059cbb";
354
+ function ecoDelivery(r, at, e) {
355
+ if (kindOf(e.toToken) === "native") return "an Eco route to the native token cannot be checked here";
356
+ const destination = r.word(at + 32 * 3);
357
+ 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`;
358
+ const route = r.bytes(at, at + 32 * 4);
359
+ const tuple = route.small(0);
360
+ const calls = tuple + route.small(tuple + 32 * 5);
361
+ const n = route.small(calls);
362
+ if (n === 0) return "the Eco route makes no calls on the destination chain";
363
+ if (n > 16) return `the Eco route makes ${n} calls on the destination chain, more than the 16 read here`;
364
+ const last = calls + 32 + route.small(calls + 32 + 32 * (n - 1));
365
+ const target = route.address(last);
366
+ const data = route.bytes(last, last + 32);
367
+ 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";
368
+ const to = data.address(4);
369
+ const amount = data.word(36);
370
+ if (!same(target, e.toToken)) return `the Eco route delivers ${target}, not ${e.toToken}`;
371
+ if (!same(to, e.receiver)) return `the Eco route pays ${to}, not ${e.receiver}`;
372
+ if (amount < e.minOut) return `the Eco route delivers ${amount}, below the ${e.minOut} shown`;
373
+ return null;
374
+ }
375
+ function m0Delivery(r, at, e) {
376
+ if (kindOf(e.toToken) === "native") return "an M0 order cannot deliver the native token";
377
+ const tokenOut = r.address(at + 32 * 3);
378
+ const amountOut = r.word(at + 32 * 5);
379
+ if (!same(tokenOut, e.toToken)) return `the M0 order asks for ${tokenOut}, not ${e.toToken}`;
380
+ if (amountOut < e.minOut) return `the M0 order asks for ${amountOut}, below the ${e.minOut} shown`;
381
+ return null;
382
+ }
383
+ var FACET_DATA = {
384
+ AcrossV4: { refunds: [["refundAddress", 1]], zeroRefused: true },
385
+ // the SpokePool depositor: an expired deposit refunds the input there
386
+ AcrossV4Swap: { nested: acrossSwapRefunds },
387
+ Centrifuge: { inline: 2, refunds: [["refundRecipient", 1]], zeroRefused: true },
388
+ Chainflip: { nested: chainflipRefunds },
389
+ Eco: { refunds: [["refundRecipient", 6]], zeroRefused: true, delivery: ecoDelivery },
390
+ // the intent's creator, refunded its reward (the input) on expiry
391
+ Frax: { inline: 5, refunds: [["refundRecipient", 3]], zeroRefused: true },
392
+ Glacis: { inline: 4, refunds: [["refundAddress", 1]], zeroRefused: true },
393
+ LayerSwap: { refunds: [["refundRecipient", 2]], zeroRefused: true },
394
+ LiFiIntentEscrowV2: { refunds: [["depositAndRefundAddress", 2]], zeroRefused: true },
395
+ // the escrow order's user
396
+ M0: { inline: 7, refunds: [["refundRecipient", 1], ["orderOwner", 2]], zeroRefused: true, delivery: m0Delivery },
397
+ // orderOwner gets a cancelled order back
398
+ Mayan: { refunds: [["refundRecipient", 7]], nested: mayanRefunds, zeroRefused: true },
399
+ NEARIntents: { refunds: [["refundRecipient", 6]], zeroRefused: true },
400
+ // also the 1Click quote's refundTo
401
+ PolymerCCTP: { refunds: [["refundRecipient", 5]], zeroRefused: true },
402
+ Stargate: { refunds: [["refundAddress", 4]] },
403
+ // (assetId, sendParams, fee.nativeFee, fee.lzTokenFee, refundAddress): LayerZero's fee refund
404
+ Superset: { refunds: [["refundAddress", 2], ["fallbackEoA", 3]], zeroRefused: true }
405
+ };
406
+ var NO_REFUND_FIELDS = /* @__PURE__ */ new Set(["Across", "AllBridge", "ArbitrumBridge", "CelerCircleBridge", "GasZip", "GnosisBridge", "MegaETHBridge", "OmniBridge", "OptimismBridge", "PolygonBridge", "Unit"]);
407
+ function facetDataAt(r, bridge, spec) {
408
+ const slot = 4 + (bridge.sourceSwaps ? 64 : 32);
409
+ return spec.inline ? slot : 4 + r.small(slot);
410
+ }
411
+ function refundMismatch(r, bridge, e) {
412
+ const spec = FACET_DATA[bridge.facet];
413
+ if (!spec) return NO_REFUND_FIELDS.has(bridge.facet) ? null : `no known refund layout for ${bridge.facet}`;
414
+ const at = facetDataAt(r, bridge, spec);
415
+ const paid = (spec.refunds ?? []).map(([field, word]) => [`${bridge.facet} ${field}`, r.address(at + 32 * word), spec.zeroRefused === true]);
416
+ paid.push(...spec.nested?.(r, at) ?? []);
417
+ for (const [what, to, zeroRefused] of paid) {
418
+ if (zeroRefused && /^0x0{40}$/.test(to)) continue;
419
+ if (!same(to, e.sender) && !same(to, e.receiver)) return `${what} pays ${to}, neither the sender ${e.sender} nor the recipient`;
420
+ }
421
+ return null;
422
+ }
299
423
  function diamondCallMismatch(data, e) {
300
424
  let r;
301
425
  try {
@@ -320,6 +444,8 @@ function diamondCallMismatch(data, e) {
320
444
  }
321
445
  return checkSteps(steps, e, e.toToken);
322
446
  }
447
+ const unbound = UNBOUND_BRIDGES[selector];
448
+ if (unbound) return `${unbound} delivers to a recipient the transaction does not bind (an off-chain order or unchecked facet data)`;
323
449
  const bridge = BRIDGE_CALLS[selector];
324
450
  if (!bridge) return `unknown Diamond call ${selector}`;
325
451
  const head = r.small(4);
@@ -338,6 +464,11 @@ function diamondCallMismatch(data, e) {
338
464
  if (!e.evmDestination) return "the recipient on a non-EVM destination sits in bridge-specific data and cannot be checked here";
339
465
  if (!same(receiver, e.receiver)) return `pays ${receiver}, not ${e.receiver}`;
340
466
  if (hasSourceSwaps !== bridge.sourceSwaps) return `unknown Diamond call ${selector}`;
467
+ const refund = refundMismatch(r, bridge, e);
468
+ if (refund) return refund;
469
+ const spec = FACET_DATA[bridge.facet];
470
+ const delivery = spec?.delivery?.(r, facetDataAt(r, bridge, spec), e);
471
+ if (delivery) return delivery;
341
472
  if (hasSourceSwaps) {
342
473
  if (minAmount === 0n) return "bridges nothing after the source swaps";
343
474
  return checkSteps(swapsAt(r, 4, 4 + 32), e, sendingAssetId);
@@ -349,8 +480,98 @@ function diamondCallMismatch(data, e) {
349
480
  }
350
481
  }
351
482
 
352
- // src/provider.ts
483
+ // src/solana.ts
353
484
  var import_util2 = require("@anywalletkit/protocol-swap2-core/util");
485
+ var ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
486
+ var MAYAN_ENTRY_PROGRAMS = /* @__PURE__ */ new Set([
487
+ "D8C8iW6zmoKg5TRr8nQ7h14TMWqQX8FiBdj2ju5MF3wa",
488
+ // CPI_PROXY_PROGRAM_ID
489
+ "BLZRi6frs4X4DNLw56V4EXai1b6QVESN1BhHBTYM9VcY",
490
+ // SWIFT_PROGRAM_ID
491
+ "mayan34VedncxdK2XobtvWFDXQASUTBXhUVzt2kKgny",
492
+ // SWIFT_V2_PROGRAM_ID
493
+ "dkpZqrxHFrhziEMQ931GLtfy11nFkCsfMftH9u6QwBU",
494
+ // MCTP_PROGRAM_ID
495
+ "Gx9rivpS3YR8pBFwMuP6omYqVxunpLvLkNn7ubNyuZZ5"
496
+ // FAST_MCTP_PROGRAM_ID
497
+ ]);
498
+ var SYSTEM_PROGRAM = "11111111111111111111111111111111";
499
+ var LIFI_SOLANA_FEE_WALLET = "34FKjAdVcTax2DHqV2XnbXa9J3zmyKcFuFKWbcmgxjgm";
500
+ function base58(bytes) {
501
+ let n = 0n;
502
+ for (const b of bytes) n = n << 8n | BigInt(b);
503
+ let out = "";
504
+ while (n > 0n) {
505
+ out = ALPHABET[Number(n % 58n)] + out;
506
+ n /= 58n;
507
+ }
508
+ for (const b of bytes) {
509
+ if (b !== 0) break;
510
+ out = `1${out}`;
511
+ }
512
+ return out;
513
+ }
514
+ var Cursor = class {
515
+ constructor(b) {
516
+ this.b = b;
517
+ }
518
+ b;
519
+ at = 0;
520
+ byte() {
521
+ if (this.at >= this.b.length) throw new Error("truncated");
522
+ return this.b[this.at++];
523
+ }
524
+ bytes(n) {
525
+ if (n < 0 || this.at + n > this.b.length) throw new Error("truncated");
526
+ const out = this.b.subarray(this.at, this.at + n);
527
+ this.at += n;
528
+ return out;
529
+ }
530
+ /** compact-u16 */
531
+ short() {
532
+ let v = 0;
533
+ for (let shift = 0; shift < 21; shift += 7) {
534
+ const b = this.byte();
535
+ v |= (b & 127) << shift;
536
+ if (!(b & 128)) return v;
537
+ }
538
+ throw new Error("bad length");
539
+ }
540
+ };
541
+ function solanaBridgeTxMismatch(base64, sender) {
542
+ const bytes = (0, import_util2.base64Bytes)(base64);
543
+ if (!bytes) return "the Solana transaction is not base64";
544
+ const c = new Cursor(bytes);
545
+ try {
546
+ c.bytes(64 * c.short());
547
+ const first = c.byte();
548
+ if ((first & 128) === 0) c.at--;
549
+ c.bytes(3);
550
+ const keys = Array.from({ length: c.short() }, () => base58(c.bytes(32)));
551
+ if (keys[0] !== sender) return `the Solana transaction is paid by ${keys[0] ?? "nobody"}, not the sender ${sender}`;
552
+ c.bytes(32);
553
+ let mayan = false;
554
+ const payees = [];
555
+ const n = c.short();
556
+ for (let i = 0; i < n; i++) {
557
+ const program = keys[c.byte()];
558
+ const accounts = Array.from(c.bytes(c.short()));
559
+ const data = c.bytes(c.short());
560
+ if (program !== void 0 && MAYAN_ENTRY_PROGRAMS.has(program)) mayan = true;
561
+ if (program === SYSTEM_PROGRAM && data.length === 12 && data[0] === 2 && data[1] === 0 && data[2] === 0 && data[3] === 0) {
562
+ const to = keys[accounts[1] ?? -1];
563
+ if (to !== void 0 && to !== sender && to !== LIFI_SOLANA_FEE_WALLET) payees.push(to);
564
+ }
565
+ }
566
+ return mayan && payees.length ? `the Solana transaction transfers SOL to ${payees[0]}, which is neither the sender nor LI.FI's fee wallet` : null;
567
+ } catch (err) {
568
+ return `the Solana transaction does not decode (${err instanceof Error ? err.message : String(err)})`;
569
+ }
570
+ }
571
+
572
+ // src/provider.ts
573
+ var import_util3 = require("@anywalletkit/protocol-swap2-core/util");
574
+ var import_validate = require("@anywalletkit/protocol-swap2-core/validate");
354
575
  var import_core = require("@anywalletkit/core");
355
576
  var DEFAULT_LIFI_BASE = "https://li.quest/v1";
356
577
  var LIFI_CHAIN = {
@@ -476,7 +697,7 @@ function trustMap(input, label) {
476
697
  }
477
698
  function lifiToken(params) {
478
699
  const sourceChain = params.sourceChain;
479
- const asset = (0, import_util2.sourceAssetOf)(params);
700
+ const asset = (0, import_util3.sourceAssetOf)(params);
480
701
  if (!asset) return null;
481
702
  if (asset.contract) return asset.contract;
482
703
  return isSolana(sourceChain) ? SOLANA_NATIVE : EVM_ZERO;
@@ -487,7 +708,8 @@ function headers(key) {
487
708
  return h;
488
709
  }
489
710
  var TOOL_KEY = /^[A-Za-z0-9_-]{1,64}$/;
490
- function routeOptions(body) {
711
+ var UNBOUND_TOOLS = Object.freeze(["relaydepository", "garden", "squid", "symbiosis"]);
712
+ function routeOptions(body, excluded) {
491
713
  const routes = body?.routes;
492
714
  if (!Array.isArray(routes)) return [];
493
715
  const out = [];
@@ -497,7 +719,7 @@ function routeOptions(body) {
497
719
  const cross = Array.isArray(step.includedSteps) ? step.includedSteps.find((i) => i?.type === "cross")?.tool : void 0;
498
720
  const tool = typeof cross === "string" ? cross : step.tool;
499
721
  const eta = step.estimate?.executionDuration;
500
- if (typeof tool !== "string" || !TOOL_KEY.test(tool) || typeof eta !== "number" || !Number.isFinite(eta) || eta < 0) continue;
722
+ if (typeof tool !== "string" || !TOOL_KEY.test(tool) || excluded.includes(tool) || typeof eta !== "number" || !Number.isFinite(eta) || eta < 0) continue;
501
723
  if (typeof r.toAmount !== "string" || typeof r.toAmountMin !== "string" || !/^\d{1,60}$/.test(r.toAmount) || !/^\d{1,60}$/.test(r.toAmountMin)) continue;
502
724
  out.push({
503
725
  tool,
@@ -561,6 +783,9 @@ function lifiProvider(config = {}) {
561
783
  const fromToken = lifiToken(params);
562
784
  if (!fromToken) return null;
563
785
  const solanaSource = isSolana(params.sourceChain);
786
+ const crossChain = params.sourceChain !== params.destChain;
787
+ const noDestinationCall = crossChain && !solanaSource;
788
+ let routeScope = config.routeWideSlippage === true && !solanaSource && !isSolana(params.destChain);
564
789
  const dry = params.dry !== false;
565
790
  const fromAddress = params.refundAddress || (dry ? solanaSource ? SOLANA_PLACEHOLDER : EVM_PLACEHOLDER : "");
566
791
  const toAddress = params.destAddress || (dry ? fromAddress : "");
@@ -570,7 +795,7 @@ function lifiProvider(config = {}) {
570
795
  let sentSlippage = slippageBps / 1e4;
571
796
  let deadline;
572
797
  let choice = null;
573
- if (config.chooseRoute && params.sourceChain !== params.destChain) {
798
+ if (config.chooseRoute && crossChain) {
574
799
  try {
575
800
  const res = await fetch(`${apiBase}/advanced/routes`, {
576
801
  method: "POST",
@@ -583,12 +808,18 @@ function lifiProvider(config = {}) {
583
808
  fromAmount: params.amountIn,
584
809
  fromAddress,
585
810
  toAddress,
586
- options: { slippage: slippageBps / 1e4, ...integrator().trim() ? { integrator: integrator().trim() } : {} }
811
+ options: {
812
+ slippage: slippageBps / 1e4,
813
+ ...routeScope ? { slippageScope: "route" } : {},
814
+ ...solanaSource ? {} : { bridges: { deny: UNBOUND_TOOLS } },
815
+ ...noDestinationCall ? { allowDestinationCall: false } : {},
816
+ ...integrator().trim() ? { integrator: integrator().trim() } : {}
817
+ }
587
818
  }),
588
- signal: signal ?? (0, import_util2.timeoutSignal)(8e3)
819
+ signal: signal ?? (0, import_util3.timeoutSignal)(8e3)
589
820
  });
590
821
  if (res.ok) {
591
- const options = routeOptions(await res.json());
822
+ const options = routeOptions(await res.json(), solanaSource ? [] : UNBOUND_TOOLS);
592
823
  if (options.length) choice = cleanChoice(config.chooseRoute(options, params));
593
824
  }
594
825
  } catch {
@@ -608,10 +839,13 @@ function lifiProvider(config = {}) {
608
839
  if (sentSlippage !== void 0) {
609
840
  url.searchParams.set("slippage", String(sentSlippage));
610
841
  }
611
- for (const t of choice?.allowBridges ?? []) url.searchParams.append("allowBridges", t);
612
- for (const t of choice?.denyBridges ?? []) url.searchParams.append("denyBridges", t);
842
+ if (routeScope) url.searchParams.set("slippageScope", "route");
843
+ if (noDestinationCall) url.searchParams.set("allowDestinationCall", "false");
844
+ const unbound = crossChain && !solanaSource ? UNBOUND_TOOLS : [];
845
+ for (const t of (choice?.allowBridges ?? []).filter((b) => solanaSource || !UNBOUND_TOOLS.includes(b))) url.searchParams.append("allowBridges", t);
846
+ for (const t of /* @__PURE__ */ new Set([...choice?.denyBridges ?? [], ...unbound])) url.searchParams.append("denyBridges", t);
613
847
  if (choice?.order) url.searchParams.set("order", choice.order);
614
- deadline = signal ? void 0 : (0, import_util2.timeoutSignal)(12e3);
848
+ deadline = signal ? void 0 : (0, import_util3.timeoutSignal)(12e3);
615
849
  const res = await fetch(url, { headers: headers(apiKey()), signal: signal ?? deadline });
616
850
  if (!res.ok && choice) {
617
851
  choice = null;
@@ -621,6 +855,11 @@ function lifiProvider(config = {}) {
621
855
  if (!res.ok) {
622
856
  const body = await res.json().catch(() => null);
623
857
  const reasons = [...new Set((body?.errors?.filteredOut ?? []).map((f) => f.reason).filter((r) => typeof r === "string"))];
858
+ if (routeScope && [typeof body?.message === "string" ? body.message : "", ...reasons].some((r) => /^ROUTE_SLIPPAGE_(UNSUPPORTED|UNAVAILABLE)/.test(r))) {
859
+ routeScope = false;
860
+ attempt--;
861
+ continue;
862
+ }
624
863
  const why = reasons.length === 1 ? ` (${reasons[0].slice(0, 200)})` : "";
625
864
  params.onRefusal?.(`lifi: ${typeof body?.message === "string" ? body.message.slice(0, 200) : `HTTP ${res.status}`}${why}`);
626
865
  return null;
@@ -653,6 +892,10 @@ function lifiProvider(config = {}) {
653
892
  if (solanaSource) {
654
893
  if (!tr.data || tr.data.length < 64 || tr.data.length % 4 !== 0 || !BASE64.test(tr.data)) return reject("the Solana transaction is not base64");
655
894
  if (tr.to !== void 0 || tr.value !== void 0 || tr.gasLimit !== void 0) return reject("the Solana transaction carries EVM fields");
895
+ if (fromChain !== toChain) {
896
+ const why = solanaBridgeTxMismatch(tr.data, fromAddress);
897
+ if (why) return reject(why);
898
+ }
656
899
  } 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");
657
900
  if (sentSlippage !== void 0 && (typeof action.slippage !== "number" || Math.abs(action.slippage - sentSlippage) > 1e-12)) return reject("the quote ignored the requested slippage");
658
901
  const sourceIsToken = fromToken !== EVM_ZERO && fromToken !== SOLANA_NATIVE;
@@ -690,7 +933,7 @@ function lifiProvider(config = {}) {
690
933
  if (sentSlippage !== void 0) {
691
934
  const dest = BigInt(est.toAmount);
692
935
  const min = BigInt(minDestAmount);
693
- if (dest > 0n && min < dest * BigInt(1e4 - slippageBps) / 10000n) {
936
+ if (dest > 0n && min < (0, import_validate.slippageFloor)(dest, slippageBps)) {
694
937
  const effective = (0, import_core.bigintToNumber)((dest - min) * 1000000n / dest) / 1e6;
695
938
  const requested = slippageBps / 1e4;
696
939
  const scaled = Math.floor(sentSlippage * requested / effective * 0.995 * 1e6) / 1e6;
@@ -719,6 +962,7 @@ function lifiProvider(config = {}) {
719
962
  toToken: expectedToToken,
720
963
  amountIn: BigInt(params.amountIn),
721
964
  receiver: toAddress,
965
+ sender: fromAddress,
722
966
  minOut: BigInt(minDestAmount),
723
967
  destChainId: BigInt(toChain),
724
968
  sameChain: fromChain === toChain,
@@ -737,6 +981,7 @@ function lifiProvider(config = {}) {
737
981
  sourceAmount: params.amountIn,
738
982
  destAmount: est.toAmount,
739
983
  minDestAmount,
984
+ ...Number.isInteger(action.toToken?.decimals) && action.toToken.decimals >= 0 && action.toToken.decimals <= 36 ? { destDecimals: action.toToken.decimals } : {},
740
985
  feeUsd,
741
986
  etaSeconds: est.executionDuration ?? 120,
742
987
  reliability: 0.97,
@@ -773,7 +1018,7 @@ function lifiProvider(config = {}) {
773
1018
  url.searchParams.set("txHash", ref.sourceTxHash);
774
1019
  if (fromChain) url.searchParams.set("fromChain", fromChain);
775
1020
  if (toChain) url.searchParams.set("toChain", toChain);
776
- const res = await fetch(url, { headers: headers(apiKey()), signal: (0, import_util2.timeoutSignal)(12e3) });
1021
+ const res = await fetch(url, { headers: headers(apiKey()), signal: (0, import_util3.timeoutSignal)(12e3) });
777
1022
  if (!res.ok) return { state: "pending" };
778
1023
  const s = await res.json();
779
1024
  const map = {
@@ -804,7 +1049,10 @@ function lifiVerifyQuote(params, quote) {
804
1049
  if (!fromChain || !toChain) return "not a LI.FI chain pair";
805
1050
  const tx = quote.txRequest;
806
1051
  if (!tx) return "the quote carries no transaction";
807
- if (isSolana(params.sourceChain)) return tx.serializedTx && tx.data === void 0 && tx.to === void 0 ? null : "a Solana source carries a serialized transaction only";
1052
+ if (isSolana(params.sourceChain)) {
1053
+ if (!tx.serializedTx || tx.data !== void 0 || tx.to !== void 0) return "a Solana source carries a serialized transaction only";
1054
+ return fromChain === toChain ? null : solanaBridgeTxMismatch(tx.serializedTx, params.refundAddress || (params.dry !== false ? SOLANA_PLACEHOLDER : ""));
1055
+ }
808
1056
  if (!isHexData(tx.data) || !tx.to || tx.serializedTx !== void 0) return "the quote carries no Diamond call";
809
1057
  if (!sameAddress(tx.to, diamondFor(params.sourceChain))) return `the call targets ${tx.to}, not the LI.FI Diamond`;
810
1058
  if (tx.chainId !== void 0 && String(tx.chainId) !== fromChain) return `the call is for chain ${tx.chainId}, not ${fromChain}`;
@@ -839,6 +1087,7 @@ function lifiVerifyQuote(params, quote) {
839
1087
  toToken: nativeFor(params.destChain, params.destToken),
840
1088
  amountIn,
841
1089
  receiver: toAddress,
1090
+ sender: fromAddress,
842
1091
  minOut,
843
1092
  destChainId: BigInt(toChain),
844
1093
  sameChain: fromChain === toChain,