Tick Structure
Understand how Midnight tick values represent prices and fixed rates, and use the structure when building offers and market displays.
Discrete price grid
Midnight enforces a discrete price grid for offers. Offers can only be placed at prices corresponding to valid ticks. This prevents economically meaningless undercutting (offering at a price 1 wei better) and creates natural price levels around which liquidity concentrates.
The market state endpoint gives you tick_granularity , the practical constraint a maker needs before constructing an offer:
GET /v0/midnight/markets/{market-id}/state
// Check what tick values are valid for this market
GET /v0/midnight/markets/{marketId}/state
// In the response:
"tick_granularity": 1Valid ticks must be multiples of this value.
tick_granularity: 1 means every integer tick is validtick_granularity: 2 would mean only even ticks (42, 40, 38...)
Constant relative change
The tick grid is designed so that consecutive ticks correspond to a constant relative change in implied return, not a constant absolute price step. This matters because the end goal of the activity is a return, and a fixed price increment implies different rate increments depending on how far prices are from par.
// The unquantized price at tick n, where δ is the target relative increment in implied return
P(n) = 1 / (1 + (1 + δ)(N/2 − n))
// Consecutive ticks satisfy:
return(n−1) = (1 + δ) × return(n)This means the relative change in implied rate is constant across the grid (each step is the same percentage bigger than the last, not the same absolute amount). δ is fixed at the protocol layer (δ = 0.02, i.e. 2%). All markets share the same tick spacing and δ is not configurable per market.
In order to see the actual tick-to-price mapping where liquidity exists, use the following query:
GET /v0/midnight/books/{market-id}/{side}
GET /v0/midnight/books/{marketId}/asks?depth=4
// Each price level shows the tick and its corresponding price:
{
"data": [
{ "tick": 42, "price": "952380952380952320", "units": "10000000000", "count": 3 },
{ "tick": 43, "price": "951474785918173312", "units": "18500000000", "count": 5 },
{ "tick": 44, "price": "950552270869375104", "units": "7200000000", "count": 2 },
{ "tick": 45, "price": "949613146596339456", "units": "4100000000", "count": 1 }
]
}
// tick 42: price ≈ 0.9524 → rate = 5.000%
// tick 43: price ≈ 0.9515 → rate = 5.100% (5.100 / 5.000 = 1.02 exactly)
// tick 44: price ≈ 0.9506 → rate = 5.202% (5.202 / 5.100 = 1.02 exactly)
// tick 45: price ≈ 0.9496 → rate = 5.306% (5.306 / 5.202 = 1.02 exactly)
// Every consecutive pair: rate(n-1) / rate(n) = 1.02 = 1 + δNear par (P ≈ 1), where rate is close to zero, the rate-based spacing becomes impractically fine, so prices are quantized to a granularity of ε = 1×10⁻⁶. This lets the same immutable grid handle both far-from-par and near-par quoting.
Mempool & Router
Understand how the Midnight router and mempool validate offers before publication and how integrators can handle routing and takeability.
Fees
Understand Midnight fees across lending, borrowing, and settlement flows, with guidance for integrators displaying costs and net position value.