02DarkBook documentation
How it works
Follow one order from the moment it is signed to the moment a position exists. Six stages, three of them on Solana and one on a rollup, with the exact size sealed for the first five.
The shape is commit and reveal. A trader publishes a hash of the size at placement and the plaintext at settlement, and the program checks that the second is what the first promised. That construction is old, and its known cost is the delay between the two phases. DarkBook absorbs the delay by matching on a MagicBlock Ephemeral Rollup, where the book turns over faster than a base-layer slot, so the reveal is the settlement step rather than a round trip a trader has to wait through before knowing they filled.
- 1
deposit_collateralSolana devnetcalled by the traderUSDC lands in the market vault
Collateral is held per market in a
UserAccountwith a locked and an unlocked balance. Nothing about the order exists yet.- Becomes public
- deposit amount, trader pubkey
- 2
place_orderSolana devnetcalled by the traderThe order goes on the book carrying a hash, not a quantity
The collateral requirement is computed from the band ceiling, not from the real size, because the ceiling is the largest size the order is allowed to reveal later. A Whale-band order has to be backed by free collateral for 32,768 lots whether the sealed number is 32,768 or 3. That is what makes over-declaring expensive rather than free.
- Becomes public
- trader, side, price_ticks, size_band, leverage_bps, commitment, placed_slot
- Still sealed
- size_lots, salt
- 3
delegate_bookSolana devnetcalled by the market operator, once per marketThe book account moves to the rollup
The
OrderBookPDA is delegated to a MagicBlock validator with a 1000 ms commit frequency. From here the rollup, not Solana, is the machine mutating the book, which is where the residual trust in this design lives.- Becomes public
- is_delegated, the validator identity
- 4
match_ordersMagicBlock Ephemeral Rollupcalled by anyone, permissionless crankBids cross asks at the maker's price
The engine pops the best bid and the best ask while the bid is at or above the ask, executing at the resting ask price, with the crossing bid recorded as the taker. A pair with the same trader on both sides is re-inserted and the loop stops rather than self-matching. Up to 32 fills are produced per call and the book holds 256 unclaimed fills at once.
No quantity is decided here. A fill says these two traders crossed at this price, and each side is bounded by its own declared ceiling. The matched amount is fixed at settlement, when both plaintexts are revealed and required to agree.
- Becomes public
- fill_id, taker, maker, price_ticks, taker_size_band, maker_size_band, matched_slot
- Still sealed
- both legs' size_lots, both salts
- 5
commit_bookMagicBlock Ephemeral Rollupcalled by the market operatorRollup state is committed back to Solana
Every fill becomes readable on Solana here, both bands included, before any claim. A fill-tape observer at this point knows who traded with whom, at what price, and the ceiling each side declared.
- Becomes public
- the committed OrderBook account, fills included
- Still sealed
- size_lots, salt
- 6
claim_fillSolana devnetcalled by a settler holding both plaintextsSizes are revealed, verified, and two positions open
The settler passes salt, size, leverage and the stored commitment for each leg. The program bounds each revealed size by that leg's own band, recomputes both hashes, requires the two revealed sizes to be equal, marks the fill claimed so it cannot be replayed, and creates a long
Positionfor the taker and a short for the maker. From this point the size is public and stays public.- Becomes public
- size_lots for both legs, entry price, collateral locked, open interest
What the program actually checks at reveal
Four independent conditions have to hold before a position exists. Any one of them failing reverts the whole claim, so a settler cannot open a position on a size the trader never committed to.
// programs/darkbook/src/ix/settlement.rs, claim_fill
// 1. each leg is bounded by its own published ceiling
let taker_band_max = SizeBand::max_lots_for_code(fill.taker_size_band)
.ok_or(DarkbookError::InvalidSizeBand)?;
let maker_band_max = SizeBand::max_lots_for_code(fill.maker_size_band)
.ok_or(DarkbookError::InvalidSizeBand)?;
require!(taker_size <= taker_band_max, DarkbookError::SizeExceedsBand);
require!(maker_size <= maker_band_max, DarkbookError::SizeExceedsBand);
// 2. the revealed plaintext hashes to the published commitment
let computed_taker =
compute_commitment(&taker_salt, taker_size, taker_leverage, &taker_pubkey);
require!(computed_taker == taker_commitment, DarkbookError::CommitmentMismatch);
// 3. one fill is one matched quantity, so the legs must agree
require!(taker_size == maker_size, DarkbookError::CommitmentMismatch);
// 4. the fill is consumed, so the reveal cannot be replayed
book.fills[idx].claimed = 1;
Who can see what, and when
The interesting column is the last one. At no stage before settlement does any party outside the trader learn the exact quantity, except the settler, which holds the plaintext by construction.
| Stage | Machine | Exact size known to |
|---|---|---|
place_order | Solana | trader only |
| book delegated | Solana to rollup | trader only |
match_orders | Rollup | trader only |
commit_book | Solana | trader only |
claim_fill submitted | Solana | trader and settler |
| position open | Solana | everyone |
The rollup validator holds the full delegated OrderBook account and still cannot read a size, because the account never carries one. It carries a hash.
Matching rules in full
- Price-time priority. Each price level holds up to four orders in arrival order, 256 levels per side.
- A fill executes at the resting order's price. The aggressor that crossed the spread is recorded as the taker and opens the long leg at settlement.
- Self-match is refused. If the best bid and best ask belong to the same trader, both are re-inserted and matching stops for that call.
match_ordersproduces at most 32 fills per call to stay inside the compute budget, and the book stores at most 256 fills waiting to be claimed.- The
FillRecordedevent carries one band, the taker's. The maker's band is on theFillaccount, and that is the field settlement enforces.
Next: which of these fields is public, and exactly when it stops being private. Read the privacy model.