darkbookDocs

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.

Read fromix/orders.rsix/matching.rsmatching_engine.rsix/settlement.rs

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. 1
    deposit_collateralSolana devnetcalled by the trader

    USDC lands in the market vault

    Collateral is held per market in a UserAccount with a locked and an unlocked balance. Nothing about the order exists yet.

    Becomes public
    deposit amount, trader pubkey
  2. 2
    place_orderSolana devnetcalled by the trader

    The 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. 3
    delegate_bookSolana devnetcalled by the market operator, once per market

    The book account moves to the rollup

    The OrderBook PDA 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. 4
    match_ordersMagicBlock Ephemeral Rollupcalled by anyone, permissionless crank

    Bids 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. 5
    commit_bookMagicBlock Ephemeral Rollupcalled by the market operator

    Rollup 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. 6
    claim_fillSolana devnetcalled by a settler holding both plaintexts

    Sizes 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 Position for 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.

rust
// 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.

StageMachineExact size known to
place_orderSolanatrader only
book delegatedSolana to rolluptrader only
match_ordersRolluptrader only
commit_bookSolanatrader only
claim_fill submittedSolanatrader and settler
position openSolanaeveryone

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_orders produces 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 FillRecorded event carries one band, the taker's. The maker's band is on the Fill account, 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.