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-limit-orders

v1.1.1

Published

Maker-side client for Equilibra limit orders: the EIP-712 order model and signing, price-to-floor math replayed against the node's vectors, fillability over the offline quoting SDK, and a typed client for the limit-order node API

Readme

@real-wagmi/equilibra-limit-orders

Maker-side client for Equilibra limit orders: build and sign an EIP-712 order that keeps the funds in the wallet, check whether a pool could fill it right now, and talk to the limit-order node — minimums, login, posting, listing, cancelling. The math is replayed digit for digit against the node's own test vectors and the deployed settlement contract.

Install

pnpm add @real-wagmi/equilibra-limit-orders @real-wagmi/equilibra-sdk @real-wagmi/v2-sdk viem

Step 5 below fetches a pool through the smart router's provider; that is optional and needs pnpm add @real-wagmi/equilibra-smart-router — any other way of obtaining an @real-wagmi/equilibra-sdk Pool works.

Usage

Robinhood chain as the example. The full path: read the node's minimum, build an order from a human price, validate it, check fillability, sign, post, follow it, cancel it.

1. Prepare the clients

Nothing the operator runs is pinned in the package: you pass the node's endpoint and the settlement address, the way the smart router takes its endpoint and factory.

import { LimitOrdersApiClient } from '@real-wagmi/equilibra-limit-orders/api';
import { createPublicClient, http } from 'viem';

const publicClient = createPublicClient({ transport: http('https://rpc.mainnet.chain.robinhood.com') });

const client = new LimitOrdersApiClient({
    endpoint: (chainId) => `${LIMIT_ORDERS_BASE}/${chainId}`, // or one full URL for a single chain
    settlement: SETTLEMENT_ADDRESS,                          // or a resolver by chain id
    // requester?: the subset of fetch the client needs; defaults to globalThis.fetch
});

// Identity first: /health names the chain and the settlement the node
// signs for — the chainId and verifyingContract of both EIP-712 domains
// (orders and login/cancel) — so a mis-pointed endpoint stops here,
// before any wallet prompt.
const health = await client.assertIdentity(4663);
health.settlement;                // the settlement, checksummed
health.minOrderAmountInFallback;  // the node's minimum at 8 decimals, e.g. 1_000_000n
health.expiryMarginSecs;          // the node's deadline window — 120n / 2_592_000n on Robinhood; step 4 takes it from here
health.maxDeadlineSecs;
health.executionBufferBps;        // the keeper's margin above the floor — 50 on Robinhood; step 5 takes it from here

2. Read the minimum order size

import { minimumOrderAmount } from '@real-wagmi/equilibra-limit-orders';
import { robinhoodTokens } from '@real-wagmi/v2-sdk';

const weth = robinhoodTokens.weth;
const anon = robinhoodTokens.anon;

// The node's answer for the input token — a hub-valued minimum or its
// decimal fallback. Fetch it right before signing: the node sends
// Cache-Control: no-store, and a hub-valued minimum can move with pool
// state.
const minimum = await client.orderMinimum(4663, weth.address);
minimum.amountIn; // raw WETH, e.g. 10_000_000_000_000_000n (0.01 WETH under the fallback)
minimum.source;   // 'hub_value' | 'fallback' — can be 'fallback' for any token

// The same fallback, computed offline from /health for a token's decimals:
minimumOrderAmount(weth.decimals, health.minOrderAmountInFallback); // 10_000_000_000_000_000n

3. Build the order from a human price

Amounts are CurrencyAmounts and the price is a v2-sdk Price over raw units, so decimals are never placed by hand. The floor is the other team's rule — exact rational, floored — and a fractional amount is refused rather than rounded into a signed field.

import { buildLimitOrder, minAmountOutForPrice, parseHumanPrice } from '@real-wagmi/equilibra-limit-orders';
import { CurrencyAmount } from '@real-wagmi/v2-sdk';

const amountIn = CurrencyAmount.fromRawAmount(weth, 2n * 10n ** 16n); // sell 0.02 WETH
const price = parseHumanPrice(weth, anon, '7000');                     // want at least 7000 ANON per WETH
const minAmountOut = minAmountOutForPrice(amountIn, price);            // CurrencyAmount of 140 ANON (raw 140 × 10^18), floored

const order = buildLimitOrder({
    maker: account.address,
    tokenIn: weth,
    tokenOut: anon,
    amountIn,
    minAmountOut,
    deadline: BigInt(Math.floor(Date.now() / 1000) + 24 * 3600), // unix seconds, inclusive
    // receiver: defaults to the zero address = "pay the maker"
    // salt:     defaults to generateSalt() — 96 random bits from Web Crypto
});

4. Validate before the wallet prompt

validateOrder returns every violated code — the four client-only structural codes first, then the node's seven in the node's order — so a form can show all of them; assertValidOrder throws one OrderValidationError whose code is the first and whose codes are all of them. The node codes are the API's own wire strings; the deadline window is the node's policy and comes from /health, never typed by hand.

import { OrderValidationCode, assertValidOrder, validateOrder } from '@real-wagmi/equilibra-limit-orders';
import { nodeOrderPolicy } from '@real-wagmi/equilibra-limit-orders/api';

const ctx = {
    settlement: SETTLEMENT_ADDRESS,
    router: ROUTER_ADDRESS, // the node refuses the router and the settlement as receiver
    now: BigInt(Math.floor(Date.now() / 1000)),
    policy: nodeOrderPolicy(health), // the window from the same /health that assertIdentity checked
};

validateOrder(order, ctx); // [] for a good order
validateOrder({ ...order, deadline: ctx.now + 31n * 24n * 3600n }, ctx); // [OrderValidationCode.DEADLINE_TOO_FAR]
assertValidOrder(order, ctx); // throws OrderValidationError { code: codes[0], codes } otherwise

5. Check fillability against a pool

The verdict answers the keeper's own question for one direct pool: would the full signed amountIn, swapped exact-in now, clear the signed floor? Bring the Pool however you already do — a smart-router pool provider, or your own reads of the pool's views assembled into an @real-wagmi/equilibra-sdk Pool — and the platform share from the contract.

import { Fillability, quoteOrderFillability, readSettlementPolicy } from '@real-wagmi/equilibra-limit-orders';
import { GraphqlPoolProvider } from '@real-wagmi/equilibra-smart-router';

const provider = new GraphqlPoolProvider({ endpoint: `${GRAPHQL_BASE}/4663`, factory: FACTORY_ADDRESS });
const candidates = await provider.getCandidatePools({ currencyA: weth, currencyB: anon });
const { pool } = candidates.find((c) => c.pool.token0.equals(weth) && c.pool.token1.equals(anon))!;

const policy = await readSettlementPolicy({ client: publicClient, settlement: SETTLEMENT_ADDRESS });
policy.platformShareBps; // 8000 on Robinhood — the platform keeps 80% of the surplus above the floor

const verdict = quoteOrderFillability(pool, order, { ...policy, executionBufferBps: health.executionBufferBps });
switch (verdict.status) {
    case Fillability.FILLABLE:
        verdict.expectedOut;           // what the pool pays for the full amountIn now
        verdict.expectedMakerOut;      // what the maker receives after the platform's share of the surplus
        verdict.clearsExecutionBuffer; // present iff a buffer was given; false = the order sits at its limit and the keeper waits
        verdict.depthAtLimit;          // the router's sweep toward the limit price: a LOWER bound, never the verdict
        break;
    case Fillability.NOT_REACHABLE: // the pool pays less than the floor for this size
        verdict.expectedOut;
        verdict.depthAtLimit;
        break;
    case Fillability.NOT_REPRESENTABLE: // the limit is outside the pool's price domain
        break;
    case Fillability.PAUSED:
        verdict.stopped; // true = the pool's stop latch: this pause never lifts
        break;
    case Fillability.REFUSED: // the pool's quoter refused this size on this state
        verdict.refusal;
        break;
}

The verdict is a direct-pool approximation of the keeper's, not a bound on it: the keeper also prices multi-leg routes (which can fill what one pool cannot) and applies a minimum platform fee (which can hold back what one pool would fill). Its execution buffer IS answered, as a field: clearsExecutionBuffer false is a FILLABLE order sitting at its limit — the contract would pay the floor, but the keeper only submits once the route pays the floor plus the buffer (50 bps on Robinhood, from /health). clearsExecutionBuffer(expectedOut, minAmountOut, bps) is exported for any other quote you hold.

Three details of the verdict follow the pools' a1b7ad3 quote path and are worth reading before you render it:

  • REFUSED is the pool's own vocabulary, not just dust. The quote view refuses where it used to answer zero, so verdict.refusal can be AMOUNT_TOO_SMALL (a size whose output floors away), INSUFFICIENT_LIQUIDITY, LP_VALUE_DECREASED (the pool's strict LP-value guard), SOLVER_DID_NOT_CONVERGE or MATH_OUT_OF_RANGEthis size on this state, never a fault and never a permanent property of the pool. The router's own price-target search sorts them in two classes: dust (AMOUNT_TOO_SMALL) is TooSmall — the search moves on to larger inputs without recording the probe — and the other four are Rejected — a search ceiling. The union is the SDK's QuoteRefusalCode, so a new code appears here without a release of this package. A solady overflow (FullMulDivFailed and its siblings) is deliberately NOT a refusal: the chain propagates it and so does quoteOrderFillability. NOT_REACHABLE with expectedOut 0 now means one thing only — an unseeded pool.
  • depthAtLimit is a lower bound. quoteSwapToPrice is the ROUTER's search since 0f43f3d (the pool view was deleted), and each of its probes is a checked quote: a Rejected probe is a search ceiling (a TooSmall one is skipped), and the input is capped at ~99% of the input-side reserve, so a sweep can stop short of the limit with no flag saying it did. Read amountIn as "the pool absorbs at least this much before its marginal price reaches the limit" — the one guarantee the chain gives — and never as "the depth up to the limit". The capped flag this package used to derive is gone for exactly that reason.
  • PAUSED carries stopped. paused() returns (paused, stopped) and the stop is irreversible. Both states are one verdict, because the pool's swap guard reads only paused, but "waiting to be unpaused" and "will never fill on this pool again" are not the same sentence to a maker.

6. Sign and post

signer is anything with signTypedData — a viem account or a wallet client with a hoisted account, as is. An ECDSA signature is canonicalised to 65 bytes with v ∈ {27, 28} (a smart-account signature of another length passes through for the node to judge); the node's returned digest must equal the one computed locally, which proves both sides hashed the same struct under the same domain.

import { buildOrderDomain, signOrder } from '@real-wagmi/equilibra-limit-orders';
import { isApiError } from '@real-wagmi/equilibra-limit-orders/api';

const domain = buildOrderDomain(4663, SETTLEMENT_ADDRESS);
const signed = await signOrder(account, domain, order); // { order, digest, signature }

try {
    const posted = await client.postOrder(4663, signed);
    posted.digest; // === signed.digest, checked by the client
    posted.status; // 'open'
} catch (error) {
    if (isApiError(error)) {
        error.status; // e.g. 400 — also 409 duplicate_order, 503 node_at_capacity, 500 internal
        error.code;   // 'insufficient_balance' | 'insufficient_allowance' | 'order_too_small' | 'no_route' | …
    }
    throw error;
}

The maker's balance and allowance to the settlement must cover the sum of ALL their active orders in this input token plus the new one; the node checks that at posting time, after the signature. Check it yourself before asking for the signature — a refused post has already cost a wallet prompt.

7. Follow the orders

Reading needs a session: a Login signature under the API's own domain, answered with a JWT. issuedAt is your clock; the node accepts ±300 s.

import { OrderStatus } from '@real-wagmi/equilibra-limit-orders/api';

const session = await client.login(4663, account, account.address, BigInt(Math.floor(Date.now() / 1000)));
session.token;     // opaque; keep it with its maker — every node sharing the signing key accepts it; a 401 later means log in again
session.expiresAt; // unix seconds

// The default listing is the maker's OPEN orders. On Robinhood the node
// caps live orders per maker at 20, so the open set fits the default
// 100-row page and this is complete; a node with a cap above 100, or
// none, needs listAllOrders for the open set too.
const open = await client.listOrders(4663, session);
open[0]?.record.order;  // a LimitOrder, addresses checksummed
open[0]?.status;        // OrderStatus.OPEN

// Everything else is newest first (createdAtUnix DESC, digest DESC): an
// unfiltered page is the 100 most recent orders of any status; older
// pages by offset. The node prunes terminal records 30 days after
// creation (Robinhood's default), once the deadline has passed and the
// order is not being processed — history is the retained window, not
// everything ever placed.
const recent = await client.listOrders(4663, session, {});         // the 100 most recent
const everything = await client.listAllOrders(4663, session); // walks 1000-row pages to the end, de-duplicated by digest
const filled = await client.listAllOrders(4663, session, { status: [OrderStatus.FILLED] });

const detail = await client.getOrder(4663, session, signed.digest);
detail.fills; // [{ amountIn, makerOut, platformFee, txHash, blockNumber, … }]

Polling is yours: a React host uses its query library, a bot its own loop.

useQuery({
    queryKey: ['limit-orders', 4663, account.address],
    queryFn: () => client.listOrders(4663, session),
    refetchInterval: 10_000,
});

8. Cancel

There is no on-chain cancel. A cancel is a signed SoftCancel request to the node; it is irreversible, and the nonce is per order, 1..2^63 − 1, strictly greater than the last one the node accepted.

const cancelled = await client.cancelOrder(4663, account, signed.digest, 1n);
cancelled.status; // 'cancelled'
  • 409 order_in_flight — a fill is being submitted; the same signed request is retryable once it resolves (the nonce was not consumed).
  • 409 order_not_cancellable — filled, cancelled or expired; terminal.
  • 403 bad_nonce — choose a nonce above the last accepted one.

Layers

  • @real-wagmi/equilibra-limit-orders — the order model side. The settlement's EIP-712 domain and Order struct; the conversions (parseHumanPrice, minAmountOutForPrice, limitToSqrtTarget, minimumOrderAmount, expectedMakerOut); buildLimitOrder, validateOrder / assertValidOrder, buildOrderDomain, signOrder, orderDigest, toWireOrder / fromWireOrder; quoteOrderFillability over an @real-wagmi/equilibra-sdk Pool and clearsExecutionBuffer; readSettlementPolicy over a structural ReadContractClient (a viem PublicClient as is).
  • @real-wagmi/equilibra-limit-orders/api — the node side. LimitOrdersApiClient with { endpoint, settlement, requester? }; health / assertIdentity (+ nodeOrderPolicy), orderMinimum, authPubkey, login, postOrder, listOrders, listAllOrders, getOrder, cancelOrder; ApiError and isApiError; the OrderStatus and API_ERROR_CODES wire values. The standalone functions behind the methods are exported too, for hosts that bring their own transport — fetchHealth, assertNodeIdentity, fetchOrderMinimum, fetchAuthPubkey, login, postOrder, listOrders, listAllOrders, getOrder, cancelOrder — all except assertNodeIdentity take a requester and a base URL (and the API domain where a signature is made); assertNodeIdentity is pure and takes the health and the expected identity. Note that the standalone listOrders has no status default; the OPEN default is the client method's.
  • Seams are structural, never wrapper classes: a signer is anything with signTypedData, a requester is the subset of fetch the client calls, a read client is anything with readContract.

Errors

  • A non-2xx answer from the node throws ApiError with status and code as fields — classify with isApiError(error) and branch on the fields, never on the message. code is one of the API's documented codes or '' for a body that was not the node's (an HTML 502, an unknown code from a newer node — the raw text stays in the message).
  • A 2xx body off the schema throws an invariant naming the field (HEALTH_SETTLEMENT, ORDER_MINIMUM_HUB_TOKEN, …); so does a bad argument before any request (TOKEN_IN, DIGEST, NONCE) — a malformed digest would otherwise earn a misleading 404 unknown_order, and a cancel would cost a wallet prompt.
  • validateOrder codes: the node's zero_amount, identical_tokens, invalid_receiver, salt_overflow, deadline_passed, deadline_too_soon, deadline_too_far, plus the client-only amount_range, deadline_range, salt_range, zero_address.

Notes

  • Amounts are raw on-chain units (bigint); Token, CurrencyAmount, Price and the chain tokens come from @real-wagmi/v2-sdk. A CurrencyAmount can carry a fraction; every function that feeds a signed field refuses one — normalise with CurrencyAmount.fromRawAmount(currency, amount.quotient) first.
  • No URL, address or node policy lives in the package: the endpoint, the settlement and the router come from the consumer; the deadline window and the keeper's execution buffer from /health (nodeOrderPolicy(health), health.executionBufferBps); the platform share from the chain — so a redeploy or a policy change is never a package release.
  • The platform keeps 80 % of any surplus above the maker's floor (platformShareBps, read from the contract); the maker always receives at least the floor.
  • orderDigest is pinned to the deployed settlement's hashOrder; loginDigest and softCancelDigest replay vectors/login.json and vectors/softcancel.json, and limitToSqrtTarget and the price rule the other three vector files, case for case — all exported by the node's own Rust reference. The vectors live in the submodule and are needed only to run this package's tests.