@anywalletkit/protocol-swap2-lifi 0.5.0 → 0.6.1

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
@@ -133,10 +133,10 @@ var BRIDGE_CALLS = {
133
133
  // startBridgeTokensViaChainflip (ChainflipFacet)
134
134
  "0xee3314a1": { sourceSwaps: true, facet: "Chainflip" },
135
135
  // swapAndStartBridgeTokensViaChainflip (ChainflipFacet)
136
- "0xbff90b61": { sourceSwaps: false, facet: "Eco" },
137
- // startBridgeTokensViaEco (EcoFacet)
138
- "0x762aea18": { sourceSwaps: true, facet: "Eco" },
139
- // swapAndStartBridgeTokensViaEco (EcoFacet)
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
140
  "0x678786ea": { sourceSwaps: false, facet: "Frax" },
141
141
  // startBridgeTokensViaFrax (FraxFacet)
142
142
  "0x07b8e4c7": { sourceSwaps: true, facet: "Frax" },
@@ -350,20 +350,50 @@ function acrossSwapRefunds(r, at) {
350
350
  if (target === 3n) return [["the sponsored CCTP refund recipient", c.address(64), true]];
351
351
  throw new Error("unknown Across Swap API target");
352
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
+ }
353
383
  var FACET_DATA = {
354
384
  AcrossV4: { refunds: [["refundAddress", 1]], zeroRefused: true },
355
385
  // the SpokePool depositor: an expired deposit refunds the input there
356
386
  AcrossV4Swap: { nested: acrossSwapRefunds },
357
387
  Centrifuge: { inline: 2, refunds: [["refundRecipient", 1]], zeroRefused: true },
358
388
  Chainflip: { nested: chainflipRefunds },
359
- Eco: { refunds: [["refundRecipient", 5]], zeroRefused: true },
389
+ Eco: { refunds: [["refundRecipient", 6]], zeroRefused: true, delivery: ecoDelivery },
360
390
  // the intent's creator, refunded its reward (the input) on expiry
361
391
  Frax: { inline: 5, refunds: [["refundRecipient", 3]], zeroRefused: true },
362
392
  Glacis: { inline: 4, refunds: [["refundAddress", 1]], zeroRefused: true },
363
393
  LayerSwap: { refunds: [["refundRecipient", 2]], zeroRefused: true },
364
394
  LiFiIntentEscrowV2: { refunds: [["depositAndRefundAddress", 2]], zeroRefused: true },
365
395
  // the escrow order's user
366
- M0: { inline: 7, refunds: [["refundRecipient", 1], ["orderOwner", 2]], zeroRefused: true },
396
+ M0: { inline: 7, refunds: [["refundRecipient", 1], ["orderOwner", 2]], zeroRefused: true, delivery: m0Delivery },
367
397
  // orderOwner gets a cancelled order back
368
398
  Mayan: { refunds: [["refundRecipient", 7]], nested: mayanRefunds, zeroRefused: true },
369
399
  NEARIntents: { refunds: [["refundRecipient", 6]], zeroRefused: true },
@@ -374,11 +404,14 @@ var FACET_DATA = {
374
404
  Superset: { refunds: [["refundAddress", 2], ["fallbackEoA", 3]], zeroRefused: true }
375
405
  };
376
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
+ }
377
411
  function refundMismatch(r, bridge, e) {
378
412
  const spec = FACET_DATA[bridge.facet];
379
413
  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);
414
+ const at = facetDataAt(r, bridge, spec);
382
415
  const paid = (spec.refunds ?? []).map(([field, word]) => [`${bridge.facet} ${field}`, r.address(at + 32 * word), spec.zeroRefused === true]);
383
416
  paid.push(...spec.nested?.(r, at) ?? []);
384
417
  for (const [what, to, zeroRefused] of paid) {
@@ -433,6 +466,9 @@ function diamondCallMismatch(data, e) {
433
466
  if (hasSourceSwaps !== bridge.sourceSwaps) return `unknown Diamond call ${selector}`;
434
467
  const refund = refundMismatch(r, bridge, e);
435
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;
436
472
  if (hasSourceSwaps) {
437
473
  if (minAmount === 0n) return "bridges nothing after the source swaps";
438
474
  return checkSteps(swapsAt(r, 4, 4 + 32), e, sendingAssetId);
@@ -747,6 +783,9 @@ function lifiProvider(config = {}) {
747
783
  const fromToken = lifiToken(params);
748
784
  if (!fromToken) return null;
749
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);
750
789
  const dry = params.dry !== false;
751
790
  const fromAddress = params.refundAddress || (dry ? solanaSource ? SOLANA_PLACEHOLDER : EVM_PLACEHOLDER : "");
752
791
  const toAddress = params.destAddress || (dry ? fromAddress : "");
@@ -756,7 +795,7 @@ function lifiProvider(config = {}) {
756
795
  let sentSlippage = slippageBps / 1e4;
757
796
  let deadline;
758
797
  let choice = null;
759
- if (config.chooseRoute && params.sourceChain !== params.destChain) {
798
+ if (config.chooseRoute && crossChain) {
760
799
  try {
761
800
  const res = await fetch(`${apiBase}/advanced/routes`, {
762
801
  method: "POST",
@@ -769,7 +808,13 @@ function lifiProvider(config = {}) {
769
808
  fromAmount: params.amountIn,
770
809
  fromAddress,
771
810
  toAddress,
772
- options: { slippage: slippageBps / 1e4, ...solanaSource ? {} : { bridges: { deny: UNBOUND_TOOLS } }, ...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
+ }
773
818
  }),
774
819
  signal: signal ?? (0, import_util3.timeoutSignal)(8e3)
775
820
  });
@@ -794,7 +839,9 @@ function lifiProvider(config = {}) {
794
839
  if (sentSlippage !== void 0) {
795
840
  url.searchParams.set("slippage", String(sentSlippage));
796
841
  }
797
- const unbound = params.sourceChain !== params.destChain && !solanaSource ? UNBOUND_TOOLS : [];
842
+ if (routeScope) url.searchParams.set("slippageScope", "route");
843
+ if (noDestinationCall) url.searchParams.set("allowDestinationCall", "false");
844
+ const unbound = crossChain && !solanaSource ? UNBOUND_TOOLS : [];
798
845
  for (const t of (choice?.allowBridges ?? []).filter((b) => solanaSource || !UNBOUND_TOOLS.includes(b))) url.searchParams.append("allowBridges", t);
799
846
  for (const t of /* @__PURE__ */ new Set([...choice?.denyBridges ?? [], ...unbound])) url.searchParams.append("denyBridges", t);
800
847
  if (choice?.order) url.searchParams.set("order", choice.order);
@@ -808,6 +855,11 @@ function lifiProvider(config = {}) {
808
855
  if (!res.ok) {
809
856
  const body = await res.json().catch(() => null);
810
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
+ }
811
863
  const why = reasons.length === 1 ? ` (${reasons[0].slice(0, 200)})` : "";
812
864
  params.onRefusal?.(`lifi: ${typeof body?.message === "string" ? body.message.slice(0, 200) : `HTTP ${res.status}`}${why}`);
813
865
  return null;