Number Systems

Perpl uses four purpose-built integer representations — each sized exactly for its job. The choice of decimal split between price and size is a fundamental tradeoff baked into every market on the exchange.

Why integers?

EVM smart contracts don't have floating point. Every price, size, and balance is an unsigned integer. The question is how many of those integers represent whole units versus fractional parts — the decimal placement is a design choice fixed at deployment time.

A naive approach uses one representation for everything. Perpl uses four, each with the right bit width and decimal precision for its specific role.

The four number systems

CNS — Collateral
Collateral Number System
Storage: 80-bit unsigned
Decimals: 6 (fixed — always USDC)
Max: ~$1.2 quintillion USDC
Used for: balances, fees, PnL, margins
PNS — Price
Price Number System
Storage: 32-bit unsigned
Decimals: 0–6, per-perp
ONS window: $1.68M (dec=1) · $16.78 (dec=6)
Used for: mark price, oracle, entry price
LNS — Lot Size
Lot Number System
Storage: 40-bit unsigned
Decimals: 0–6, per-perp
Max: 1.1 trillion raw lots
Used for: position sizes, order sizes
ONS — Orderbook
Orderbook Number System
Storage: 24-bit unsigned
Decimals: 0 (tick offsets only)
Max: 16,777,215 ticks from base
Used for: orderbook price slots
Figure 1: Bit Width Comparison — all four types relative to a 256-bit EVM word
CNS
80-bit unsigned
uint80 · max ~1.2 × 10²⁴
Collateral, fees, PnL
LNS
40-bit unsigned
uint40 · max 1,099,511,627,775
Lot sizes, position sizes
PNS
32-bit unsigned
uint32 · max 4,294,967,295
Mark price, oracle, entry
ONS
24-bit unsigned
uint24 · max 16,777,215
Orderbook price slots
EVM word
256-bit reference (1 storage slot)
LNS is wider than PNS (40 vs 32 bits) because lot counts can be vastly larger than prices — a trader holding billions of a near-zero-price token needs more range than any realistic price. All four types are chosen to pack tightly into EVM storage words, minimising gas cost per operation.
▶ Explore the interactive

The precision budget

CNS (collateral) is always 6 decimal places — USDC has 6 decimals, and that's fixed. When the contract computes PnL as price × size, the result must land in CNS. That means price and size decimals together can't exceed 6:

d_c = 6     (always — USDC fixed)
d_p + d_l ≤ 6
d_p ≥ 0,  d_l ≥ 0 Both live perps use the full budget (d_p + d_l = 6). Using fewer wastes precision; using more causes truncation errors.

This means every market is making a tradeoff: more price precision means less size precision, and vice versa. The split is chosen once at market creation and can't change. Using fewer than all 6 decimals is allowed but wastes precision that could otherwise be allocated to price or size granularity.

Figure 2: The Precision Budget — Formal Derivation
price
PNS · price_dec decimals
×
size
LNS · size_dec decimals
=
PnL
CNS · must have 6 decimals
price_dec + size_dec ≤ 6
= 6 is optimal — uses the full budget without truncation
BTC example (1 + 5 = 6)
price = $95,000.1 → PNS = 950,001
size = 2.50000 BTC → LNS = 250,000
raw product = 950,001 × 250,000
             = 237,500,250,000
÷ 106 = $237,500.25 USDC ✓
MON example (6 + 0 = 6)
price = $0.040000 → PNS = 40,000
size = 5,000 MON → LNS = 5,000
raw product = 40,000 × 5,000
             = 200,000,000
÷ 106 = $200.00 USDC ✓
If price_dec + size_dec > 6, the product has more decimal places than CNS can hold — fractional USDC is silently dropped, causing rounding errors in every PnL calculation. If < 6, no error occurs but precision is wasted (the product always ends in trailing zeros). Both live perps use exactly 6: the optimal choice that uses the full precision budget without any truncation.
How the 6 decimal budget is split — live perps
BTC-PERP
1
5
1 price + 5 size
MON-PERP
6
6 price + 0 size

Why BTC and MON look so different

At ~$95,000, Bitcoin needs fine size granularity — you want to let traders buy $1 worth of BTC, which means 0.00001 BTC minimum lots (5 size decimals). Price ticks of $0.10 are more than adequate at that scale. So BTC uses 1 price decimal, 5 size decimals.

Monad at ~$0.04 has the opposite problem. A $0.10 price tick would be 250% of the entire asset price — the tick is larger than the price itself. You need $0.000001 ticks to express meaningful price moves. And whole-MON lot sizes ($0.04 each) are still small enough for retail. So MON uses 6 price decimals, 0 size decimals.

Figure 3: BTC vs MON — Full Configuration Comparison
Property BTC-PERP MON-PERP
Market price~$95,000~$0.04
price_decimals16
size_decimals50
Price tick size$0.10$0.000001
Tick as % of price0.000105%0.0025%
Min lot size0.00001 BTC1 MON
Min trade value~$0.95~$0.04
Max lot size~10.9B BTC~1.1T MON
ONS price range$0 – $1,677,721$0 – $16.78
Rally headroom (ONS)17.7× current price419× current price
Base price update riskVery low (wide window)Needed above ~$16
PNS for current price950,00040,000
price_dec + size_dec1 + 5 = 6 ✓6 + 0 = 6 ✓
Design priorityFine size granularityFine price granularity
Both live perps use all 6 decimal places (1+5=6 and 6+0=6), maximising precision. Using fewer is valid but wastes the unused budget. The direction of the split is driven entirely by the asset's price magnitude: expensive assets need sub-cent tradeable units; cheap assets need sub-cent price resolution.

The ONS range tradeoff

Prices in the orderbook are stored as 24-bit offsets (ONS) from a base price. This caps the total representable price range at 16,777,215 ticks. More price decimals means smaller ticks, which means the total range shrinks:

BTC at 1 price decimal: each tick is $0.10, so the range spans $1,677,721 — enough to cover BTC from near zero to multiples of today's price. MON at 6 price decimals: each tick is $0.000001, so the range spans $16.78 — fine at $0.04 but requires a base price update if MON ever rallies significantly.

Figure 4: ONS Price Range vs price_decimals (16,777,215 ticks total)
price_dec size_dec Price tick ONS range ($0 → max) Range visual Min trade (BTC) Min trade (MON) Notes
06$1.00 $16,777,215
$0.000095 $1.00 (2500% of price) MON: tick swamps price
15$0.10 $1,677,721
$0.95 $0.10 (250% of price) ← BTC live config
24$0.01 $167,772
$9.50 $0.01 (25% of price)
33$0.001 $16,777
$95.00 $0.001 (2.5%)
42$0.0001 $1,677
$950 $0.0001 (0.25%)
51$0.00001 $167.77
$9,500 $0.00001 (0.025%)
60$0.000001 $16.78
$95,000 (whole BTC) $0.04 ← MON live config
The range visual shows total ONS window relative to dec=0 ($16.7M). At dec=6, the entire window spans only $16.78. If MON rallied to $20, basePricePNS would need to shift up. At dec=1 (BTC), the window spans $1.68M — more than enough for any realistic price, no updates needed.
Figure 5: ONS → PNS Relationship (basePricePNS + ONS = PNS)
BTC · price_dec = 1 · base = 0
Full PNS range (32-bit, $0 → $429M)
$0ONS spans $0 → $1,677,721 (39% of PNS)$429M
ONS window detail (24-bit)
$0 ▲ $95,000 (56.6% of window) $1,677,721
Headroom: $1,582,721 above current price. No base update needed.
MON · price_dec = 6 · base = 0
Full PNS range (32-bit, $0 → $4,295)
$0ONS spans $0 → $16.78 (0.4% of PNS)$4,295
ONS window detail (24-bit)
$0 ▲ $0.04 (0.24%) $16.78
419× headroom to ONS ceiling. If MON exceeds ~$16, basePricePNS shifts up.
basePricePNS is a fixed offset stored per-perp. The orderbook stores all prices as ONS values (ticks above this base). When the market moves outside the ONS window, a privileged transaction shifts the window by updating basePricePNS. For BTC with 1 price decimal the window is wide enough that this almost never happens; for MON with 6 decimals a 400× rally from $0.04 to ~$16 would exhaust the window and require a shift.
The key insight: There's no universally optimal decimal split. It depends on the asset's price magnitude. Low-price tokens need fine price precision; high-price tokens need fine size precision. The 6-decimal budget forces a choice that has to match the asset's characteristics — and it's fixed at market creation.
▶ Try the interactive explorer