Documentation

How Lunar works

Every contract the interface touches, how a quote is produced, what travels inside the calldata, and the API that serves the same numbers the site displays.

Overview

Lunar is an interface onto liquidity that already exists on Robinhood Chain. It does two things: it turns a liquidity position into a claim you can move, and it routes trades in crypto and tokenized equities through one execution path with a price floor enforced at every hop.

Nothing on the site is a stored number. Prices, pool state, balances, routes and yields are read from the chain when you ask for them, through the same API the terminal uses. If a figure looks wrong, the explorer link next to it will tell you why.

The protocol is non custodial. Lunar never holds keys, never takes deposits and cannot move funds. The server builds calldata; your wallet decides whether it is ever signed.

Network and contracts

Everything settles on Robinhood Chain, an Arbitrum Orbit L2 that pays gas in ETH.

ItemValue
ChainRobinhood Chain
Chain id4663 (0x1237)
Gas tokenETH, 18 decimals
Block timeroughly 101 ms
RPChttps://rpc.mainnet.chain.robinhood.com
Explorerrobinhoodchain.blockscout.com

Contracts the interface reads and writes

ContractAddressUsed for
Uniswap v3 factory0x1f7d7550B1b028f7571E69A784071F0205FD2EfAPool discovery per fee tier
Universal Router0x8876789976dEcBfCbBbe364623C63652db8C0904Swap execution and hop price floors
Permit20x000000000022D473030F116dDEE9F6B43aC78BA3Scoped, expiring spending rights
Multicall30xcA11bde05977b3631167028862bE2a173976CA11Batching every chain read
WETH0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73Wrapping inside the router call
Chainlink ETH/USD0x78F3556b67E17Df817D51Ef5a990cDaF09E8d3A9Reference price
Chainlink USDG/USD0x61B7e5650328764B076A108EFF5fa7282a1B9aD2Dollar anchor for every quote

The live list, including the block the server last saw, is always at /api/network.

Listed assets

The registry ships with 32 assets: native ETH and wrapped ETH, the USDG quote asset, stablecoins and majors, the protocol's own VAI token, plus 17 Robinhood tokenized equities and ETFs including AAPL, NVDA, MSFT, TSLA, GOOGL, AMZN, META, PLTR, COIN, SPY and QQQ.

The list is a convenience, not a boundary. Paste any ERC-20 address into the token picker and the terminal reads symbol, name, decimals and totalSupply straight from the chain, then discovers its pools the same way it does for a listed asset.

Prices for tokenized equities carry two numbers: the pool price and, where a Chainlink feed exists, the reference price, with the premium between them shown next to both.

YieldShares

Providing concentrated liquidity earns fees, but the claim on those fees sits inside the position. It cannot be sent, collateralised or traded without unwinding the whole thing. A YieldShare separates the two: the same economic exposure, expressed as a fungible ERC-20.

What the share represents

  • Reserves. A proportional claim on both sides of the pool at the current tick, valued continuously from slot0 rather than from a stored snapshot.
  • Accrued fees. The pool tracks fee growth per unit of liquidity; the share's yield is that growth multiplied by the liquidity standing behind it.
  • Range management. Rebalancing lives in the vault, so holders keep a fungible token instead of a position NFT with its own tick bounds.
The vault contracts are not deployed yet. What the terminal shows today is the analytics layer over liquidity that already exists on chain, which is what the vaults will settle against.

How the APR is measured

No projection and no incentive assumptions. Every Uniswap v3 pool exposes two monotonically increasing counters, feeGrowthGlobal0X128 and feeGrowthGlobal1X128, holding fees earned per unit of liquidity in Q128 fixed point. Two samples separated by a known interval give the revenue for that interval:

fees = (feeGrowthNow - feeGrowthThen) × liquidity ÷ 2¹²⁸

Both token amounts are priced with the same pool derived map used everywhere else, summed, divided by the pool's total value and annualised by the length of the window. Because liquidity itself moves, the server accumulates each interval against the liquidity that actually stood behind it rather than assuming one figure held throughout.

The node prunes historical state after a few thousand blocks, so a full day of fee growth cannot be read in a single call. The server samples every listed pool on a timer and keeps up to 24 hours in data/fee-series.json. The measured span is printed next to every APR, because a five minute sample and a twelve hour sample deserve different amounts of trust.

Quoting and routing

A quote is a simulation, not an estimate. The engine loads slot0, liquidity and tickSpacing for a pool, then runs the pool's own swap loop off chain: compute a swap step toward the next initialised tick, cross the boundary, apply liquidityNet, repeat. Tick bitmaps and tick data are fetched lazily as the walk needs them, so a small trade costs one round trip and a large one costs a few.

This matters for size. A spot price estimate is accurate for dust and wrong for anything that moves the pool. Walking ticks returns the amount the contract will actually deliver, including the liquidity changes on the way.

Route selection

  • The direct pair at every fee tier: 0.01%, 0.05%, 0.30% and 1.00%.
  • Two hop routes through USDG.
  • Two hop routes through wrapped ETH.

Every candidate is simulated in full and the one with the highest output wins. The chosen path, its fee tiers, its pool addresses and the number of tick steps per hop are all shown on the quote.

Pricing

USDG anchors the dollar scale through its Chainlink feed. Every other asset is priced from its deepest pool, relaxing outwards from USDG so that assets quoted only against ETH still resolve. Where an asset also carries a reference feed, both numbers are displayed along with the premium between them, which is the honest way to show a tokenized equity.

Reads go through Multicall3, so a full market refresh across dozens of pools is one eth_call rather than a hundred round trips.

Approvals

Spending runs through Permit2 rather than an open ended ERC-20 allowance to a router. There are two scoped steps:

  1. The token approves Permit2 once.
  2. Permit2 grants the Universal Router a specific amount with a 30 day expiry.

The second can be revoked without touching the first, and it lapses on its own. The terminal checks both before offering a swap and asks only for the step that is missing.

Execution

A swap is one call on the Universal Router:

execute(bytes commands, bytes[] inputs, uint256 deadline)

The commands depend on what you are paying with and receiving:

TradeCommandsNotes
ERC-20 to ERC-20V3_SWAP_EXACT_INPaid from your wallet through Permit2
ETH to ERC-20WRAP_ETH + V3_SWAP_EXACT_INWrapping costs no extra transaction
ERC-20 to ETHV3_SWAP_EXACT_IN + UNWRAP_WETHUnwraps to you in the same call

Before the action unlocks, the assembled transaction is estimated against the live node with your address as sender. A revert is decoded and surfaced with its reason rather than swallowed, and the gas figure that comes back is the one attached to the transaction you sign.

After you sign, the terminal records the hash locally and polls for the receipt, moving the order from pending to filled or reverted on the Orders page.

The MEV guard

The Universal Router deployed on Robinhood Chain is a fork. Its V3_SWAP_EXACT_IN takes a sixth parameter the upstream contract does not have:

V3_SWAP_EXACT_IN(
  address recipient,
  uint256 amountIn,
  uint256 amountOutMin,
  bytes   path,
  bool    payerIsUser,
  uint256[] minHopPriceX36   // minimum price per hop, scaled by 1e36
)

An empty array disables the check. Lunar populates it from the simulated hop amounts and your slippage budget, so the floors travel inside the calldata you sign.

This is the meaningful protection on a multi hop route. Without it, a path can degrade badly at an intermediate pool and still clear a final minimum that was set for the whole route. With it, the trade reverts at the pool that moved. You can see the floors on every quote, and turn the guard off in Settings if you would rather only enforce the final amount.

The guard bounds price, not privacy. Transactions are public in the mempool like any other. It limits what a reorder can cost you; it does not hide the trade.

The terminal

Eight views, all reading the same live data.

ViewWhat it does
SwapRoute crypto and tokenized equities. Live balances, simulated quotes, approvals, gas estimate and the hop floors.
YieldSharesEvery listed pool with reserves, TVL and the fee yield measured from its own accumulators.
PortfolioNative ETH, every listed ERC-20 and anything else the explorer reports, priced and weighted.
Crypto MarketListed crypto assets priced from live pool state.
Stock MarketTokenized equities and ETFs, pool price beside the Chainlink reference.
OrdersEverything submitted from this browser, tracked to its receipt.
HistoryTransactions and token transfers for the connected address.
SettingsSlippage, deadline, MEV guard, refresh rate, wallet and network status.

Watching an address

Portfolio and History accept an address parameter, so any wallet can be inspected read only without connecting one:

/app/portfolio?address=0x…
/app/history?address=0x…

Deep links

The market tables link straight into a prepared swap:

/app/swap?in=USDG&out=NVDA

What is stored

Settings, order history and your wallet preference live in your browser's local storage. Nothing is uploaded, no account is created and no analytics are collected. The server logs API paths for operations and holds no personal data.

Public API

Every number on the site is available as JSON, with no key and no rate limit beyond the upstream RPC.

EndpointReturns
GET /api/networkChain head, gas price, contract addresses, sampler depth
GET /api/overviewEverything the landing page needs in one call
GET /api/tokensThe listed asset registry
GET /api/markets?kind=crypto|stockPriced market rows with oracle premium
GET /api/poolsPool state, reserves, TVL and measured APR
GET /api/protocolProtocol totals and the top pools
GET /api/pricesAddress to USD price map
GET /api/quoteRoute, amounts, calldata, approvals and gas
GET /api/portfolio/:addressHoldings and valuations
GET /api/activity/:addressTransactions and token transfers
GET /api/token/:addressResolve any ERC-20 from the chain
GET /api/tx/:hashReceipt status
GET /api/healthUptime and cache counters

Quote parameters

ParameterDefaultMeaning
in, outrequiredSymbol or address
amountrequiredDecimal amount of the input token
slippageBps50Sets the minimum output and the hop floors
accountnoneEnables approval checks and gas estimation
deadlineMinutes20How long the router will accept the swap
mevGuardtruefalse ships an empty hop floor array

Example

curl "http://localhost:4173/api/quote?in=ETH&out=AAPL&amount=0.5"

The response carries the full route, the amounts, the readable hop floors, the Universal Router calldata and, when an account is given, the approvals still required and the gas estimate.

Self hosting

The server has no runtime dependencies and no build step. Node 18 or newer is enough.

node server.js                 # serves on http://localhost:4173
node scripts/selftest.mjs      # end to end check against a running server
PORT=8080 node server.js       # different port

Configuration

VariableDefaultMeaning
LUNAR_RPC_URLSRobinhood mainnet RPCComma separated, used in failover order
LUNAR_EXPLORERBlockscoutExplorer base URL
LUNAR_CACHE_TTL12000Upstream cache lifetime in milliseconds
LUNAR_SAMPLE_MS60000Fee sampler interval
LUNAR_RPC_CONCURRENCY3Parallel RPC requests
PORT, HOST4173, 0.0.0.0Listener

Risks and limits

Stated plainly, because a trading interface earns trust by being specific.

  • Smart contract risk remains. Trades settle against Uniswap v3 pools and the Universal Router deployed on Robinhood Chain. Those are third party contracts.
  • Quotes are simulations, not guarantees. They match execution for the state read at quote time; the chain can move between the quote and the block your transaction lands in. That is what the minimum output and the hop floors are for.
  • The MEV guard is a price floor, not privacy. Transactions are public like any other.
  • Tokenized equities carry their own risk. The token tracks a reference price, but pool price and oracle price can diverge. That premium is displayed, never smoothed over.
  • Vault contracts are not live. YieldShare vaults are in progress; the analytics describe pools that already exist.
  • No audit to point at. There is no completed third party review of vault contracts, because those contracts are not deployed. When they are, this page will say who reviewed them.
  • Explorer dependency. Blockscout sits behind a bot check, so history and unlisted balances can fail independently of the RPC. Views that depend on it degrade rather than break.

Put your liquidity to work

Mint a YieldShare, add depth to a pool, or route a guarded trade in crypto or tokenised equities.

Open terminal