@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/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 },
136
+ "0xbff90b61": { sourceSwaps: false, facet: "Eco" },
141
137
  // startBridgeTokensViaEco (EcoFacet)
142
- "0x762aea18": { sourceSwaps: true },
138
+ "0x762aea18": { sourceSwaps: true, facet: "Eco" },
143
139
  // swapAndStartBridgeTokensViaEco (EcoFacet)
144
- "0x678786ea": { sourceSwaps: false },
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,79 @@ 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 FACET_DATA = {
354
+ AcrossV4: { refunds: [["refundAddress", 1]], zeroRefused: true },
355
+ // the SpokePool depositor: an expired deposit refunds the input there
356
+ AcrossV4Swap: { nested: acrossSwapRefunds },
357
+ Centrifuge: { inline: 2, refunds: [["refundRecipient", 1]], zeroRefused: true },
358
+ Chainflip: { nested: chainflipRefunds },
359
+ Eco: { refunds: [["refundRecipient", 5]], zeroRefused: true },
360
+ // the intent's creator, refunded its reward (the input) on expiry
361
+ Frax: { inline: 5, refunds: [["refundRecipient", 3]], zeroRefused: true },
362
+ Glacis: { inline: 4, refunds: [["refundAddress", 1]], zeroRefused: true },
363
+ LayerSwap: { refunds: [["refundRecipient", 2]], zeroRefused: true },
364
+ LiFiIntentEscrowV2: { refunds: [["depositAndRefundAddress", 2]], zeroRefused: true },
365
+ // the escrow order's user
366
+ M0: { inline: 7, refunds: [["refundRecipient", 1], ["orderOwner", 2]], zeroRefused: true },
367
+ // orderOwner gets a cancelled order back
368
+ Mayan: { refunds: [["refundRecipient", 7]], nested: mayanRefunds, zeroRefused: true },
369
+ NEARIntents: { refunds: [["refundRecipient", 6]], zeroRefused: true },
370
+ // also the 1Click quote's refundTo
371
+ PolymerCCTP: { refunds: [["refundRecipient", 5]], zeroRefused: true },
372
+ Stargate: { refunds: [["refundAddress", 4]] },
373
+ // (assetId, sendParams, fee.nativeFee, fee.lzTokenFee, refundAddress): LayerZero's fee refund
374
+ Superset: { refunds: [["refundAddress", 2], ["fallbackEoA", 3]], zeroRefused: true }
375
+ };
376
+ var NO_REFUND_FIELDS = /* @__PURE__ */ new Set(["Across", "AllBridge", "ArbitrumBridge", "CelerCircleBridge", "GasZip", "GnosisBridge", "MegaETHBridge", "OmniBridge", "OptimismBridge", "PolygonBridge", "Unit"]);
377
+ function refundMismatch(r, bridge, e) {
378
+ const spec = FACET_DATA[bridge.facet];
379
+ if (!spec) return NO_REFUND_FIELDS.has(bridge.facet) ? null : `no known refund layout for ${bridge.facet}`;
380
+ const slot = 4 + (bridge.sourceSwaps ? 64 : 32);
381
+ const at = spec.inline ? slot : 4 + r.small(slot);
382
+ const paid = (spec.refunds ?? []).map(([field, word]) => [`${bridge.facet} ${field}`, r.address(at + 32 * word), spec.zeroRefused === true]);
383
+ paid.push(...spec.nested?.(r, at) ?? []);
384
+ for (const [what, to, zeroRefused] of paid) {
385
+ if (zeroRefused && /^0x0{40}$/.test(to)) continue;
386
+ if (!same(to, e.sender) && !same(to, e.receiver)) return `${what} pays ${to}, neither the sender ${e.sender} nor the recipient`;
387
+ }
388
+ return null;
389
+ }
299
390
  function diamondCallMismatch(data, e) {
300
391
  let r;
301
392
  try {
@@ -320,6 +411,8 @@ function diamondCallMismatch(data, e) {
320
411
  }
321
412
  return checkSteps(steps, e, e.toToken);
322
413
  }
414
+ const unbound = UNBOUND_BRIDGES[selector];
415
+ if (unbound) return `${unbound} delivers to a recipient the transaction does not bind (an off-chain order or unchecked facet data)`;
323
416
  const bridge = BRIDGE_CALLS[selector];
324
417
  if (!bridge) return `unknown Diamond call ${selector}`;
325
418
  const head = r.small(4);
@@ -338,6 +431,8 @@ function diamondCallMismatch(data, e) {
338
431
  if (!e.evmDestination) return "the recipient on a non-EVM destination sits in bridge-specific data and cannot be checked here";
339
432
  if (!same(receiver, e.receiver)) return `pays ${receiver}, not ${e.receiver}`;
340
433
  if (hasSourceSwaps !== bridge.sourceSwaps) return `unknown Diamond call ${selector}`;
434
+ const refund = refundMismatch(r, bridge, e);
435
+ if (refund) return refund;
341
436
  if (hasSourceSwaps) {
342
437
  if (minAmount === 0n) return "bridges nothing after the source swaps";
343
438
  return checkSteps(swapsAt(r, 4, 4 + 32), e, sendingAssetId);
@@ -349,8 +444,98 @@ function diamondCallMismatch(data, e) {
349
444
  }
350
445
  }
351
446
 
352
- // src/provider.ts
447
+ // src/solana.ts
353
448
  var import_util2 = require("@anywalletkit/protocol-swap2-core/util");
449
+ var ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
450
+ var MAYAN_ENTRY_PROGRAMS = /* @__PURE__ */ new Set([
451
+ "D8C8iW6zmoKg5TRr8nQ7h14TMWqQX8FiBdj2ju5MF3wa",
452
+ // CPI_PROXY_PROGRAM_ID
453
+ "BLZRi6frs4X4DNLw56V4EXai1b6QVESN1BhHBTYM9VcY",
454
+ // SWIFT_PROGRAM_ID
455
+ "mayan34VedncxdK2XobtvWFDXQASUTBXhUVzt2kKgny",
456
+ // SWIFT_V2_PROGRAM_ID
457
+ "dkpZqrxHFrhziEMQ931GLtfy11nFkCsfMftH9u6QwBU",
458
+ // MCTP_PROGRAM_ID
459
+ "Gx9rivpS3YR8pBFwMuP6omYqVxunpLvLkNn7ubNyuZZ5"
460
+ // FAST_MCTP_PROGRAM_ID
461
+ ]);
462
+ var SYSTEM_PROGRAM = "11111111111111111111111111111111";
463
+ var LIFI_SOLANA_FEE_WALLET = "34FKjAdVcTax2DHqV2XnbXa9J3zmyKcFuFKWbcmgxjgm";
464
+ function base58(bytes) {
465
+ let n = 0n;
466
+ for (const b of bytes) n = n << 8n | BigInt(b);
467
+ let out = "";
468
+ while (n > 0n) {
469
+ out = ALPHABET[Number(n % 58n)] + out;
470
+ n /= 58n;
471
+ }
472
+ for (const b of bytes) {
473
+ if (b !== 0) break;
474
+ out = `1${out}`;
475
+ }
476
+ return out;
477
+ }
478
+ var Cursor = class {
479
+ constructor(b) {
480
+ this.b = b;
481
+ }
482
+ b;
483
+ at = 0;
484
+ byte() {
485
+ if (this.at >= this.b.length) throw new Error("truncated");
486
+ return this.b[this.at++];
487
+ }
488
+ bytes(n) {
489
+ if (n < 0 || this.at + n > this.b.length) throw new Error("truncated");
490
+ const out = this.b.subarray(this.at, this.at + n);
491
+ this.at += n;
492
+ return out;
493
+ }
494
+ /** compact-u16 */
495
+ short() {
496
+ let v = 0;
497
+ for (let shift = 0; shift < 21; shift += 7) {
498
+ const b = this.byte();
499
+ v |= (b & 127) << shift;
500
+ if (!(b & 128)) return v;
501
+ }
502
+ throw new Error("bad length");
503
+ }
504
+ };
505
+ function solanaBridgeTxMismatch(base64, sender) {
506
+ const bytes = (0, import_util2.base64Bytes)(base64);
507
+ if (!bytes) return "the Solana transaction is not base64";
508
+ const c = new Cursor(bytes);
509
+ try {
510
+ c.bytes(64 * c.short());
511
+ const first = c.byte();
512
+ if ((first & 128) === 0) c.at--;
513
+ c.bytes(3);
514
+ const keys = Array.from({ length: c.short() }, () => base58(c.bytes(32)));
515
+ if (keys[0] !== sender) return `the Solana transaction is paid by ${keys[0] ?? "nobody"}, not the sender ${sender}`;
516
+ c.bytes(32);
517
+ let mayan = false;
518
+ const payees = [];
519
+ const n = c.short();
520
+ for (let i = 0; i < n; i++) {
521
+ const program = keys[c.byte()];
522
+ const accounts = Array.from(c.bytes(c.short()));
523
+ const data = c.bytes(c.short());
524
+ if (program !== void 0 && MAYAN_ENTRY_PROGRAMS.has(program)) mayan = true;
525
+ if (program === SYSTEM_PROGRAM && data.length === 12 && data[0] === 2 && data[1] === 0 && data[2] === 0 && data[3] === 0) {
526
+ const to = keys[accounts[1] ?? -1];
527
+ if (to !== void 0 && to !== sender && to !== LIFI_SOLANA_FEE_WALLET) payees.push(to);
528
+ }
529
+ }
530
+ return mayan && payees.length ? `the Solana transaction transfers SOL to ${payees[0]}, which is neither the sender nor LI.FI's fee wallet` : null;
531
+ } catch (err) {
532
+ return `the Solana transaction does not decode (${err instanceof Error ? err.message : String(err)})`;
533
+ }
534
+ }
535
+
536
+ // src/provider.ts
537
+ var import_util3 = require("@anywalletkit/protocol-swap2-core/util");
538
+ var import_validate = require("@anywalletkit/protocol-swap2-core/validate");
354
539
  var import_core = require("@anywalletkit/core");
355
540
  var DEFAULT_LIFI_BASE = "https://li.quest/v1";
356
541
  var LIFI_CHAIN = {
@@ -476,7 +661,7 @@ function trustMap(input, label) {
476
661
  }
477
662
  function lifiToken(params) {
478
663
  const sourceChain = params.sourceChain;
479
- const asset = (0, import_util2.sourceAssetOf)(params);
664
+ const asset = (0, import_util3.sourceAssetOf)(params);
480
665
  if (!asset) return null;
481
666
  if (asset.contract) return asset.contract;
482
667
  return isSolana(sourceChain) ? SOLANA_NATIVE : EVM_ZERO;
@@ -487,7 +672,8 @@ function headers(key) {
487
672
  return h;
488
673
  }
489
674
  var TOOL_KEY = /^[A-Za-z0-9_-]{1,64}$/;
490
- function routeOptions(body) {
675
+ var UNBOUND_TOOLS = Object.freeze(["relaydepository", "garden", "squid", "symbiosis"]);
676
+ function routeOptions(body, excluded) {
491
677
  const routes = body?.routes;
492
678
  if (!Array.isArray(routes)) return [];
493
679
  const out = [];
@@ -497,7 +683,7 @@ function routeOptions(body) {
497
683
  const cross = Array.isArray(step.includedSteps) ? step.includedSteps.find((i) => i?.type === "cross")?.tool : void 0;
498
684
  const tool = typeof cross === "string" ? cross : step.tool;
499
685
  const eta = step.estimate?.executionDuration;
500
- if (typeof tool !== "string" || !TOOL_KEY.test(tool) || typeof eta !== "number" || !Number.isFinite(eta) || eta < 0) continue;
686
+ if (typeof tool !== "string" || !TOOL_KEY.test(tool) || excluded.includes(tool) || typeof eta !== "number" || !Number.isFinite(eta) || eta < 0) continue;
501
687
  if (typeof r.toAmount !== "string" || typeof r.toAmountMin !== "string" || !/^\d{1,60}$/.test(r.toAmount) || !/^\d{1,60}$/.test(r.toAmountMin)) continue;
502
688
  out.push({
503
689
  tool,
@@ -583,12 +769,12 @@ function lifiProvider(config = {}) {
583
769
  fromAmount: params.amountIn,
584
770
  fromAddress,
585
771
  toAddress,
586
- options: { slippage: slippageBps / 1e4, ...integrator().trim() ? { integrator: integrator().trim() } : {} }
772
+ options: { slippage: slippageBps / 1e4, ...solanaSource ? {} : { bridges: { deny: UNBOUND_TOOLS } }, ...integrator().trim() ? { integrator: integrator().trim() } : {} }
587
773
  }),
588
- signal: signal ?? (0, import_util2.timeoutSignal)(8e3)
774
+ signal: signal ?? (0, import_util3.timeoutSignal)(8e3)
589
775
  });
590
776
  if (res.ok) {
591
- const options = routeOptions(await res.json());
777
+ const options = routeOptions(await res.json(), solanaSource ? [] : UNBOUND_TOOLS);
592
778
  if (options.length) choice = cleanChoice(config.chooseRoute(options, params));
593
779
  }
594
780
  } catch {
@@ -608,10 +794,11 @@ function lifiProvider(config = {}) {
608
794
  if (sentSlippage !== void 0) {
609
795
  url.searchParams.set("slippage", String(sentSlippage));
610
796
  }
611
- for (const t of choice?.allowBridges ?? []) url.searchParams.append("allowBridges", t);
612
- for (const t of choice?.denyBridges ?? []) url.searchParams.append("denyBridges", t);
797
+ const unbound = params.sourceChain !== params.destChain && !solanaSource ? UNBOUND_TOOLS : [];
798
+ for (const t of (choice?.allowBridges ?? []).filter((b) => solanaSource || !UNBOUND_TOOLS.includes(b))) url.searchParams.append("allowBridges", t);
799
+ for (const t of /* @__PURE__ */ new Set([...choice?.denyBridges ?? [], ...unbound])) url.searchParams.append("denyBridges", t);
613
800
  if (choice?.order) url.searchParams.set("order", choice.order);
614
- deadline = signal ? void 0 : (0, import_util2.timeoutSignal)(12e3);
801
+ deadline = signal ? void 0 : (0, import_util3.timeoutSignal)(12e3);
615
802
  const res = await fetch(url, { headers: headers(apiKey()), signal: signal ?? deadline });
616
803
  if (!res.ok && choice) {
617
804
  choice = null;
@@ -653,6 +840,10 @@ function lifiProvider(config = {}) {
653
840
  if (solanaSource) {
654
841
  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
842
  if (tr.to !== void 0 || tr.value !== void 0 || tr.gasLimit !== void 0) return reject("the Solana transaction carries EVM fields");
843
+ if (fromChain !== toChain) {
844
+ const why = solanaBridgeTxMismatch(tr.data, fromAddress);
845
+ if (why) return reject(why);
846
+ }
656
847
  } 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
848
  if (sentSlippage !== void 0 && (typeof action.slippage !== "number" || Math.abs(action.slippage - sentSlippage) > 1e-12)) return reject("the quote ignored the requested slippage");
658
849
  const sourceIsToken = fromToken !== EVM_ZERO && fromToken !== SOLANA_NATIVE;
@@ -690,7 +881,7 @@ function lifiProvider(config = {}) {
690
881
  if (sentSlippage !== void 0) {
691
882
  const dest = BigInt(est.toAmount);
692
883
  const min = BigInt(minDestAmount);
693
- if (dest > 0n && min < dest * BigInt(1e4 - slippageBps) / 10000n) {
884
+ if (dest > 0n && min < (0, import_validate.slippageFloor)(dest, slippageBps)) {
694
885
  const effective = (0, import_core.bigintToNumber)((dest - min) * 1000000n / dest) / 1e6;
695
886
  const requested = slippageBps / 1e4;
696
887
  const scaled = Math.floor(sentSlippage * requested / effective * 0.995 * 1e6) / 1e6;
@@ -719,6 +910,7 @@ function lifiProvider(config = {}) {
719
910
  toToken: expectedToToken,
720
911
  amountIn: BigInt(params.amountIn),
721
912
  receiver: toAddress,
913
+ sender: fromAddress,
722
914
  minOut: BigInt(minDestAmount),
723
915
  destChainId: BigInt(toChain),
724
916
  sameChain: fromChain === toChain,
@@ -737,6 +929,7 @@ function lifiProvider(config = {}) {
737
929
  sourceAmount: params.amountIn,
738
930
  destAmount: est.toAmount,
739
931
  minDestAmount,
932
+ ...Number.isInteger(action.toToken?.decimals) && action.toToken.decimals >= 0 && action.toToken.decimals <= 36 ? { destDecimals: action.toToken.decimals } : {},
740
933
  feeUsd,
741
934
  etaSeconds: est.executionDuration ?? 120,
742
935
  reliability: 0.97,
@@ -773,7 +966,7 @@ function lifiProvider(config = {}) {
773
966
  url.searchParams.set("txHash", ref.sourceTxHash);
774
967
  if (fromChain) url.searchParams.set("fromChain", fromChain);
775
968
  if (toChain) url.searchParams.set("toChain", toChain);
776
- const res = await fetch(url, { headers: headers(apiKey()), signal: (0, import_util2.timeoutSignal)(12e3) });
969
+ const res = await fetch(url, { headers: headers(apiKey()), signal: (0, import_util3.timeoutSignal)(12e3) });
777
970
  if (!res.ok) return { state: "pending" };
778
971
  const s = await res.json();
779
972
  const map = {
@@ -804,7 +997,10 @@ function lifiVerifyQuote(params, quote) {
804
997
  if (!fromChain || !toChain) return "not a LI.FI chain pair";
805
998
  const tx = quote.txRequest;
806
999
  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";
1000
+ if (isSolana(params.sourceChain)) {
1001
+ if (!tx.serializedTx || tx.data !== void 0 || tx.to !== void 0) return "a Solana source carries a serialized transaction only";
1002
+ return fromChain === toChain ? null : solanaBridgeTxMismatch(tx.serializedTx, params.refundAddress || (params.dry !== false ? SOLANA_PLACEHOLDER : ""));
1003
+ }
808
1004
  if (!isHexData(tx.data) || !tx.to || tx.serializedTx !== void 0) return "the quote carries no Diamond call";
809
1005
  if (!sameAddress(tx.to, diamondFor(params.sourceChain))) return `the call targets ${tx.to}, not the LI.FI Diamond`;
810
1006
  if (tx.chainId !== void 0 && String(tx.chainId) !== fromChain) return `the call is for chain ${tx.chainId}, not ${fromChain}`;
@@ -839,6 +1035,7 @@ function lifiVerifyQuote(params, quote) {
839
1035
  toToken: nativeFor(params.destChain, params.destToken),
840
1036
  amountIn,
841
1037
  receiver: toAddress,
1038
+ sender: fromAddress,
842
1039
  minOut,
843
1040
  destChainId: BigInt(toChain),
844
1041
  sameChain: fromChain === toChain,