@anywalletkit/protocol-swap2-curve 0.1.0 → 0.1.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.
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  CURVE_POOLS,
3
3
  curveProvider
4
- } from "./chunk-FICPW2P2.js";
4
+ } from "./chunk-IFREGXMV.js";
5
5
  import {
6
6
  CURVE_DEPLOYMENTS,
7
7
  deploymentOf
@@ -35,4 +35,4 @@ var CurveSwap2Protocol = class extends SingleProviderSwap2 {
35
35
  export {
36
36
  CurveSwap2Protocol
37
37
  };
38
- //# sourceMappingURL=chunk-IIPLIHNY.js.map
38
+ //# sourceMappingURL=chunk-JCWXGKPJ.js.map
package/dist/index.cjs CHANGED
@@ -644,25 +644,56 @@ var CURVE_POOLS = Object.freeze({
644
644
  // src/rpc.ts
645
645
  var import_util2 = require("@anywalletkit/protocol-swap2-core/util");
646
646
  var RPC_BATCH_MAX = 10;
647
+ var RPC_RATE_LIMIT_PAUSE_MS = 1e3;
648
+ function isExecutionError(error) {
649
+ if (!error || typeof error !== "object") return false;
650
+ const { code, message } = error;
651
+ return code === 3 || typeof message === "string" && /revert|invalid opcode|out of gas/i.test(message);
652
+ }
653
+ function isRateLimit(error) {
654
+ if (!error || typeof error !== "object") return false;
655
+ const { code, message } = error;
656
+ return code === -32005 || code === -32007 || code === 429 || typeof message === "string" && /rate|limit|too many/i.test(message);
657
+ }
658
+ var errorText = (error) => {
659
+ const m = error && typeof error === "object" ? error.message : void 0;
660
+ return (0, import_util2.redactUrls)(typeof m === "string" ? m : JSON.stringify(error) ?? String(error)).slice(0, 160);
661
+ };
647
662
  function rpcClient(rpcUrls) {
663
+ async function once(url, calls, signal) {
664
+ const res = await fetch(url, {
665
+ method: "POST",
666
+ headers: { "content-type": "application/json" },
667
+ body: JSON.stringify(calls.map((c, id) => ({ jsonrpc: "2.0", id, ...c }))),
668
+ signal: signal ?? (0, import_util2.timeoutSignal)(12e3)
669
+ });
670
+ if (!res.ok) throw Object.assign(new Error(`HTTP ${res.status}`), { limited: res.status === 429 });
671
+ const body = await res.json();
672
+ if (!Array.isArray(body)) throw Object.assign(new Error(body.error?.message ?? "batch refused"), { limited: isRateLimit(body.error) });
673
+ const out = calls.map(() => null);
674
+ const answered = /* @__PURE__ */ new Set();
675
+ for (const r of body) {
676
+ if (typeof r.id !== "number" || r.id < 0 || r.id >= out.length) continue;
677
+ if (r.error !== void 0 && r.error !== null && !isExecutionError(r.error)) throw Object.assign(new Error(errorText(r.error)), { limited: isRateLimit(r.error) });
678
+ answered.add(r.id);
679
+ if (r.error === void 0 || r.error === null) out[r.id] = r.result ?? null;
680
+ }
681
+ if (answered.size !== calls.length) throw new Error(`answered ${answered.size} of ${calls.length} calls`);
682
+ return out;
683
+ }
648
684
  async function batch(chainId, calls, signal) {
649
685
  let lastErr;
650
- for (const url of rpcUrls[chainId] ?? []) {
651
- try {
652
- const res = await fetch(url, {
653
- method: "POST",
654
- headers: { "content-type": "application/json" },
655
- body: JSON.stringify(calls.map((c, id) => ({ jsonrpc: "2.0", id, ...c }))),
656
- signal: signal ?? (0, import_util2.timeoutSignal)(12e3)
657
- });
658
- if (!res.ok) throw new Error(`HTTP ${res.status}`);
659
- const body = await res.json();
660
- if (!Array.isArray(body)) throw new Error(body.error?.message ?? "batch refused");
661
- const out = calls.map(() => null);
662
- for (const r of body) if (typeof r.id === "number" && r.id >= 0 && r.id < out.length && r.error === void 0) out[r.id] = r.result ?? null;
663
- return out;
664
- } catch (e) {
665
- lastErr = e;
686
+ for (let pass = 0; pass < 2; pass++) {
687
+ if (pass > 0) {
688
+ if (!lastErr?.limited || signal?.aborted) break;
689
+ await new Promise((r) => setTimeout(r, RPC_RATE_LIMIT_PAUSE_MS));
690
+ }
691
+ for (const url of rpcUrls[chainId] ?? []) {
692
+ try {
693
+ return await once(url, calls, signal);
694
+ } catch (e) {
695
+ lastErr = e;
696
+ }
666
697
  }
667
698
  }
668
699
  throw new Error(`curve: no RPC answered on chain ${chainId} (${lastErr instanceof Error ? lastErr.message : String(lastErr)})`);
@@ -675,11 +706,16 @@ function rpcClient(rpcUrls) {
675
706
  };
676
707
  }
677
708
  var ethCall = (to, data) => ({ method: "eth_call", params: [{ to, data }, "latest"] });
709
+ function firstWord(raw) {
710
+ return typeof raw === "string" && raw.length >= 66 && /^0x(?:[0-9a-fA-F]{64})+$/.test(raw) ? raw.slice(2, 66).toLowerCase() : null;
711
+ }
678
712
  function wordAddress(raw) {
679
- return typeof raw === "string" && /^0x[0-9a-fA-F]{64}$/.test(raw) ? `0x${raw.slice(-40)}`.toLowerCase() : null;
713
+ const w = firstWord(raw);
714
+ return w !== null && /^0{24}/.test(w) ? `0x${w.slice(24)}` : null;
680
715
  }
681
716
  function wordUint(raw) {
682
- return typeof raw === "string" && /^0x[0-9a-fA-F]{64}$/.test(raw) ? BigInt(raw) : null;
717
+ const w = firstWord(raw);
718
+ return w === null ? null : BigInt(`0x${w}`);
683
719
  }
684
720
 
685
721
  // src/slippage.ts
@@ -837,20 +873,32 @@ function curveOnChain(opts) {
837
873
  await verifyDeployment(chainId);
838
874
  const index = poolIndex(chainId, d2, own(pools, chainId));
839
875
  const hops = routePools(call).map((a) => index.get(lc2(a)));
840
- const calls = [ethCall(d2.router, encodeGetDy(d2.family, call, call.amount))];
841
- for (const pool of hops) {
842
- calls.push({ method: "eth_getCode", params: [pool.address, "latest"] });
843
- pool.coins.forEach((_, k) => calls.push(ethCall(pool.address, `${COINS_UINT}${k.toString(16).padStart(64, "0")}`)));
844
- }
876
+ const reads = poolCalls(hops);
877
+ const calls = [ethCall(d2.router, encodeGetDy(d2.family, call, call.amount)), ...reads];
845
878
  const probeIn = call.amount / 1000n;
846
879
  const probeAt = calls.length;
847
880
  if (probe && probeIn > 0n) calls.push(ethCall(d2.router, encodeGetDy(d2.family, call, probeIn)));
848
881
  const res = await rpc(chainId, calls);
849
- let at = 1;
882
+ const poolReason = await poolsReason(chainId, hops, res.slice(1, 1 + reads.length));
883
+ if (poolReason) return poolReason;
884
+ const out = wordUint(res[0]);
885
+ if (out === null) return "the router no longer quotes this route";
886
+ if (out < call.minDy) return `price moved \u2014 the pools now pay ${out}, under your minimum ${call.minDy}`;
887
+ return { out, probeOut: probe && probeIn > 0n ? wordUint(res[probeAt]) : null, amount: call.amount, minDy: call.minDy };
888
+ }
889
+ function poolCalls(list) {
890
+ return list.flatMap((pool) => [
891
+ { method: "eth_getCode", params: [pool.address, "latest"] },
892
+ ...pool.coins.map((_, k) => ethCall(pool.address, `${COINS_UINT}${k.toString(16).padStart(64, "0")}`))
893
+ ]);
894
+ }
895
+ async function poolsReason(chainId, list, res) {
896
+ let at = 0;
850
897
  const retry = [];
851
- for (const pool of hops) {
898
+ for (const pool of list) {
852
899
  const code = res[at++];
853
- if (typeof code !== "string" || code.length <= 2 || lc2((0, import_viem3.keccak256)(code)) !== lc2(pool.codeHash)) return `pool ${pool.name} (${pool.address}) is not the pinned code`;
900
+ if (typeof code !== "string") return `could not read pool ${pool.name} (${pool.address}) over RPC`;
901
+ if (code.length <= 2 || lc2((0, import_viem3.keccak256)(code)) !== lc2(pool.codeHash)) return `pool ${pool.name} (${pool.address}) is not the pinned code`;
854
902
  for (let k = 0; k < pool.coins.length; k++) {
855
903
  const coin = wordAddress(res[at++]);
856
904
  if (coin === null) retry.push({ pool, k });
@@ -864,12 +912,12 @@ function curveOnChain(opts) {
864
912
  if (coin !== lc2(pool.coins[k])) return `pool ${pool.name} does not report its pinned coin ${k}`;
865
913
  }
866
914
  }
867
- const out = wordUint(res[0]);
868
- if (out === null) return "the router no longer quotes this route";
869
- if (out < call.minDy) return `price moved \u2014 the pools now pay ${out}, under your minimum ${call.minDy}`;
870
- return { out, probeOut: probe && probeIn > 0n ? wordUint(res[probeAt]) : null, amount: call.amount, minDy: call.minDy };
915
+ return null;
916
+ }
917
+ async function checkPools(chainId, list) {
918
+ return list.length ? poolsReason(chainId, list, await rpc(chainId, poolCalls(list))) : null;
871
919
  }
872
- return { rpc, verifyDeployment, checkSealed, inspectSealed };
920
+ return { rpc, verifyDeployment, checkSealed, inspectSealed, checkPools };
873
921
  }
874
922
  function lastToken(call) {
875
923
  let k = 0;