The core BPE mechanism treats capacity as a signal and money as a conserved flow. This section introduces six extensions that recast the protocol as a thermodynamic system, where aggregate variance maps to temperature, capital ratios map to virial equilibrium, and phase transitions trigger circuit breakers. Three new contracts (TemperatureOracle, VirialMonitor, SystemStateEmitter) plus modifications to BackpressurePool, Pipeline, and DemurrageToken implement the framework.
In statistical mechanics, temperature measures the width of the energy distribution across microstates. We define an analogous economic temperature from the variance of capacity attestations within an epoch.
Let be the EWMA-smoothed variance of declared capacities across all active sinks, and be the maximum expected variance (a governance parameter). The system temperature is
capped at when . Default bounds are and (scaled to and in 18-decimal fixed point). When all providers report identical capacity, and , producing near-deterministic routing. When capacity reports diverge, rises, spreading flow across more sinks.
Setting prevents an oscillation trap: if is too low, two sinks with nearly equal capacity alternate as the sole recipient each epoch. A floor of 0.5 plus the exploration bonus described in the next section eliminates this failure mode.
The TemperatureOracle contract stores on-chain. An authorised updater (typically the OffchainAggregator) calls updateTemperature(σ², σ²_max), which computes the equation above in fixed-point arithmetic and emits a TemperatureUpdated event.
Classical BPE assigns pool shares proportional to . Boltzmann routing replaces this with a probabilistic allocation: the share for sink with spare capacity is
Computing on-chain is expensive. We use a first-order Taylor approximation , which is accurate when (the common case—spare capacity is typically a fraction of ). The TemperatureOracle exposes this as boltzmannWeight(c) .
Full Boltzmann weights are computed off-chain, signed under EIP-712 by the aggregator, and submitted via BackpressurePool.rebalanceWithShares(). On-chain, these shares are blended with a uniform exploration term:
where defaults to 5% (capped at 20%). The exploration term guarantees that every connected sink receives at least of the flow, preventing starvation even at low temperature. Shares are scaled to GDA units via .
This architecture keeps the expensive softmax off-chain while preserving verifiability: anyone can recompute the Boltzmann distribution from the signed capacity attestations and check the submitted shares.
In celestial mechanics, the virial theorem relates a system's kinetic and potential energy: at equilibrium. We adapt this to bound capital:
where is epoch throughput (total tokens that flowed through pools), is total staked capital, and is total escrowed capital. The equilibrium target is : throughput and bound capital are balanced. means the system is under-capitalised relative to throughput (a "bull" phase); means excess capital is locked up with too little activity.
The VirialMonitor contract stores and exposes two advisory functions. recommendedDemurrageRate() returns an adaptive decay rate :
so that when (healthy or bull), (1%/year), and when (stagnant), rises toward (10%/year), taxing idle capital and pushing it back into circulation. recommendedStakeAdjustment() returns as a signed integer, signalling whether participants should add or withdraw stake.
The DemurrageToken is an ERC-20 wrapper that applies continuous decay to idle balances. Given a fixed or virial-adaptive decay rate and elapsed time since last rebase:
On-chain, a linear approximation avoids the exponential: . At the default 5%/year rate with hourly rebases, the approximation error is , well within rounding noise.
When adaptiveDecayEnabled is true and a VirialMonitor is attached, getEffectiveDecayRate() uses the monitor's recommended rate instead of the fixed . This closes a feedback loop: low activity () raises demurrage, which pushes idle tokens into staking or spending, which raises throughput, which raises back toward equilibrium.
Accounts marked decayExempt (staking contracts, escrow buffers) skip decay. Decayed value accrues to a configurable decayRecipient—typically a protocol treasury or burn address.
The EscrowBuffer stores overflow payments when all sinks are saturated. We define escrow pressure as an osmotic analog:
where is the current buffer level and is the configured maximum. ranges from 0 (empty) to 1 (full), scaled to . The buffer emits a PressureChanged event on every deposit, and the SystemStateEmitter reads when classifying system phase.
Draining is permissionless: any caller can trigger drain(taskTypeId), which distributes buffered tokens to sinks proportionally by capacity. This creates a pull-based pressure release—when downstream capacity returns, accumulated demand is served without coordinator intervention.
The Pipeline contract composes multiple pool stages into a sequential workflow. Each stage carries a CircuitBreakerState that tracks its current phase. Five phases exist:
| Phase | Code | Trigger |
|---|---|---|
| Steady | 0 | Default healthy state |
| Bull | 1 | |
| Shock | 2 | or |
| Recovery | 3 | and |
| Collapse | 4 | or |
Collapse triggers come in two flavours. An immediate collapse fires when throughput drops below 5% of declared capacity in a single epoch or escrow pressure exceeds 95%. A gradual collapse fires after three consecutive epochs where throughput stays below 20% of declared capacity or escrow pressure exceeds 90%.
When a stage collapses, the pipeline decouples it: upstream effective capacity is capped at the downstream throughput observed before collapse, halting new flow into the failed segment. Recovery occurs when the stage's throughput ratio returns above 20% and escrow pressure drops below 90%. Decoupling prevents the cascade failure that typifies tightly coupled pipelines—a bottleneck in stage 3 of a 5-stage pipeline no longer starves stages 1 and 2 of routing information.
The SystemStateEmitter reads , , and from their respective contracts and emits a single SystemStateUpdate(scope, τ, V, P, phase, timestamp) event. Phase classification follows the table above: the emitter evaluates threshold conditions and assigns the highest-severity applicable phase. Any address can call emitSystemState—the function is permissionless, designed for keeper bots or cron jobs.
Dashboards and off-chain agents subscribe to this event to make routing and capital allocation decisions without polling multiple contracts.
The throughput-optimality proof uses a Lyapunov function over virtual queue backlogs. Boltzmann routing preserves the drift bound because the exploration term is bounded and the Boltzmann weights are proportional to spare capacity—the same monotonicity property that the original max-weight policy exploits. The temperature parameter controls the sharpness of the allocation but does not change the sign of the drift: for any , the expected drift remains negative outside the capacity region, which is sufficient for Foster–Lyapunov stability.
Adaptive demurrage operates on a slower timescale (hours to days) than pool rebalancing (minutes). This separation of timescales means the demurrage feedback loop does not interfere with the per-epoch allocation proof: within any single epoch, is constant and the balance dynamics reduce to the base case.