npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@real-wagmi/equilibra-smart-router

v1.4.1

Published

Trade routing for the EquilibraSwap cubic-invariant AMM: candidate discovery, route search and split optimization over the offline quoting SDK

Readme

@real-wagmi/equilibra-smart-router

Trade routing for the EquilibraSwap cubic-invariant AMM: candidate pool discovery (indexer GraphQL with on-chain fallback), route enumeration, split optimization with a gas model, and router calldata — all quoting offline through @real-wagmi/equilibra-sdk's mirror of the protocol's quote kernel (protocol build a1b7ad3; the pinned quotes in this package's specs are dumped from an in-process deployment of it). No RPC in the hot path.

Install

pnpm add @real-wagmi/equilibra-smart-router @real-wagmi/equilibra-sdk @real-wagmi/v2-sdk viem

Quick start

import { ChainId, CurrencyAmount, Percent, TradeType, nativeOnChain, robinhoodTokens } from '@real-wagmi/v2-sdk';
import {
    GraphqlPoolProvider,
    OnChainPoolProvider,
    OnChainStateRefresher,
    createGasModel,
    getBestTrade,
    swapCallParameters,
    withPoolProviderFallback,
} from '@real-wagmi/equilibra-smart-router';
import { createPublicClient, hexToBigInt, http } from 'viem';

const client = createPublicClient({ chain, transport: http(RPC_URL) });

// Candidate sources: the indexer API is primary (pools arrive with TVL and
// full quoting state in ONE query), the chain is the fallback.
const poolProvider = withPoolProviderFallback([
    new GraphqlPoolProvider({
        endpoint: (chainId) => `https://api.example.com/graphql/${chainId}`,
        factory: FACTORY_ADDRESS, // the factory the trade will EXECUTE against
    }),
    new OnChainPoolProvider({ client, factory: FACTORY_ADDRESS }),
]);

// Gas pricing (optional): measured dev-chain constants + native prices
// from the indexer (`tokens { address derivedNativeWadRaw }`).
const gasModel = createGasModel({
    gasPriceWei: await client.getGasPrice(),
    nativePriceWadByToken, // Record<lowercase address, bigint>
});

// Head-state refresh (recommended): the indexer may lag the chain by a
// few blocks — the refresher re-snapshots the SELECTED pools in one
// multicall so quotes run on exactly the state execution will see.
const stateRefresher = new OnChainStateRefresher({ client });

const eth = nativeOnChain(ChainId.ROBINHOOD);
const trade = await getBestTrade(
    CurrencyAmount.fromRawAmount(eth, 10n ** 18n), // exact side
    robinhoodTokens.anon,                          // the other side
    TradeType.EXACT_INPUT,
    { poolProvider, gasModel, stateRefresher },
);
if (!trade) throw new Error('no route');

trade.outputAmount.toSignificant(6); // best obtainable ANON
trade.routes.map((route) => `${route.percent}% via ${route.path.map((token) => token.symbol).join(' -> ')}`);

// Split layer versions. The default is v2 — the percent grid plus a
// water-filling refinement of every finalist, so leg allocations are
// exact integer wei rather than grid multiples. v1 — the plain 5% grid —
// stays callable for measurement and as the fallback story. v2 never
// returns a worse gas-adjusted quote than v1 for the same inputs (the
// pour is seeded with the grid winner); the 2026-08-24 stand A/B
// (git history, 68f6f3e) measured exactly that at scale: zero losses
// in 99,867 comparisons, +4.7 bps mean, at ~3x v1's search time
// (12.4 ms vs 4.3 ms).
// `trade` above IS a v2 trade — no option needed. The explicit form of
// both, on the same inputs:
const v2 = await getBestTrade(
    CurrencyAmount.fromRawAmount(eth, 10n ** 18n),
    robinhoodTokens.anon,
    TradeType.EXACT_INPUT,
    { poolProvider, gasModel, stateRefresher, splitVersion: 'v2' }, // same as omitting it
);
const v1 = await getBestTrade(
    CurrencyAmount.fromRawAmount(eth, 10n ** 18n),
    robinhoodTokens.anon,
    TradeType.EXACT_INPUT,
    { poolProvider, gasModel, stateRefresher, splitVersion: 'v1' }, // the plain grid
);

// In a v2 trade `route.percent` is a DISPLAY label (nearest-integer
// apportionment, sums to 100, can be 0 for a sub-half-percent sliver);
// `route.amount`/`route.quote` carry the exact allocations. Consumers
// that settle or account read the amounts, never the percents.

// Execute through the router.
const { calldata, value } = swapCallParameters(trade, {
    slippageTolerance: new Percent(50n, 10_000n), // 0.5%
    recipient: account,
    deadline: BigInt(Math.floor(Date.now() / 1000) + 1200),
});
await walletClient.sendTransaction({ to: ROUTER_ADDRESS, data: calldata, value: hexToBigInt(value) });

How a trade is found

  1. CandidatesPoolProvider.getCandidatePools returns every pool between {input, output} ∪ bases (default bases: the chain's [WETH9, ...STABLE_COINS, ...EXTRA_BASES] — the extras are a routing policy registry in this package, e.g. ANON on Robinhood). The GraphQL provider gets them in one indexer query, factory-scoped, with TVL; the on-chain provider discovers via factory.getPoolsByPair + one batched state multicall (TVL unknown → ranking degrades to discovery order). Either way a pool that is PAUSED OR PERMANENTLY STOPPED (paused() returns both latches; the stop is irreversible and implies the pause), has no anchor yet (priceScale 0, pre-first-liquidity) or has emptied a reserve side is not a candidate.
  2. SelectionselectPoolsByTvl trims to the pools worth routing over (per-base buckets around each endpoint, direct pools, native bridges, global top-TVL, second hops — the v3 heuristic, with topN raised from 2 to 4 on our own routing data; see the constant's note). With a stateRefresher configured, the SELECTED candidates are then re-snapshotted at the chain head in one multicall (OnChainStateRefresher) — ranking may run on indexer-lagged TVL, but quotes run on exactly the state execution will see. With the GraphQL provider primary, the refresher is also the only fresh source of the pause/stop latches (the on-chain provider reads them at discovery, but it is the fallback): state.paused is as fresh as the indexer's fold, and the indexer has not yet re-pinned the PauseStateChanged topic a1b7ad3 changed — until it does, a pool paused or stopped after the update can still arrive as a live candidate. Configure a stateRefresher if you settle.
  3. RoutescomputeAllRoutes DFS up to maxHops (default 3), no pool reused within a route.
  4. Grid + quotes — the amount is sliced at every multiple of distributionPercent (default 5%) as integer raw amounts; OfflineQuoteProvider simulates every (route, slice) through the SDK kernel and folds the gas model into quoteAdjustedForGas. A slice the pool REFUSES is dropped and the search goes on; the refusal set is exactly the SDK's QuoteRefusalCode — paused, zero or dust amount, insufficient liquidity, insufficient output, and the three classes a1b7ad3 added: numeric domain (MathOutOfRange), solver non-convergence (SolverDidNotConverge) and LP-depth decrease (LpValueDecreased), each a refusal of THIS amount on THIS state, which is how the on-chain router's own price-target probe classifies them. Anything else — a solady arithmetic fault, a kernel invariant — is a bug and PROPAGATES out of getBestTrade, exactly as the chain propagates it.
  5. Split searchgetBestRouteCombinations runs the BFS over percent buckets: disjoint routes only (a pool may serve ONE leg of a split), up to maxSplits (default 4), ranked by summed gas-adjusted quotes, exact ties preferring fewer pools.
  6. Exact re-simulation — the top-K finalists (default 3) are re-run at integer allocations summing EXACTLY to the request (grid floor-dust folds into the largest leg); the winner is judged on the re-simulated numbers. The result's amounts are precisely what execution settles against the snapshot — for EXACT_INPUT, to the raw unit. For EXACT_OUTPUT the guarantee is the protocol's own inversion bound: replaying the quoted input exact-in delivers at least out - 1 raw units PER LEG (a1b7ad3 solves exact-out for out + max(1, out/99999999) and takes no input surcharge), so a k-way split carries up to k units of slack. Slippage minimums are what protect the settlement; see Calldata.
  7. The pour (v2, the default) — each exactly-re-simulated finalist's allocation is refined by water-filling (utils/water-fill-allocation): integer wei move between legs in a halving-Δ schedule — each Δ is retried until no move of that size improves, and the whole descending schedule (floored at one wei) repeats after any sweep that accepted a move. A move is accepted only when the SUMMED objective strictly improves — more output for exact-in, less input for exact-out. That objective test is also why the pour survives a1b7ad3's weaker quote response: at the newly admissible λ = 1e12 the checked quote is not monotone in the input and a refusal can be a HOLE (refused at X, quotable at X ± δ), which the pour treats as a veto on that move and nothing more. Never-worse-than-seed and termination do not rest on curvature; optimality is a MEASUREMENT, and it is measured against brute force at the envelope corners (see utils/water-fill-allocation.test.ts). The pour is seeded with the finalist's own allocation, so it can never do worse than the grid; legs poured empty are dropped and the survivors requoted. Route DISCOVERY is untouched — the pour polishes allocations within a route set, it cannot invent routes, which is why the 5% grid stays the search's resolution (coarser grids were measured and REJECTED: they miss route sets the pour cannot recover). splitVersion: 'v1' skips this step; any other value than 'v1'/'v2'/omitted throws.

Calldata

swapCallParameters(trade, { slippageTolerance, recipient, deadline }) returns { calldata, value } for the router:

  • ≤ 2 splits — the SDK SwapRouter encoding: per-route minimums.
  • > 2 splits — aggregated slippage: every leg swaps with minimum 0 to router custody, and a single closing sweepToken / unwrapWETH9 pays the recipient enforcing the bound on the TOTAL (per-leg minimums would revert the batch on harmless per-leg jitter). Native input attaches the summed per-leg maximums and closes with refundETH when it can underspend.

Send value verbatim — it is the exact native input the batch can consume, not a floor. The router's refundETH() is permissionless, so an over-attached surplus is claimable by the next account that calls it.

Price impact breakdown

getTradeBreakdown(trade) decomposes the quote's deviation from spot, Uniswap-style: feePct (the route's resolved DYNAMIC fee — paid at any size) apart from impactPct (what the trade's size did to the price along the curve, from re-quoting each leg's gross input through zero-fee clones of the same pool snapshots). totalPct = feePct + impactPct exactly; when the fee-free counterfactual cannot quote, only totalPct is returned (feePct/impactPct null). Show the two apart — the fee-inclusive number makes every quote look 1.4–2.8%/hop worse than the size actually moved the market.

Configuration

All getBestTrade parameters are optional except poolProvider:

| Option | Default | Meaning | | --- | --- | --- | | splitVersion | 'v2' | 'v2' = grid + water-filling pour; 'v1' = plain grid; anything else throws | | quoteProvider | offline over gasModel | quoting seam (an on-chain quoter can slot in) | | stateRefresher | none | head-state re-snapshot of selected candidates | | gasModel | zero-cost | see createGasModel | | maxHops | 3 | route length cap | | maxSplits | 4 | disjoint routes per trade | | distributionPercent | 5 | split grid step | | topK | 3 | finalists re-simulated exactly | | bases | [WETH9, ...STABLE_COINS, ...EXTRA_BASES] | intermediate tokens | | selector | v3 defaults, topN 4 | TVL bucket caps |

createGasModel({ gasPriceWei, nativePriceWadByToken }): units are an affine curve, 60k base + 150k per hop, fit on 324 executed a1b7ad3 receipts — 270 MULTI-HOP router swaps over two four-token chains of three pools per config (1, 2 and 3 hops; exactInput, exactOutput, exactInputSingle; ERC20, WETH and native input — the sampling grid per entrypoint is stated in constants/gas.ts) plus the 54 single-hop swaps of the nine parity scenarios. The affine SHAPE is measured, not assumed: the marginal between the per-hop maxima is 130.9..131.4k with auto-repeg off and 146.0..146.4k with an auto-repeg on every hop (over every individual exactInput series 128.1..131.4k and 143.3..146.4k; the exactOutput series sit lower, 122.3k and 140.0..140.2k), and within every series the 1→2 and 2→3 marginals agree to within 400 gas. The slope is the repegging marginal rounded up; the base absorbs the one-time native wrap (+9,615) and the storage-skewed 1-hop corpus point, so the envelope covers every receipt (1 hop 210,000 ≥ 204,084; 2 hops 360,000 ≥ 342,155; 3 hops 510,000 ≥ 488,307). See constants/gas.ts for the per-config table and the blind spots — skewed reserves measured at one hop only, the native side measured as a direct payable exactInput rather than through multicall + refundETH/unwrapWETH9, one curve pair, and hardhat execution gas with no L1 data cost. Cost converts native → quote currency through the supplied prices (the indexer's derivedNativeWadRaw verbatim; numeric overrides welcome). No gas price → zero cost, and the split search tie-breaks toward fewer hops. These constants RANK routes; they are not execution limits.