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.

CEX
✓ Works
Background job, no gas
Naive DEX
✗ Fails
Loop N positions per event
Appchain
⚠ Costly
Custom opcodes, dedicated chain
Perpl
✓ Works
Virtualized, 2 reads at close

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:

Fpayment[j] = Fproduct[j] × L where Fproduct[j] = Pi[j] × Frate[j] — the funding price times the funding rate at event j

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.

Fsum[j] = Σ Fproduct[k] for all k ≤ j One value, updated once per funding event, regardless of how many positions are open
▶ See it in the interactive sim Full formula derivation

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:

θpnl = (Fsum[j] − Fsum[m]) × L Two reads. Any number of funding events in between. Exact to the satoshi.

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 changes at each funding event: one write — update Fsum.
What happens when a position closes: two reads — fetch Fsum[j] and the stored Fsum[m]. Compute. Done.

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.

▶ Try the interactive simulation Technical diagrams