Skip to content

API ReferenceSecurity

Reference

Security

Trust model

PartyCanCannot
The APIPropose a route and a minOutTake funds. The user signs tx, and the contract enforces minOut and recipient. A malicious or buggy API could propose a bad route, but not one below the minOut the user signs
Executor ownerChange the fee (≤ 1%) and recipient, the WHYPE allow-list; pause swapsMove user funds (the Executor holds none), raise the fee above 1%, block cancels
OrderBook ownerChoose keepersTouch escrow; stop owners from cancelling
KeeperChoose the route of a due sliceFill below the order's floor, send output elsewhere than the owner, fill early or after expiry
FeeCollector keeperConvert collected fees into the target tokenSend them anywhere but the treasury
A pool (any, including malicious)Execute the swap it was asked to doGet paid in another token, get paid more than the hop's input, call back outside its own swap

The owner is an EOA by choice (no multisig). Ownership transfers are two-step.

Contract protections

  • Callback authentication with transient storage: only the pool being called can trigger payment; the token and the maximum amount are fixed before the call; the pool-relayed data is never trusted for payments; the v4 pool key is pinned. See Executor → Callback security.
  • Balance-delta accounting everywhere: fee-on-transfer tokens can't make the Executor over-credit itself.
  • minOut on what the recipient actually receives.
  • Reentrancy guard on every entry point; checks-effects-interactions in the OrderBook.
  • Zero balance between transactions: fees forwarded in the same transaction; receive() restricted.
  • Emergency stop: setPaused(true) blocks swaps and fills, never cancels or quotes.

API protections

  • Input validation at the boundary (addresses, integers, ranges).
  • Internal errors are never returned (they could contain the RPC URL or keys); logged with the request id.
  • No RPC URL from the server's environment is exposed; /config returns the network file's public RPC.
  • Rate limiting per IP, per partner key, shared through Redis.
  • The API serves no web page (the front is a separate project); its responses carry x-content-type-options: nosniff.
  • Every transaction is simulated before it's returned.
  • Fee conversion is protected against price manipulation by an EMA guard.

Internal audit

An independent read-only review of the contracts and the API, with each fix covered by a regression test:

SeverityFindingFix
CriticalOrderBook.fill was open to anyone, and the UI created DCAs with minOut = 0: a fake "pool" could take a whole slicecreate rejects a zero floor; the API requires minOut; the UI sets a DCA floor
HighWhoever filled kept the surplus above the floor, so a DCA always executed at its floorFills reserved to allow-listed keepers, plus a keeper fillerMinOut; the contract enforces max(floor, fillerMinOut). Our keeper uses 99.5% of its quote
MediumunlockCallback was unauthenticated for unknown modesEvery non-quote mode requires the active PoolManager
LowCallbacks paid the token and amount relayed by the poolExpected token, max amount and v4 key in transient storage
LowminOut was checked before the tax of a taxed tokenOutChecked on the recipient's actual balance change
LowRefunds could get stuck (owner can't receive HYPE, or is blacklisted)cancelTo(id, to)
LowOwner powersTwo-step ownership; feeRecipient can't be 0 or the Executor
InfoA taxed leg could round to 0; the probe failed on USDT-style tokensZero legs skipped; low-level calls in the probe

Automated reviews of later commits, also fixed:

AreaFindingFix
API keysAn unknown key got its 401 before the rate limiter ran, so keys could be guessed without limitThe key check runs after the limiter: wrong keys count against the caller's IP limit. Keys must be ≥ 24 characters (the API won't start otherwise)
AdaptersA pool or factory could report a fee outside [0, 100%); the stable-curve solver had no step cap; Solidly factories were auto-indexed without a back-pointer checkSuch fees price the pool at zero; the Newton solve stops at 64 steps; a Solidly factory is only indexed when its sample pool's factory() points back to it
SDKtoken(address) put an unchecked value in the URL path (../orders…)Rejects anything but a 20-byte hex address before any request (pool ids too)
Event storeThe quote-savings cache's size cap only evicted old entriesHard cap, oldest first
Data routesPrice cache keyed by request input; /candles accepted any addressSize-capped caches; unknown tokens → 404
List paramslimit=-5 became LIMIT -5 in SQL (no limit)Every limit/since/offset validated as a bounded integer
PnLThe equity curve priced every day × token (pathfinding each time)One price query per token, 180-day window, 30 s cache per wallet
Self-serve keysFree wallets → unlimited keys, each with its own bucket (Sybil)Self-serve keys start at the public tier and share their IP's bucket; only operator-raised keys (explicit upgraded flag) get their own; 5 creations per IP per day
LiquidityA contract that calls a pool's mint and answers factory() with the real factory would have been treated as a position manager, so users would approve tokens to itOnly managers configured in the network file are used; discovery only suggests candidates
LiquidityA remove on a pool not yet indexed was built with zero minimumsRefused (409) until the pool's price is known
Behind RailwayEvery request had the proxy's IP, so all users shared one per-IP bucketTRUST_PROXY=<hops> (a hop count, so a spoofed X-Forwarded-For doesn't help)

An earlier review fixed: XSS in the UI token list, a CDN-hosted script, error messages leaking upstream details, and fee conversions vulnerable to sandwiching.

Tests

  • Fork tests on real Elysium testnet state (swaps across every venue type, orders, Permit2, fee-on-transfer, launchpad).
  • Fuzz tests: exact quote, exact fee floor(gross × bps / 10,000), minOut enforced, native round trip, taxed input across splits, every callback mode from a stranger.
  • Invariant campaigns (256 runs × 500 calls): the Executor always ends at zero (tokens and HYPE); each swap equals its quote; the fee is exact; the OrderBook's escrow equals the sum of open orders.
  • Hostile mock venues: greedy pool, forged callback, partial fill.

Not done

  • External audit. Commission one before significant volume.
  • Multisig / timelock on owner functions (declined by the operator).
  • On-chain oracle for keepers: the price guard lives in the API.

Reporting a vulnerability

Contact the operator privately before disclosing. Include the request id or transaction hash and the steps to reproduce.

lane.markets · Make the most of every move.