# OrderBook Contract

`contracts/OrderBook.sol` is the canonical on-chain matching engine for dBook. It handles ERC-20 pairs, integrates directly with `Manager`, and keeps the runtime under the 24 KB limit. The 2025-08 refresh adds a configurable queue unit size so makers and takers stay aligned on base-token granularity.

The design focuses on predictable gas, deterministic data structures, and explicit hooks that the Manager drives. For a tour of measured gas costs (placements, taker sweeps, cancellations, claims, withdrawals), see the [Gas Profile Snapshot](https://github.com/jkjx/dbook-gitbook/blob/main/technical-documentation/orderbook-contract/technical-documentation/orderbook-contract/gas-profile.md).

## Constructor & Immutable Context

```solidity
constructor(address manager, address baseToken, address quoteToken, uint256 tickSize, uint256 queueUnitSize)
```

* `manager` must implement `IManager`. The address is immutable and used for all role checks.
* `baseToken` / `quoteToken` are standard ERC-20s. Decimals are cached so math can align around a shared 36-decimal grid.
* `tickSize` (must be > 0) establishes the price grid. Prices are expressed in `PRICE_PRECISION` (1e18) and must be multiples of `tickSize`.
* `queueUnitSize` (defaults to 1 when passed as 0) enforces base-amount granularity. It must divide `BASE_SCALE`; taker fills are rounded down to this unit and any resting remainder must be a clean multiple.
* `minQuoteAmount` is fetched from the Manager’s per-quote map when the book is deployed so dust rules mirror governance settings; token whitelisting remains a Manager-side registration gate.
* The constructor seeds skip-list sentinels, primes the cached taker-fee rate via `manager.getTakerFeeRate(address(this))`, and reverts if the returned value is out of range.

## Storage Layout Highlights

* **Order records** – packed into three slots: trader + price, amount + claimRangeStart, claimRangeEnd + metadata. Status lives in slot 2 to avoid extra lookups.
* **Skip lists** – deterministic, nine-level skip lists maintain the active price levels per side. `_getDistributedLevel` hashes the price to choose a level, while `_nextForcedLevel` ensures periodic higher-tier nodes even under adversarial pricing. A level only stays active if its volume is at least `_minExecutableBase(price)`, preventing dust levels that cannot satisfy at least one quote unit at that price.
* **Fenwick trees** – per-price Fenwick trees track cancel deltas so claim accounting stays O(log n) without re-traversing the queue.
* **Price-level data** – `buyPriceData` / `sellPriceData` pack volume and cumulative claimable totals into one slot, keeping per-level updates constant-cost.
* **User balances** – `withdrawableBase` and `withdrawableQuote` record deferred settlement and cancellation refunds. `_withdrawTokens` flushes both balances in one go.
* **Cached configuration** – `takerFeeRateCached`, `tickSize`, `minQuoteAmount`, `QUEUE_UNIT_SIZE`, and the best bid/ask pointers minimize redundant reads.

## Limit Orders

```solidity
function createBuyOrder(uint256 baseAmount, uint256 price, bool postOnly, ExecutionType executionType, bool withdrawTokensAfter)
function createSellOrder(uint256 baseAmount, uint256 price, bool postOnly, ExecutionType executionType, bool withdrawTokensAfter)
```

* `ExecutionType` supports `Standard`, `IOC`, and `FOK`. IOC/FOK semantics are enforced before any maker state is written.
* Any resting remainder must be a multiple of `QUEUE_UNIT_SIZE`; taker fills are rounded down to the nearest unit during matching.
* `postOnly` forces the order to rest; the call reverts if an immediate match would occur.
* Orders that remain on the book inherit queue positions without walking linked lists. Any portion that filled during placement emits `OrderReduced`. Buys that partially fill at a better price now refund any surplus quote immediately before resting the remainder.
* Passing `withdrawTokensAfter = true` triggers a final `_withdrawTokens` so taker fills and refunds hit the wallet immediately.

`cancelAndOrCreate(cancelIds, createParams, withdrawAfter)` is the maker-facing utility for atomic cancel/re-place flows. It accepts empty arrays (cancel-only or create-only), accumulates refunds, and optionally withdraws at the end.

### Order Maintenance

* `decreaseOrderSize(orderId, reduceAmount)` and `decreaseOrderSizes(OrderSizeEdit[])` are available when you deploy the optional `OrderBookExtended` wrapper; the lean core omits them to stay within the bytecode budget and for chains with <24 KB limits. Increases are intentionally unsupported on-chain.
* Native cancel / claim utilities remain callable while the book is paused so makers can manage exposure during incidents.

## Market Trades

```solidity
function marketBuy(uint256 maxQuoteIn, uint256 minBaseOut)
function marketSell(uint256 baseAmount, uint256 minQuoteOut)
```

* The engine walks the active price list (`nextActive*` pointers), consuming price levels via `_matchAtPriceLevel`. Gas per level stays flat regardless of maker count.
* Both functions settle immediately: collateral is transferred, fees are applied, balances are withdrawn, and the appropriate event (`MarketBuyExecuted` / `MarketSellExecuted`) is emitted.
* Slippage guards (`maxQuoteIn`, `minBaseOut`, `minQuoteOut`) trigger the standardized `OrderBook__SlippageExceeded` error when violated; `minBaseOut` and `minQuoteOut` are enforced after taker fees (net fill).

## Claiming & Withdrawals

* `claimFilledOrders(uint64[] orderIds, bool withdrawAfter)` moves fills from the packed claim range into withdrawable balances. When `withdrawAfter` is true, it immediately flushes those balances to the caller and returns the withdrawn totals.
* `withdrawTokens()` transfers any outstanding withdrawable base/quote to the caller. This also handles refunds from cancellations and IOC remainders (plus price-improvement refunds on taker buys).
* Makers incur zero fees. The helper structs `_computeBuyClaim` / `_computeSellClaim` simply translate pending fills into withdrawable units.
* Claims move matched proceeds into an internal ledger; withdrawals move that ledger to the wallet. Takers typically use `withdrawAfter` in one call, while makers claim first (optionally in batch) and withdraw later or batch with other maintenance calls.
* Front-end flows abstract claims: a user “withdrawal” in the UI performs claim + withdrawal under the hood. Market makers or programmatic integrators interacting directly with the contract still need to call `claimFilledOrders` (or use `withdrawAfter`) before `withdrawTokens` when bypassing the UI.

## Fees

* `takerFeeRateCached` holds the latest rate pulled from Manager. Anytime governance updates a market, Manager calls `syncSettings(4, 0, 0, newTakerFeeRate)` so the cache refreshes before the next taker match. Calls from other addresses revert.
* `claimFees()` transfers `pendingBaseFees` and `pendingQuoteFees` to the Manager’s `feeCollector`. The function attempts both tokens independently and emits `FeesClaimed` with success flags so operators can detect stuck transfers.
* Pending fee totals surface through `getParams()` (see below) rather than a dedicated getter.

## Administrative Hooks

* `pause()` / `unpause()` – gated by the Manager’s `PAUSER_ROLE`. Pausing blocks new matches and placements; maintenance utilities (cancel, claim, withdraw) remain available.
* `syncSettings(uint8 mask, uint256 newTickSize, uint256 newMinQuote, uint256 newTakerFeeRate)` – only the Manager may call this. Bit `0` flips the tick size, bit `1` sets `minQuoteAmount`, and bit `2` refreshes the cached taker fee. There are no standalone `setTickSize` / `setMinQuoteAmount` setters anymore, so all parameter updates flow through this hook.

## View & Preview Helpers

* `getParams()` returns a snapshot struct with token metadata, best bid/ask, tick size, min quote, cached fee, pending fees, scale constants, and skip-list heights. Ideal for read-heavy front ends.
* `previewTakerOrder(uint256 amount, bool isBuy, PreviewTarget target)` simulates market consumption against the current book without mutating state.
* `getOrderBookSnapshotPaged(uint256 depth, uint256 buyOffset, uint256 sellOffset)` exposes batched level data for UI depth charts.
* `getUserOrderIdsPaged(address user, uint64 startAfter, uint256 limit)` iterates over the bitmap history the engine keeps per trader and returns `(orderIds, nextStartAfter)` for pagination that skips empty chunks.
* `getPriceLevelData(bool isBuy, uint256 price)` returns the packed `{volume, cumulativeClaimable}` pair for analytics or off-chain settlement reconciliation.
* `calculateQuoteNeeded(uint256 baseAmount, uint256 price)` exposes the canonical conversion formula used everywhere else.

## Event Surface

```solidity
event OrderCreated(uint64 indexed orderId, address indexed trader, bool indexed isBuyOrder, uint256 price, uint256 amount, uint64 timestamp);
event OrderReduced(uint64 indexed orderId, address indexed trader, bool indexed isBuyOrder, uint256 price, uint256 amountReduced, uint256 newRemainingAmount);
event OrderCancelled(uint64 indexed orderId, address indexed trader, bool indexed isBuyOrder, uint256 price, uint256 remainingAmount);
event OrderSizeDecreased(uint64 indexed orderId, address indexed trader, uint256 reduceAmount, uint256 newTotalSize);

event MarketBuyExecuted(address indexed taker, uint256 baseFilled, uint256 quoteUsed, uint256 takerFee);
event MarketSellExecuted(address indexed taker, uint256 baseFilled, uint256 quoteFilled, uint256 takerFee);
event ConditionalOrderExecuted(address indexed trader, bool indexed isBuy, uint8 executionType, uint256 price, uint256 baseAmount, uint256 baseFilled, uint256 quoteUsed, uint256 refundAmount, uint256 takerFee);
event PriceLevelTraded(address indexed trader, bool indexed isBuy, uint256 indexed price, uint256 baseAmount, uint256 quoteAmount, uint256 timestamp);

event MakerOrderClaimed(uint64 indexed orderId, address indexed trader, uint256 baseClaimed, uint256 quoteClaimed);
event TokensClaimed(address indexed user, uint256 baseAmount, uint256 quoteAmount);
event FeesClaimed(address indexed collector, uint256 baseAttempted, uint256 quoteAttempted, bool baseSuccess, bool quoteSuccess);

event Paused(address indexed pauser);
event Unpaused(address indexed pauser);
event MinQuoteAmountUpdated(uint256 oldMinQuoteAmount, uint256 newMinQuoteAmount);
event TickSizeUpdated(uint256 oldTickSize, uint256 newTickSize);
```

> `OrderSizeDecreased` is emitted only when the `OrderBookExtended` wrapper is deployed. The per-maker `Trade` event lives in `IOrderBook` for ABI compatibility but is not emitted by the canonical implementation. Indexers should subscribe to `PriceLevelTraded` for aggregated fills and the various lifecycle events for state sync.

## Integration Checklist

1. **Registration** – Manager passes the constructor args, then immediately calls `syncSettings(4, 0, 0, takerFee)` during registration so the cached fee matches governance state. Both base and quote tokens must be whitelisted in Manager or registration will revert. No factory wrappers are required anymore.
2. **Placing orders** – SDKs should:
   * Validate ticks (`price % tickSize == 0`).
   * Enforce base amounts that are multiples of `QUEUE_UNIT_SIZE` and round simulations accordingly.
   * Provide the correct execution type to mirror existing UX (`IOC`/`FOK`); both execution shortcuts emit `ConditionalOrderExecuted`.
   * Optionally set `withdrawTokensAfter = true` for taker-centric flows.
3. **Monitoring** – Subscribe to lifecycle events and `PriceLevelTraded`. Use `getParams` and `getOrderBookSnapshotPaged` for polling dashboards.
4. **Maker tooling** – Prefer `cancelAndOrCreate` for atomic quote updates and `decreaseOrderSize` for trims that preserve FIFO.
5. **Fees** – Query the Manager for live rates. If `FeesClaimed` reports a failed token transfer, operators should investigate token allowances or pause the market.

## Not Present

* Per-user STP toggles and maker fee machinery are absent in the current implementation.
* Transient storage accumulators were replaced with in-memory `MatchAccumulator` structs to stay compatible with Istanbul gas rules.

The new contract keeps the external surface familiar while delivering deterministic gas, simpler maintenance, and tighter coordination with the Manager contract.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://dbookio.gitbook.io/dbook/technical-documentation/orderbook-contract.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
