Virtualized Funding: Gas-Free Settlement at Any Scale
How Perpl settles funding payments for 100,000+ positions with two storage reads instead of 100,000 writes.
Perpetual exchanges use a funding rate to keep the perpetual price anchored to the underlying asset. Periodically — roughly every hour — longs pay shorts (or vice versa) based on the rate and position size. On a centralized exchange this is trivial: a background job debits and credits every account. On a decentralized exchange running on a general-purpose EVM chain, it's a fundamental scaling problem.
The Settlement Problem
With 100,000 open positions, paying out funding every hour requires 100,000 on-chain state writes per funding event. Each write to a position's storage slot costs at least 5,000 gas. The arithmetic is unforgiving: 100,000 positions × 5,000 gas = 500 million gas — roughly 16 full blocks — just to settle one hour of funding. At Monad's block rate of ~2.5 blocks per second, that's six seconds of block space consumed every hour, purely by bookkeeping.
In practice it's worse. Each position update involves reads, balance changes, and event emissions. The real cost is closer to 25,000 gas per position. At 100,000 positions that's 2.5 billion gas per event — 83 blocks. This isn't a scalability inconvenience; it's a protocol design constraint that has shaped every decentralized perp that exists.
The Insight: Superposition
The key observation is that a position's cumulative funding payment depends only on three things: its lot size, when it was opened, and when it was closed. The lot size and side are fixed between open and close (the contract forces settlement if they change). This means the per-event payment for any position factors cleanly:
The funding product Fproduct[j] is entirely market-level data — it doesn't depend on any individual position. So instead of writing to every position at each event, you can store a single running sum: the funding product sum, Fsum.
Frate[j] = funding rate (10⁻⁶ units)
L = signed lot size (+long, −short)
One value computed per event.
Initialized to 0 at contract creation.
j = Fsum read at position close
Two reads. Exact. Any duration.
θ_pnl = (16.045 − 7.880) × 10 × (−1) = −81.65 USDC (long pays funding — 4 positive rate events minus 1 negative, net positive).
Computed from 2 reads: Fsum[m] stored at open, Fsum[j] read at close.
Two Reads, Any History
When a position opens, the contract records the current Fsum as the position's funding checkpoint. Call it Fsum[m]. When the position closes, the contract reads the current Fsum[j]. The cumulative funding payment — the position's premium PNL — is:
It doesn't matter if the position was held through 1 funding event or 1,000. The cumulative effect telescopes into the difference between two stored sums. No loop, no iteration, no per-position writes at event time.
What happens when a position closes: two reads — fetch Fsum[j] and the stored Fsum[m]. Compute. Done.
as funding checkpoint
(the checkpoint)
for each event j
Position NOT touched ✓
regardless of positions
θ_pnl realized now
New checkpoint stored
reset checkpoint
Fsum[m]) × L
Final settlement
any # of events
Gas Cost: O(1) Per Event
For a naive DEX, settlement gas scales linearly with position count: double the traders, double the gas. Perpl's gas cost for a funding event is constant regardless of how many positions are open. A single storage write to update Fsum, plus whatever overhead the permissioned rate-setter transaction requires. The protocol never loops over positions.
The settlement cost is deferred and amortized. Each position pays its cumulative funding when it changes or closes — folded into the settlement transaction the trader was going to execute anyway. The chain never does work for dormant positions.
Forced Settlement on Position Change
The contract enforces a constraint: any change to a position's lot size or side triggers immediate premium PNL realization. This is what makes the virtualization safe. If a trader could silently grow or shrink their position without settling, the Fsum checkpoint would become invalid. The forced settlement means the checkpoint is always correct relative to the current position parameters.
market.fSum and position.fSumCheckpoint — two reads, constant cost. The funding event transaction writes only market.fSum — one write, constant cost.