@anywalletkit/protocol-swap2-lifi 0.4.0 → 0.5.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/provider.cjs CHANGED
@@ -94,144 +94,162 @@ var GENERIC_SWAPS = {
94
94
  var BRIDGE_INPUT_FEE_MAX_BPS = 100n;
95
95
  var kindOf = (asset) => /^0x0{40}$/i.test(asset) ? "native" : "erc20";
96
96
  var BRIDGE_CALLS = {
97
- "0x1fd8010c": { sourceSwaps: false },
97
+ "0x1fd8010c": { sourceSwaps: false, facet: "Across" },
98
98
  // startBridgeTokensViaAcross (AcrossFacet)
99
- "0x3a3f7332": { sourceSwaps: true },
99
+ "0x3a3f7332": { sourceSwaps: true, facet: "Across" },
100
100
  // swapAndStartBridgeTokensViaAcross (AcrossFacet)
101
- "0xa1f1ce43": { sourceSwaps: false },
101
+ "0xa1f1ce43": { sourceSwaps: false, facet: "AcrossV4" },
102
102
  // startBridgeTokensViaAcrossV4 (AcrossFacetV4)
103
- "0x1794958f": { sourceSwaps: true },
103
+ "0x1794958f": { sourceSwaps: true, facet: "AcrossV4" },
104
104
  // swapAndStartBridgeTokensViaAcrossV4 (AcrossFacetV4)
105
- "0x6a90d66e": { sourceSwaps: false },
105
+ "0x6a90d66e": { sourceSwaps: false, facet: "AcrossV4Swap" },
106
106
  // startBridgeTokensViaAcrossV4Swap (AcrossV4SwapFacet)
107
- "0x9b054bc4": { sourceSwaps: true },
107
+ "0x9b054bc4": { sourceSwaps: true, facet: "AcrossV4Swap" },
108
108
  // swapAndStartBridgeTokensViaAcrossV4Swap (AcrossV4SwapFacet)
109
- "0x6a51e9a9": { sourceSwaps: false },
109
+ "0x6a51e9a9": { sourceSwaps: false, facet: "AllBridge" },
110
110
  // startBridgeTokensViaAllBridge (AllBridgeFacet)
111
- "0x63267469": { sourceSwaps: true },
111
+ "0x63267469": { sourceSwaps: true, facet: "AllBridge" },
112
112
  // swapAndStartBridgeTokensViaAllBridge (AllBridgeFacet)
113
- "0xc9851d0b": { sourceSwaps: false },
113
+ "0xc9851d0b": { sourceSwaps: false, facet: "ArbitrumBridge" },
114
114
  // startBridgeTokensViaArbitrumBridge (ArbitrumBridgeFacet)
115
- "0x3cc9517b": { sourceSwaps: true },
115
+ "0x3cc9517b": { sourceSwaps: true, facet: "ArbitrumBridge" },
116
116
  // swapAndStartBridgeTokensViaArbitrumBridge (ArbitrumBridgeFacet)
117
- "0xe0a4201c": { sourceSwaps: false },
117
+ "0xe0a4201c": { sourceSwaps: false, facet: "CelerCircleBridge" },
118
118
  // startBridgeTokensViaCelerCircleBridge (CelerCircleBridgeFacet)
119
- "0x4f3b0759": { sourceSwaps: true },
119
+ "0x4f3b0759": { sourceSwaps: true, facet: "CelerCircleBridge" },
120
120
  // swapAndStartBridgeTokensViaCelerCircleBridge (CelerCircleBridgeFacet)
121
- "0x0315138f": { sourceSwaps: false },
121
+ "0x0315138f": { sourceSwaps: false, facet: "Centrifuge" },
122
122
  // startBridgeTokensViaCentrifuge (CentrifugeFacet)
123
- "0x8fd4d4dd": { sourceSwaps: true },
123
+ "0x8fd4d4dd": { sourceSwaps: true, facet: "Centrifuge" },
124
124
  // swapAndStartBridgeTokensViaCentrifuge (CentrifugeFacet)
125
- "0x0ad553b3": { sourceSwaps: false },
125
+ "0x0ad553b3": { sourceSwaps: false, facet: "Chainflip" },
126
126
  // startBridgeTokensViaChainflip (ChainflipFacet)
127
- "0xee3314a1": { sourceSwaps: true },
127
+ "0xee3314a1": { sourceSwaps: true, facet: "Chainflip" },
128
128
  // swapAndStartBridgeTokensViaChainflip (ChainflipFacet)
129
- "0x4004633e": { sourceSwaps: false },
130
- // startBridgeTokensViaDeBridgeDln (DeBridgeDlnFacet)
131
- "0x2c7d2db0": { sourceSwaps: true },
132
- // swapAndStartBridgeTokensViaDeBridgeDln (DeBridgeDlnFacet)
133
- "0xbff90b61": { sourceSwaps: false },
129
+ "0xbff90b61": { sourceSwaps: false, facet: "Eco" },
134
130
  // startBridgeTokensViaEco (EcoFacet)
135
- "0x762aea18": { sourceSwaps: true },
131
+ "0x762aea18": { sourceSwaps: true, facet: "Eco" },
136
132
  // swapAndStartBridgeTokensViaEco (EcoFacet)
137
- "0x678786ea": { sourceSwaps: false },
133
+ "0x678786ea": { sourceSwaps: false, facet: "Frax" },
138
134
  // startBridgeTokensViaFrax (FraxFacet)
139
- "0x07b8e4c7": { sourceSwaps: true },
135
+ "0x07b8e4c7": { sourceSwaps: true, facet: "Frax" },
140
136
  // swapAndStartBridgeTokensViaFrax (FraxFacet)
141
- "0x5f9af35d": { sourceSwaps: false },
142
- // startBridgeTokensViaGarden (GardenFacet)
143
- "0x76ad76fe": { sourceSwaps: true },
144
- // swapAndStartBridgeTokensViaGarden (GardenFacet)
145
- "0xfc5f1003": { sourceSwaps: false },
137
+ "0xfc5f1003": { sourceSwaps: false, facet: "GasZip" },
146
138
  // startBridgeTokensViaGasZip (GasZipFacet)
147
- "0x606326ff": { sourceSwaps: true },
139
+ "0x606326ff": { sourceSwaps: true, facet: "GasZip" },
148
140
  // swapAndStartBridgeTokensViaGasZip (GasZipFacet)
149
- "0x6f9206ba": { sourceSwaps: false },
141
+ "0x6f9206ba": { sourceSwaps: false, facet: "Glacis" },
150
142
  // startBridgeTokensViaGlacis (GlacisFacet)
151
- "0x9c4b6dd9": { sourceSwaps: true },
143
+ "0x9c4b6dd9": { sourceSwaps: true, facet: "Glacis" },
152
144
  // swapAndStartBridgeTokensViaGlacis (GlacisFacet)
153
- "0xf66fe519": { sourceSwaps: false },
145
+ "0xf66fe519": { sourceSwaps: false, facet: "GnosisBridge" },
154
146
  // startBridgeTokensViaGnosisBridge (GnosisBridgeFacet)
155
- "0x7bf96e0a": { sourceSwaps: true },
147
+ "0x7bf96e0a": { sourceSwaps: true, facet: "GnosisBridge" },
156
148
  // swapAndStartBridgeTokensViaGnosisBridge (GnosisBridgeFacet)
157
- "0xee9e98e0": { sourceSwaps: false },
149
+ "0xee9e98e0": { sourceSwaps: false, facet: "LayerSwap" },
158
150
  // startBridgeTokensViaLayerSwap (LayerSwapFacet)
159
- "0x4c279d6b": { sourceSwaps: true },
151
+ "0x4c279d6b": { sourceSwaps: true, facet: "LayerSwap" },
160
152
  // swapAndStartBridgeTokensViaLayerSwap (LayerSwapFacet)
161
- "0x7dbcf1d9": { sourceSwaps: false },
153
+ "0x7dbcf1d9": { sourceSwaps: false, facet: "LiFiIntentEscrowV2" },
162
154
  // startBridgeTokensViaLiFiIntentEscrowV2 (LiFiIntentEscrowFacetV2)
163
- "0x6d21c5df": { sourceSwaps: true },
155
+ "0x6d21c5df": { sourceSwaps: true, facet: "LiFiIntentEscrowV2" },
164
156
  // swapAndStartBridgeTokensViaLiFiIntentEscrowV2 (LiFiIntentEscrowFacetV2)
165
- "0x65782c4b": { sourceSwaps: false },
157
+ "0x65782c4b": { sourceSwaps: false, facet: "M0" },
166
158
  // startBridgeTokensViaM0 (M0Facet)
167
- "0xb9eaf86d": { sourceSwaps: true },
159
+ "0xb9eaf86d": { sourceSwaps: true, facet: "M0" },
168
160
  // swapAndStartBridgeTokensViaM0 (M0Facet)
169
- "0xb4875292": { sourceSwaps: false },
161
+ "0xb4875292": { sourceSwaps: false, facet: "Mayan" },
170
162
  // startBridgeTokensViaMayan (MayanFacet)
171
- "0x80c65808": { sourceSwaps: true },
163
+ "0x80c65808": { sourceSwaps: true, facet: "Mayan" },
172
164
  // swapAndStartBridgeTokensViaMayan (MayanFacet)
173
- "0x4213dfff": { sourceSwaps: false },
165
+ "0x4213dfff": { sourceSwaps: false, facet: "MegaETHBridge" },
174
166
  // startBridgeTokensViaMegaETHBridge (MegaETHBridgeFacet)
175
- "0x22256e89": { sourceSwaps: true },
167
+ "0x22256e89": { sourceSwaps: true, facet: "MegaETHBridge" },
176
168
  // swapAndStartBridgeTokensViaMegaETHBridge (MegaETHBridgeFacet)
177
- "0x4698f032": { sourceSwaps: false },
169
+ "0x4698f032": { sourceSwaps: false, facet: "NEARIntents" },
178
170
  // startBridgeTokensViaNEARIntents (NEARIntentsFacet)
179
- "0x02631b09": { sourceSwaps: true },
171
+ "0x02631b09": { sourceSwaps: true, facet: "NEARIntents" },
180
172
  // swapAndStartBridgeTokensViaNEARIntents (NEARIntentsFacet)
181
- "0x782621d8": { sourceSwaps: false },
173
+ "0x782621d8": { sourceSwaps: false, facet: "OmniBridge" },
182
174
  // startBridgeTokensViaOmniBridge (OmniBridgeFacet)
183
- "0x95726782": { sourceSwaps: true },
175
+ "0x95726782": { sourceSwaps: true, facet: "OmniBridge" },
184
176
  // swapAndStartBridgeTokensViaOmniBridge (OmniBridgeFacet)
185
- "0xce8a97a5": { sourceSwaps: false },
177
+ "0xce8a97a5": { sourceSwaps: false, facet: "OptimismBridge" },
186
178
  // startBridgeTokensViaOptimismBridge (OptimismBridgeFacet)
187
- "0x5bb5d448": { sourceSwaps: true },
179
+ "0x5bb5d448": { sourceSwaps: true, facet: "OptimismBridge" },
188
180
  // swapAndStartBridgeTokensViaOptimismBridge (OptimismBridgeFacet)
189
- "0xaf62c7d6": { sourceSwaps: false },
181
+ "0xaf62c7d6": { sourceSwaps: false, facet: "PolygonBridge" },
190
182
  // startBridgeTokensViaPolygonBridge (PolygonBridgeFacet)
191
- "0xb4f37581": { sourceSwaps: true },
183
+ "0xb4f37581": { sourceSwaps: true, facet: "PolygonBridge" },
192
184
  // swapAndStartBridgeTokensViaPolygonBridge (PolygonBridgeFacet)
193
- "0xf434d6ca": { sourceSwaps: false },
185
+ "0xf434d6ca": { sourceSwaps: false, facet: "PolymerCCTP" },
194
186
  // startBridgeTokensViaPolymerCCTP (PolymerCCTPFacet)
195
- "0x17917a4e": { sourceSwaps: true },
187
+ "0x17917a4e": { sourceSwaps: true, facet: "PolymerCCTP" },
196
188
  // swapAndStartBridgeTokensViaPolymerCCTP (PolymerCCTPFacet)
197
- "0x092e8fa4": { sourceSwaps: false },
198
- // startBridgeTokensViaRelayDepository (RelayDepositoryFacet)
199
- "0xa3443faa": { sourceSwaps: true },
200
- // swapAndStartBridgeTokensViaRelayDepository (RelayDepositoryFacet)
201
- "0x3f313808": { sourceSwaps: false },
202
- // startBridgeTokensViaSquid (SquidFacet)
203
- "0xa8f66666": { sourceSwaps: true },
204
- // swapAndStartBridgeTokensViaSquid (SquidFacet)
205
- "0x14d53077": { sourceSwaps: false },
189
+ "0x14d53077": { sourceSwaps: false, facet: "Stargate" },
206
190
  // startBridgeTokensViaStargate (StargateFacetV2)
207
- "0xa6010a66": { sourceSwaps: true },
191
+ "0xa6010a66": { sourceSwaps: true, facet: "Stargate" },
208
192
  // swapAndStartBridgeTokensViaStargate (StargateFacetV2)
209
- "0x0691ff78": { sourceSwaps: false },
193
+ "0x0691ff78": { sourceSwaps: false, facet: "Superset" },
210
194
  // startBridgeTokensViaSuperset (SupersetFacet)
211
- "0xf26e657f": { sourceSwaps: true },
195
+ "0xf26e657f": { sourceSwaps: true, facet: "Superset" },
212
196
  // swapAndStartBridgeTokensViaSuperset (SupersetFacet)
213
- "0xe23b7a08": { sourceSwaps: false },
214
- // startBridgeTokensViaSymbiosis (SymbiosisFacet)
215
- "0xc46059b2": { sourceSwaps: true },
216
- // swapAndStartBridgeTokensViaSymbiosis (SymbiosisFacet)
217
- "0x2541ec57": { sourceSwaps: false },
218
- // startBridgeTokensViaThorSwap (ThorSwapFacet)
219
- "0xad673d88": { sourceSwaps: true },
220
- // swapAndStartBridgeTokensViaThorSwap (ThorSwapFacet)
221
- "0x64261d58": { sourceSwaps: false },
197
+ "0x64261d58": { sourceSwaps: false, facet: "Unit" },
222
198
  // startBridgeTokensViaUnit (UnitFacet)
223
- "0x21a3af52": { sourceSwaps: true }
199
+ "0x21a3af52": { sourceSwaps: true, facet: "Unit" }
224
200
  // swapAndStartBridgeTokensViaUnit (UnitFacet)
225
201
  };
226
- var Reader = class {
202
+ var UNBOUND_BRIDGES = {
203
+ "0x092e8fa4": "Relay",
204
+ // startBridgeTokensViaRelayDepository
205
+ "0xa3443faa": "Relay",
206
+ // swapAndStartBridgeTokensViaRelayDepository
207
+ "0x5f9af35d": "Garden",
208
+ // startBridgeTokensViaGarden
209
+ "0x76ad76fe": "Garden",
210
+ // swapAndStartBridgeTokensViaGarden
211
+ "0x4004633e": "deBridge DLN",
212
+ // startBridgeTokensViaDeBridgeDln
213
+ "0x2c7d2db0": "deBridge DLN",
214
+ // swapAndStartBridgeTokensViaDeBridgeDln
215
+ "0x2541ec57": "ThorSwap",
216
+ // startBridgeTokensViaThorSwap
217
+ "0xad673d88": "ThorSwap",
218
+ // swapAndStartBridgeTokensViaThorSwap
219
+ "0x3f313808": "Squid",
220
+ // startBridgeTokensViaSquid
221
+ "0xa8f66666": "Squid",
222
+ // swapAndStartBridgeTokensViaSquid
223
+ "0xe23b7a08": "Symbiosis",
224
+ // startBridgeTokensViaSymbiosis
225
+ "0xc46059b2": "Symbiosis"
226
+ // swapAndStartBridgeTokensViaSymbiosis
227
+ };
228
+ var Reader = class _Reader {
227
229
  b;
228
- constructor(hex) {
229
- if (!/^0x([0-9a-fA-F]{2})*$/.test(hex)) throw new Error("not hex");
230
- this.b = Uint8Array.from(hex.slice(2).match(/../g) ?? [], (h) => Number.parseInt(h, 16));
230
+ constructor(src) {
231
+ if (typeof src !== "string") {
232
+ this.b = src;
233
+ return;
234
+ }
235
+ if (!/^0x([0-9a-fA-F]{2})*$/.test(src)) throw new Error("not hex");
236
+ this.b = Uint8Array.from(src.slice(2).match(/../g) ?? [], (h) => Number.parseInt(h, 16));
231
237
  }
232
238
  get length() {
233
239
  return this.b.length;
234
240
  }
241
+ /** `n` raw bytes at `at`, as 0x-hex. */
242
+ raw(at, n) {
243
+ if (at < 0 || at + n > this.b.length) throw new Error("out of bounds");
244
+ return `0x${Array.from(this.b.subarray(at, at + n), (x) => x.toString(16).padStart(2, "0")).join("")}`;
245
+ }
246
+ /** The `bytes` whose offset word sits at `head` (relative to `base`), as a reader over its contents only. */
247
+ bytes(base, head) {
248
+ const at = base + this.small(head);
249
+ const n = this.small(at);
250
+ if (at + 32 + n > this.b.length) throw new Error("bytes out of bounds");
251
+ return new _Reader(this.b.subarray(at + 32, at + 32 + n));
252
+ }
235
253
  word(at) {
236
254
  if (at < 0 || at + 32 > this.b.length) throw new Error("out of bounds");
237
255
  let v = 0n;
@@ -289,6 +307,79 @@ function checkSteps(steps, e, finalAsset) {
289
307
  if (!same(last.receivingAssetId, finalAsset)) return `delivers ${last.receivingAssetId}, not ${finalAsset}`;
290
308
  return null;
291
309
  }
310
+ var MAYAN_REFUND_AT = {
311
+ "0xa3a30834": 4 + 32 * 3,
312
+ // Swift v2 createOrderWithToken(tokenIn, amountIn, (payloadType, trader, destAddr, …), customPayload)
313
+ "0x6147435b": 4 + 32 * 3,
314
+ // Swift v2 createOrderWithSig: the same order tuple
315
+ "0xb866e173": 4,
316
+ // Swift createOrderWithEth((trader, tokenOut, …))
317
+ "0x8e8d142b": 4 + 32 * 2,
318
+ // Swift createOrderWithToken(tokenIn, amountIn, (trader, tokenOut, …))
319
+ "0x6111ad25": 4 + 32 * 9,
320
+ // MayanSwap swap(relayerFees, (mayanAddr, mayanChainId, auctionAddr, destAddr, destChainId, referrer, refundAddr), …)
321
+ "0x1eb1cff0": 4 + 32 * 9
322
+ // MayanSwap wrapAndSwapETH: the same recipient tuple
323
+ };
324
+ function mayanRefunds(r, at) {
325
+ const p = r.bytes(at, at + 32 * 2);
326
+ const pos = MAYAN_REFUND_AT[p.raw(0, 4)];
327
+ return pos === void 0 ? [] : [["the Mayan order's refund address", p.address(pos)]];
328
+ }
329
+ function chainflipRefunds(r, at) {
330
+ const p = r.bytes(at, at + 32 * 5);
331
+ if (p.length < 25 || p.raw(0, 1) !== "0x01") throw new Error("unknown Chainflip parameters");
332
+ return [["the Chainflip refund address", p.raw(5, 20)]];
333
+ }
334
+ function acrossSwapRefunds(r, at) {
335
+ const target = r.word(at);
336
+ const c = r.bytes(at, at + 32);
337
+ if (target === 0n) return [["the Across deposit's depositor", c.address(c.small(0)), true]];
338
+ if (target === 1n) {
339
+ const swap = c.small(0);
340
+ return [["the Across deposit's depositor", c.address(swap + c.small(swap + 64) + 96), true]];
341
+ }
342
+ if (target === 2n) return [["the sponsored OFT refund recipient", c.address(c.small(0) + 32), true]];
343
+ if (target === 3n) return [["the sponsored CCTP refund recipient", c.address(64), true]];
344
+ throw new Error("unknown Across Swap API target");
345
+ }
346
+ var FACET_DATA = {
347
+ AcrossV4: { refunds: [["refundAddress", 1]], zeroRefused: true },
348
+ // the SpokePool depositor: an expired deposit refunds the input there
349
+ AcrossV4Swap: { nested: acrossSwapRefunds },
350
+ Centrifuge: { inline: 2, refunds: [["refundRecipient", 1]], zeroRefused: true },
351
+ Chainflip: { nested: chainflipRefunds },
352
+ Eco: { refunds: [["refundRecipient", 5]], zeroRefused: true },
353
+ // the intent's creator, refunded its reward (the input) on expiry
354
+ Frax: { inline: 5, refunds: [["refundRecipient", 3]], zeroRefused: true },
355
+ Glacis: { inline: 4, refunds: [["refundAddress", 1]], zeroRefused: true },
356
+ LayerSwap: { refunds: [["refundRecipient", 2]], zeroRefused: true },
357
+ LiFiIntentEscrowV2: { refunds: [["depositAndRefundAddress", 2]], zeroRefused: true },
358
+ // the escrow order's user
359
+ M0: { inline: 7, refunds: [["refundRecipient", 1], ["orderOwner", 2]], zeroRefused: true },
360
+ // orderOwner gets a cancelled order back
361
+ Mayan: { refunds: [["refundRecipient", 7]], nested: mayanRefunds, zeroRefused: true },
362
+ NEARIntents: { refunds: [["refundRecipient", 6]], zeroRefused: true },
363
+ // also the 1Click quote's refundTo
364
+ PolymerCCTP: { refunds: [["refundRecipient", 5]], zeroRefused: true },
365
+ Stargate: { refunds: [["refundAddress", 4]] },
366
+ // (assetId, sendParams, fee.nativeFee, fee.lzTokenFee, refundAddress): LayerZero's fee refund
367
+ Superset: { refunds: [["refundAddress", 2], ["fallbackEoA", 3]], zeroRefused: true }
368
+ };
369
+ var NO_REFUND_FIELDS = /* @__PURE__ */ new Set(["Across", "AllBridge", "ArbitrumBridge", "CelerCircleBridge", "GasZip", "GnosisBridge", "MegaETHBridge", "OmniBridge", "OptimismBridge", "PolygonBridge", "Unit"]);
370
+ function refundMismatch(r, bridge, e) {
371
+ const spec = FACET_DATA[bridge.facet];
372
+ if (!spec) return NO_REFUND_FIELDS.has(bridge.facet) ? null : `no known refund layout for ${bridge.facet}`;
373
+ const slot = 4 + (bridge.sourceSwaps ? 64 : 32);
374
+ const at = spec.inline ? slot : 4 + r.small(slot);
375
+ const paid = (spec.refunds ?? []).map(([field, word]) => [`${bridge.facet} ${field}`, r.address(at + 32 * word), spec.zeroRefused === true]);
376
+ paid.push(...spec.nested?.(r, at) ?? []);
377
+ for (const [what, to, zeroRefused] of paid) {
378
+ if (zeroRefused && /^0x0{40}$/.test(to)) continue;
379
+ if (!same(to, e.sender) && !same(to, e.receiver)) return `${what} pays ${to}, neither the sender ${e.sender} nor the recipient`;
380
+ }
381
+ return null;
382
+ }
292
383
  function diamondCallMismatch(data, e) {
293
384
  let r;
294
385
  try {
@@ -313,6 +404,8 @@ function diamondCallMismatch(data, e) {
313
404
  }
314
405
  return checkSteps(steps, e, e.toToken);
315
406
  }
407
+ const unbound = UNBOUND_BRIDGES[selector];
408
+ if (unbound) return `${unbound} delivers to a recipient the transaction does not bind (an off-chain order or unchecked facet data)`;
316
409
  const bridge = BRIDGE_CALLS[selector];
317
410
  if (!bridge) return `unknown Diamond call ${selector}`;
318
411
  const head = r.small(4);
@@ -331,6 +424,8 @@ function diamondCallMismatch(data, e) {
331
424
  if (!e.evmDestination) return "the recipient on a non-EVM destination sits in bridge-specific data and cannot be checked here";
332
425
  if (!same(receiver, e.receiver)) return `pays ${receiver}, not ${e.receiver}`;
333
426
  if (hasSourceSwaps !== bridge.sourceSwaps) return `unknown Diamond call ${selector}`;
427
+ const refund = refundMismatch(r, bridge, e);
428
+ if (refund) return refund;
334
429
  if (hasSourceSwaps) {
335
430
  if (minAmount === 0n) return "bridges nothing after the source swaps";
336
431
  return checkSteps(swapsAt(r, 4, 4 + 32), e, sendingAssetId);
@@ -342,8 +437,98 @@ function diamondCallMismatch(data, e) {
342
437
  }
343
438
  }
344
439
 
345
- // src/provider.ts
440
+ // src/solana.ts
346
441
  var import_util2 = require("@anywalletkit/protocol-swap2-core/util");
442
+ var ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
443
+ var MAYAN_ENTRY_PROGRAMS = /* @__PURE__ */ new Set([
444
+ "D8C8iW6zmoKg5TRr8nQ7h14TMWqQX8FiBdj2ju5MF3wa",
445
+ // CPI_PROXY_PROGRAM_ID
446
+ "BLZRi6frs4X4DNLw56V4EXai1b6QVESN1BhHBTYM9VcY",
447
+ // SWIFT_PROGRAM_ID
448
+ "mayan34VedncxdK2XobtvWFDXQASUTBXhUVzt2kKgny",
449
+ // SWIFT_V2_PROGRAM_ID
450
+ "dkpZqrxHFrhziEMQ931GLtfy11nFkCsfMftH9u6QwBU",
451
+ // MCTP_PROGRAM_ID
452
+ "Gx9rivpS3YR8pBFwMuP6omYqVxunpLvLkNn7ubNyuZZ5"
453
+ // FAST_MCTP_PROGRAM_ID
454
+ ]);
455
+ var SYSTEM_PROGRAM = "11111111111111111111111111111111";
456
+ var LIFI_SOLANA_FEE_WALLET = "34FKjAdVcTax2DHqV2XnbXa9J3zmyKcFuFKWbcmgxjgm";
457
+ function base58(bytes) {
458
+ let n = 0n;
459
+ for (const b of bytes) n = n << 8n | BigInt(b);
460
+ let out = "";
461
+ while (n > 0n) {
462
+ out = ALPHABET[Number(n % 58n)] + out;
463
+ n /= 58n;
464
+ }
465
+ for (const b of bytes) {
466
+ if (b !== 0) break;
467
+ out = `1${out}`;
468
+ }
469
+ return out;
470
+ }
471
+ var Cursor = class {
472
+ constructor(b) {
473
+ this.b = b;
474
+ }
475
+ b;
476
+ at = 0;
477
+ byte() {
478
+ if (this.at >= this.b.length) throw new Error("truncated");
479
+ return this.b[this.at++];
480
+ }
481
+ bytes(n) {
482
+ if (n < 0 || this.at + n > this.b.length) throw new Error("truncated");
483
+ const out = this.b.subarray(this.at, this.at + n);
484
+ this.at += n;
485
+ return out;
486
+ }
487
+ /** compact-u16 */
488
+ short() {
489
+ let v = 0;
490
+ for (let shift = 0; shift < 21; shift += 7) {
491
+ const b = this.byte();
492
+ v |= (b & 127) << shift;
493
+ if (!(b & 128)) return v;
494
+ }
495
+ throw new Error("bad length");
496
+ }
497
+ };
498
+ function solanaBridgeTxMismatch(base64, sender) {
499
+ const bytes = (0, import_util2.base64Bytes)(base64);
500
+ if (!bytes) return "the Solana transaction is not base64";
501
+ const c = new Cursor(bytes);
502
+ try {
503
+ c.bytes(64 * c.short());
504
+ const first = c.byte();
505
+ if ((first & 128) === 0) c.at--;
506
+ c.bytes(3);
507
+ const keys = Array.from({ length: c.short() }, () => base58(c.bytes(32)));
508
+ if (keys[0] !== sender) return `the Solana transaction is paid by ${keys[0] ?? "nobody"}, not the sender ${sender}`;
509
+ c.bytes(32);
510
+ let mayan = false;
511
+ const payees = [];
512
+ const n = c.short();
513
+ for (let i = 0; i < n; i++) {
514
+ const program = keys[c.byte()];
515
+ const accounts = Array.from(c.bytes(c.short()));
516
+ const data = c.bytes(c.short());
517
+ if (program !== void 0 && MAYAN_ENTRY_PROGRAMS.has(program)) mayan = true;
518
+ if (program === SYSTEM_PROGRAM && data.length === 12 && data[0] === 2 && data[1] === 0 && data[2] === 0 && data[3] === 0) {
519
+ const to = keys[accounts[1] ?? -1];
520
+ if (to !== void 0 && to !== sender && to !== LIFI_SOLANA_FEE_WALLET) payees.push(to);
521
+ }
522
+ }
523
+ return mayan && payees.length ? `the Solana transaction transfers SOL to ${payees[0]}, which is neither the sender nor LI.FI's fee wallet` : null;
524
+ } catch (err) {
525
+ return `the Solana transaction does not decode (${err instanceof Error ? err.message : String(err)})`;
526
+ }
527
+ }
528
+
529
+ // src/provider.ts
530
+ var import_util3 = require("@anywalletkit/protocol-swap2-core/util");
531
+ var import_validate = require("@anywalletkit/protocol-swap2-core/validate");
347
532
  var import_core = require("@anywalletkit/core");
348
533
  var DEFAULT_LIFI_BASE = "https://li.quest/v1";
349
534
  var LIFI_CHAIN = {
@@ -469,7 +654,7 @@ function trustMap(input, label) {
469
654
  }
470
655
  function lifiToken(params) {
471
656
  const sourceChain = params.sourceChain;
472
- const asset = (0, import_util2.sourceAssetOf)(params);
657
+ const asset = (0, import_util3.sourceAssetOf)(params);
473
658
  if (!asset) return null;
474
659
  if (asset.contract) return asset.contract;
475
660
  return isSolana(sourceChain) ? SOLANA_NATIVE : EVM_ZERO;
@@ -480,7 +665,8 @@ function headers(key) {
480
665
  return h;
481
666
  }
482
667
  var TOOL_KEY = /^[A-Za-z0-9_-]{1,64}$/;
483
- function routeOptions(body) {
668
+ var UNBOUND_TOOLS = Object.freeze(["relaydepository", "garden", "squid", "symbiosis"]);
669
+ function routeOptions(body, excluded) {
484
670
  const routes = body?.routes;
485
671
  if (!Array.isArray(routes)) return [];
486
672
  const out = [];
@@ -490,7 +676,7 @@ function routeOptions(body) {
490
676
  const cross = Array.isArray(step.includedSteps) ? step.includedSteps.find((i) => i?.type === "cross")?.tool : void 0;
491
677
  const tool = typeof cross === "string" ? cross : step.tool;
492
678
  const eta = step.estimate?.executionDuration;
493
- if (typeof tool !== "string" || !TOOL_KEY.test(tool) || typeof eta !== "number" || !Number.isFinite(eta) || eta < 0) continue;
679
+ if (typeof tool !== "string" || !TOOL_KEY.test(tool) || excluded.includes(tool) || typeof eta !== "number" || !Number.isFinite(eta) || eta < 0) continue;
494
680
  if (typeof r.toAmount !== "string" || typeof r.toAmountMin !== "string" || !/^\d{1,60}$/.test(r.toAmount) || !/^\d{1,60}$/.test(r.toAmountMin)) continue;
495
681
  out.push({
496
682
  tool,
@@ -576,12 +762,12 @@ function lifiProvider(config = {}) {
576
762
  fromAmount: params.amountIn,
577
763
  fromAddress,
578
764
  toAddress,
579
- options: { slippage: slippageBps / 1e4, ...integrator().trim() ? { integrator: integrator().trim() } : {} }
765
+ options: { slippage: slippageBps / 1e4, ...solanaSource ? {} : { bridges: { deny: UNBOUND_TOOLS } }, ...integrator().trim() ? { integrator: integrator().trim() } : {} }
580
766
  }),
581
- signal: signal ?? (0, import_util2.timeoutSignal)(8e3)
767
+ signal: signal ?? (0, import_util3.timeoutSignal)(8e3)
582
768
  });
583
769
  if (res.ok) {
584
- const options = routeOptions(await res.json());
770
+ const options = routeOptions(await res.json(), solanaSource ? [] : UNBOUND_TOOLS);
585
771
  if (options.length) choice = cleanChoice(config.chooseRoute(options, params));
586
772
  }
587
773
  } catch {
@@ -601,10 +787,11 @@ function lifiProvider(config = {}) {
601
787
  if (sentSlippage !== void 0) {
602
788
  url.searchParams.set("slippage", String(sentSlippage));
603
789
  }
604
- for (const t of choice?.allowBridges ?? []) url.searchParams.append("allowBridges", t);
605
- for (const t of choice?.denyBridges ?? []) url.searchParams.append("denyBridges", t);
790
+ const unbound = params.sourceChain !== params.destChain && !solanaSource ? UNBOUND_TOOLS : [];
791
+ for (const t of (choice?.allowBridges ?? []).filter((b) => solanaSource || !UNBOUND_TOOLS.includes(b))) url.searchParams.append("allowBridges", t);
792
+ for (const t of /* @__PURE__ */ new Set([...choice?.denyBridges ?? [], ...unbound])) url.searchParams.append("denyBridges", t);
606
793
  if (choice?.order) url.searchParams.set("order", choice.order);
607
- deadline = signal ? void 0 : (0, import_util2.timeoutSignal)(12e3);
794
+ deadline = signal ? void 0 : (0, import_util3.timeoutSignal)(12e3);
608
795
  const res = await fetch(url, { headers: headers(apiKey()), signal: signal ?? deadline });
609
796
  if (!res.ok && choice) {
610
797
  choice = null;
@@ -646,6 +833,10 @@ function lifiProvider(config = {}) {
646
833
  if (solanaSource) {
647
834
  if (!tr.data || tr.data.length < 64 || tr.data.length % 4 !== 0 || !BASE64.test(tr.data)) return reject("the Solana transaction is not base64");
648
835
  if (tr.to !== void 0 || tr.value !== void 0 || tr.gasLimit !== void 0) return reject("the Solana transaction carries EVM fields");
836
+ if (fromChain !== toChain) {
837
+ const why = solanaBridgeTxMismatch(tr.data, fromAddress);
838
+ if (why) return reject(why);
839
+ }
649
840
  } 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");
650
841
  if (sentSlippage !== void 0 && (typeof action.slippage !== "number" || Math.abs(action.slippage - sentSlippage) > 1e-12)) return reject("the quote ignored the requested slippage");
651
842
  const sourceIsToken = fromToken !== EVM_ZERO && fromToken !== SOLANA_NATIVE;
@@ -683,7 +874,7 @@ function lifiProvider(config = {}) {
683
874
  if (sentSlippage !== void 0) {
684
875
  const dest = BigInt(est.toAmount);
685
876
  const min = BigInt(minDestAmount);
686
- if (dest > 0n && min < dest * BigInt(1e4 - slippageBps) / 10000n) {
877
+ if (dest > 0n && min < (0, import_validate.slippageFloor)(dest, slippageBps)) {
687
878
  const effective = (0, import_core.bigintToNumber)((dest - min) * 1000000n / dest) / 1e6;
688
879
  const requested = slippageBps / 1e4;
689
880
  const scaled = Math.floor(sentSlippage * requested / effective * 0.995 * 1e6) / 1e6;
@@ -712,6 +903,7 @@ function lifiProvider(config = {}) {
712
903
  toToken: expectedToToken,
713
904
  amountIn: BigInt(params.amountIn),
714
905
  receiver: toAddress,
906
+ sender: fromAddress,
715
907
  minOut: BigInt(minDestAmount),
716
908
  destChainId: BigInt(toChain),
717
909
  sameChain: fromChain === toChain,
@@ -730,6 +922,7 @@ function lifiProvider(config = {}) {
730
922
  sourceAmount: params.amountIn,
731
923
  destAmount: est.toAmount,
732
924
  minDestAmount,
925
+ ...Number.isInteger(action.toToken?.decimals) && action.toToken.decimals >= 0 && action.toToken.decimals <= 36 ? { destDecimals: action.toToken.decimals } : {},
733
926
  feeUsd,
734
927
  etaSeconds: est.executionDuration ?? 120,
735
928
  reliability: 0.97,
@@ -766,7 +959,7 @@ function lifiProvider(config = {}) {
766
959
  url.searchParams.set("txHash", ref.sourceTxHash);
767
960
  if (fromChain) url.searchParams.set("fromChain", fromChain);
768
961
  if (toChain) url.searchParams.set("toChain", toChain);
769
- const res = await fetch(url, { headers: headers(apiKey()), signal: (0, import_util2.timeoutSignal)(12e3) });
962
+ const res = await fetch(url, { headers: headers(apiKey()), signal: (0, import_util3.timeoutSignal)(12e3) });
770
963
  if (!res.ok) return { state: "pending" };
771
964
  const s = await res.json();
772
965
  const map = {
@@ -797,7 +990,10 @@ function lifiVerifyQuote(params, quote) {
797
990
  if (!fromChain || !toChain) return "not a LI.FI chain pair";
798
991
  const tx = quote.txRequest;
799
992
  if (!tx) return "the quote carries no transaction";
800
- if (isSolana(params.sourceChain)) return tx.serializedTx && tx.data === void 0 && tx.to === void 0 ? null : "a Solana source carries a serialized transaction only";
993
+ if (isSolana(params.sourceChain)) {
994
+ if (!tx.serializedTx || tx.data !== void 0 || tx.to !== void 0) return "a Solana source carries a serialized transaction only";
995
+ return fromChain === toChain ? null : solanaBridgeTxMismatch(tx.serializedTx, params.refundAddress || (params.dry !== false ? SOLANA_PLACEHOLDER : ""));
996
+ }
801
997
  if (!isHexData(tx.data) || !tx.to || tx.serializedTx !== void 0) return "the quote carries no Diamond call";
802
998
  if (!sameAddress(tx.to, diamondFor(params.sourceChain))) return `the call targets ${tx.to}, not the LI.FI Diamond`;
803
999
  if (tx.chainId !== void 0 && String(tx.chainId) !== fromChain) return `the call is for chain ${tx.chainId}, not ${fromChain}`;
@@ -832,6 +1028,7 @@ function lifiVerifyQuote(params, quote) {
832
1028
  toToken: nativeFor(params.destChain, params.destToken),
833
1029
  amountIn,
834
1030
  receiver: toAddress,
1031
+ sender: fromAddress,
835
1032
  minOut,
836
1033
  destChainId: BigInt(toChain),
837
1034
  sameChain: fromChain === toChain,