Documentation
How LightBlue works
Orders go in sealed, open together in a reveal window, and cross at one Uniswap pool TWAP in one batch on Robinhood Chain.
Overview
LightBlue is a batch exchange. Instead of a continuous order book, trading happens in discrete batches per market. Each batch has a commit window, a reveal window and a settle window. Every eligible order in a batch crosses at one reference price: the time-weighted average price of one verified Uniswap v3 pool over a window that ended at the batch's commit cutoff. Orders are hidden during the commit window because only a hash of them is submitted.
- Markets pair one base asset (a Robinhood Stock Token) with the quote asset (USDG, 6 decimals). Each market names its reference pool, its price source and its terms.
- Balances are held by the exchange contract: deposit first, trade from the available balance, withdraw whatever is not reserved.
- Prices are integers: quote units per whole base token (for USDG: 6 decimals, so 180.25 USDG = 180250000). Quantities are base units (18 decimals). Nothing is a float.
Batch lifecycle
A batch is opened by anyone (openBatch(marketId)) when the market has no open batch. Its schedule starts at the opening block's timestamp t0, with the market's durations copied into the batch so a later change of terms cannot touch it:
| Phase | Condition (block timestamp t) | Allowed |
|---|---|---|
| COMMIT | t0 ≤ t < commitEnd, commitEnd = t0 + commitDuration | commit(batchId, digest) |
| REVEAL | commitEnd ≤ t < revealEnd, revealEnd = commitEnd + revealDuration | reveal(batchId, side, quantity, limit, nonce, salt) |
| SETTLE | revealEnd ≤ t < settleDeadline, settleDeadline = revealEnd + settleGrace | settle(batchId) by anyone |
| EXPIRED | settleDeadline ≤ t and not settled | expire(batchId) by anyone → ABORTED |
| FINALIZED / ABORTED | terminal, written by settle / expire | nothing |
Commitments are bounded to 2,048 per batch and reveals to 128, so settlement gas is bounded. A reveal that cannot be funded fails with InsufficientAvailable and can be retried inside the window after a deposit; several commitments from one wallet are reserved in the order they are revealed.
Reference price: a Uniswap v3 pool TWAP, not a bid/ask midpoint
The price source contract asks the market's pool for its oracle cumulatives at windowEnd = commitEnd and windowStart = commitEnd − twapWindow (via observe) and computes the arithmetic mean tick over that window. The tick is converted to "quote units per whole base token" with the exact math of Uniswap's OracleLibrary.getQuoteAtTick, oriented by which token is token0. Because the window ends at the commit cutoff, nothing revealed afterwards can influence which prices are averaged; because it is time-weighted, moving it requires holding the pool off-price for a large part of the window.
The batch is aborted and every reservation released when any of these holds at settlement:
- the pool's observation buffer does not reach back to the window start (
WINDOW_NOT_COVERED), the pool did not answer or returned zero (SOURCE_ERROR), or it has no in-range liquidity (NO_LIQUIDITY); - current in-range liquidity or the harmonic-mean liquidity over the window is below the market's
minLiquidity(LOW_LIQUIDITY); - the pool's spot price at the settlement block deviates from the TWAP by more than
maxSpotDeviationBps(SPOT_DEVIATION); - a Chainlink feed is configured and is fresh (≤
maxFeedAge) but deviates from the TWAP by more thanmaxFeedDeviationBps(FEED_DEVIATION); or the feed is stale and the market requires a fresh feed (FEED_STALE). A stale feed on a market that does not require one is recorded as "skipped".
There is no operator-entered price and no fallback source. The reference details (window, mean tick, liquidity, spot, feed price and age, block) are stored on chain per batch and shown on every receipt.
Matching & settlement
With reference P: buys with limit ≥ P and sells with limit ≤ P are eligible. V = min(eligible buy quantity, eligible sell quantity). The side with more quantity is pro-rated: each order gets floor(q · V / total); the residual base units (fewer than the number of pro-rated orders) are handed out one unit at a time in reveal order to orders that still have room. The smaller side fills completely. Every fill executes at P: a buyer pays ceil(fill · P / 10^baseDecimals) plus the fee, a seller receives floor(fill · P / 10^baseDecimals) minus the fee. The rounding difference (at most one quote unit per filled order) stays with the protocol so the books balance exactly. Unfilled quantity and unused collateral are released to the available balance; nothing rolls into the next batch and nothing is routed to a pool. The Solidity library and its TypeScript twin are replayed against the same vectors in the test suite.
Privacy model
Hidden during the commit window: side, quantity, limit price, nonce and salt. The commitment is an EIP-712 digest over (marketId, batchId, owner, side, quantity, limitPrice, nonce, salt) under the domain (LightBlue, 1, chainId, exchange address), so it cannot be replayed in another batch, on another contract or another chain, and it cannot be revealed by another wallet. The salt is 32 random bytes from the browser's CSPRNG. Order plaintext and salts live only in the owner's browser (and in the recovery package the owner may download); the server, the indexer and analytics never receive them.
Always public: wallet addresses, deposits and withdrawals (amounts and timing), the fact and time of each commitment, every reveal from the moment it is sent, fills and settlement. Funding a buy of a particular size right before a batch can therefore leak information.
Honest limits: ordinary commit–reveal does not open all orders simultaneously: orders become public one reveal at a time during the reveal window, and a participant may strategically withhold a reveal after seeing others (nothing is reserved at commit time, so withholding costs nothing). A lost salt or a missed reveal window means the order cannot participate. LightBlue is not a shielded wallet and not an anonymous exchange. A verified threshold-encryption or MPC opening, which would open every order at once and remove the withholding option, is a separately scoped enhancement and is not implemented.
Recovery & failure modes
- Refresh / new device: pending orders are stored per chain, exchange and wallet in the browser and reappear after a refresh. The recovery package (JSON download on the Orders page) carries them to another device; it is sensitive and labelled as such.
- Rejected or failed commit: the order stays local with status Failed and can be deleted; nothing is on chain.
- Missed reveal: nothing was reserved; the order shows as Expired and has no effect on the batch.
- Unfunded reveal: the transaction reverts with the exact shortfall; deposit and retry inside the window.
- No valid price / nobody settles: settle aborts the batch, or anyone expires it after the settle deadline. Either way every reservation goes back to its owner's available balance.
- Pause: while paused no commit, reveal, deposit, open or settle is possible, but withdrawals of available balances and
expirekeep working, so funds are never trapped. - Replaced / dropped transactions: the UI tracks the hash it submitted, reports replacement and re-reads chain state; the chain is the authority for every status.
Fees & balances
A market's feeBps (at most 100 bps) is charged in the quote asset on both sides of every fill, on the executed notional, rounded down. Buys reserve ceil(quantity · limit / 10^baseDecimals) + fee(at limit) in quote at reveal; sells reserve the quantity in base. Deposits reject fee-on-transfer tokens by checking the received amount; rebasing tokens are not supported (Robinhood Stock Tokens and USDG do not rebase). Network gas is paid by the wallet separately and is shown apart from protocol fees.
Trust & administration
The owner (an EOA locally; a multisig is the intended production owner) can list markets, update terms for future batches, activate/deactivate markets, allow price sources, pause and collect accrued protocol fees to the treasury. The owner cannot move user balances, change an open batch's terms, pick a price or settle selectively. Settlement and expiry are permissionless. The contracts are not upgradeable. The implementation is unaudited and must not be presented as ready for unrestricted customer funds.
Status & verified integrations
- Network
- Robinhood Chain · chain 4663
- Contracts
- deployed · 0x7ddD…b917
- Uniswap v3 factory
- 0x1f7d…2EfA
- $LIGHTBLUE
- 0x6A18…C3b2 · verified on chain 2026-09-29
- Registry verified
- 2026-09-29 · block 75531248
- Markets verified
- 10 of 10
Verified integrations on Robinhood Chain (chain 4663): the official Uniswap v3 factory, position manager and quoter from Uniswap's deployment registry (Sourcify matches); USDG; ten Robinhood Stock Tokens checked against the issuer's beacon and registry; their Chainlink feeds; and for each market the deepest USDG pool, its liquidity, observation buffer and whether a 30-minute TWAP is observable. Unresolved external dependencies: a deployment of the LightBlue contracts by an authorised owner, a hosted PostgreSQL and a persistent worker host for the keeper, a production RPC provider, an owner multisig, and a security audit. See docs/SETUP.md and the markets page.