SSRN 7157638 · Aave v3 live-book study

Published · SSRN

Liquidation Without Loss

A Live-Book Decomposition of Aave v3

By

Read as a loss figure, a looped book's at-risk number overstates a lending protocol's exposure without bound. This paper separates the three quantities a stress test collapses into one, derives the depth at which volume could become loss, and sizes the channels that actually convert it. Everything below runs on one live Ethereum block: 17,419 borrowers, $6.5B of debt.

Data providers

Bitquery served the event logs and the borrower enumerations, including the pre-event Aave-v2 book used for the out-of-sample test. CoinGecko Pro served the daily price marks behind the covariance basket and the event reconstructions. Both provided API access as sponsors of this work.

What the paper establishes

THE RESULT

$1.38B eligible, $0 lost

A 5% de-peg of the 1,437-position liquid-staking slice flags $1.38B as liquidation-eligible. About half of it transacts, and on the $1.357B pure-loop core the engine runs down the path, none of it is capital the protocol loses.

THE MECHANISM

LT(1+b) < 1

When a category's liquidation threshold times its bonus stays under one, a loop crossing HF = 1 is removed while its collateral still covers the bonus-grossed debt. Aave satisfies this in all eight live e-mode categories.

THE CONTROL

Break the path, not the depth

Loss needs a discrete gap, a stale oracle, or a pool too thin to absorb the seize. Depth alone does not produce it. So the levers are oracle bounds and supply caps, not the liquidation threshold.

Three quantities, not one

A stress test prints one number and a risk desk reads it as a loss. In a looped book it is neither the volume that transacts nor the capital the protocol loses, and the three separate cleanly once you run the book down an actual price path rather than marking it at a single trough.

Eligible debt is the debt in positions whose health factor crosses one. Under a partial close factor a position is liquidated in parts: a call repays a fraction of its current debt, the seizure re-buffers it above the threshold, and it may cross again later. So eligible debt runs to roughly twice the executed repayment, and across the live cluster not one position closes fully.

Eligible debt 1356.9 million dollars, executed repayment 691.3 million, seized collateral 720.2 million, protocol bad debt zero.LIVE LOOP CLUSTER · 236 POSITIONS · 5% DE-PEG$1,356.9Mliquidation-eligible debt$691.3Mexecuted repayment$720.2M collateral seizedpar units, not additive with the repayment above$0protocol bad debtLT(1+b) = 0.95 × 1.01 = 0.9595 < 1every loop is removed while its collateral still covers the bonus-grossed debt170 partial-liquidation calls · 0 positions fully closed · close factor 0.5Eligible debt is roughly twice executed repayment, and none of it is capital the protocol loses.
Fig. 1:The live loop cluster at a 5% de-peg: 236 positions, $2.19B. This is the pure-loop core the continuous-path engine runs, carrying $1,356.9M of the $1,381.5M the wider 1,437-position slice flags. Eligible debt is not executed volume, and executed volume is not loss. From headline_liveslice.json.

Why a loop is hedged against the shock everyone tests

A loop supplies an asset, borrows a correlated one, and re-supplies. The stress engine scales each position's real on-chain health factor by a collateral-versus-debt weighted move, where each u is the USD value of that leg and the on-chain HF₀ already embeds the protocol's e-mode thresholds. Written as a ratio it assumes the collateral legs share one liquidation threshold, which holds for the pure-loop core; the explicit per-asset form carries each leg's own threshold inside the collateral sum:

The mechanism is visible in the ratio. When the collateral class equals the debt class, the (1 + move) factors divide out and a market-wide crash barely touches the health factor. The loop survives. A directional position, whose stablecoin debt leg is held while its collateral falls, sees the move land in full.

acquisition/aave_v3_correlated_stress.pypython
def shocked_hf(position, moves):    """Scale the real on-chain HF by a collateral-vs-debt weighted move.     We never re-derive a loan-to-value: HF0 is read from the chain and so    already embeds whichever e-mode liquidation threshold the account selected.    """    coll = sum(u * (1 + moves[a]) for a, u in position.collateral.items())    debt = sum(u * (1 + moves[a]) for a, u in position.debt.items())     coll_w = coll / sum(position.collateral.values())    debt_w = debt / sum(position.debt.values())     return position.hf0 * (coll_w / debt_w)   # liquidatable iff < 1  # A loop holds the same class on both legs, so the move divides out:#     hf0 * (1 + m) / (1 + m) == hf0        -> survives a market crash# A directional position holds a stable debt leg, so it does not:#     hf0 * (1 + m) / (1 + 0) == hf0*(1+m)  -> liquidated
The shocked health factor. Nothing here re-derives a loan-to-value; the engine anchors to the real on-chain health factor so the protocol's own e-mode thresholds carry through.

This is why the number a uniform dashboard reports for a looped book is a netting convention rather than a property of the book. Against a correctly-netting desk, the correlated model and the same-class contrast agree to within 8% at every severity, 0.92× at −5%. The larger 6.53× and 9.13× ratios measure the size of a netting omission. And the cancellation is not an artifact of how assets are bucketed: shuffling the collateral-to-debt pairing collapses the ratio from 9.13× to about 1.3×, p = 0.0002 under both schemes.

The invariant that makes liquidation lossless

A loop is liquidated under the parameters of the mode it is held in, and this is where a reading can go wrong: the live cluster sits in ETH-correlated e-mode, whose category bonus is 1%, not the 6% wstETH reserve bonus. Read from the Pool contract, the pair is LT 0.95 with b = 0.01.

A liquidator seizing collateral worth the repayment grossed by the bonus is capped at the collateral the position still holds. So a shortfall can only open once collateral stops covering that grossed seizure, which gives the condition directly:

At LT 0.95, reaching the loss-on-crossing regime would need a bonus above 5.26%, five times what Aave sets. Every one of the eight live e-mode categories sits between 0.876 and 0.960, and the wstETH reserve pair is 0.859. This is not a knife-edge, and it is why the $0 is structural rather than an empirical robustness finding.

Liquidation threshold times one plus bonus, by parameter pair, all below the value one.LT(1+b), THE SOLVENT-LIQUIDATION INVARIANT0.850.900.951.001.05LT(1+b) = 1above this line, loss beginsthe instant a loop is liquidatableETH-correlated e-modeLT 0.95 · bonus 1%carries the live loops0.9595Stablecoin e-modeLT 0.92 · bonus 4%0.9568BTC e-modeLT 0.86 · bonus 3%0.8858wstETH reserveLT 0.81 · bonus 6%reserve, not a category0.8586
Fig. 2:Every live Aave parameter pair sits below one, with room. A reader who mistook the 6% reserve bonus for the 1% category bonus would compute 1.007 and conclude the opposite.

Where volume could become loss

The gap between volume and loss is only actionable once you can say where it ends. A loop supplying collateral C at threshold LT against debt D has an on-chain health factor of HF₀ = C·LT/D, and a de-peg of depth f marks the collateral down against the held debt leg. Two depths matter, and the breakpoint is the larger of them:

The first term is where the health factor reaches one and liquidation becomes eligible. The second is where the bonus-grossed seizure can no longer be covered and protocol bad debt begins. Loss cannot start before a loop is even liquidatable, so D* is the maximum. In leverage terms both shrink as the loop levers up.

acquisition/d_star.pypython
def d_star(leverage, lt, bonus):    """Depth at which a de-peg stops being volume and can become loss.     f_hf   the loop becomes liquidation-eligible  (HF reaches 1)    f_exh  collateral stops covering the bonus-grossed seizure    """    hf0 = lt * leverage / (leverage - 1)     f_hf = 1 - 1 / hf0                       # == 1 - (L-1)/(L*LT)    f_exh = 1 - (1 + bonus) * lt / hf0       # == 1 - (1+b)(L-1)/L     return max(f_hf, f_exh), f_hf, f_exh  # The live ETH-correlated e-mode anchor: ~12x leverage, LT 0.95, 1% bonusd, f_hf, f_exh = d_star(12, 0.95, 0.01)#   f_hf  = 0.0351   eligible at a 3.5% de-peg#   f_exh = 0.0742   loss cannot begin before 7.4%#   d     = 0.0742   -> a 3.9-point band of pure liquidation volume
The breakpoint in closed form. The figure below plots this function directly rather than a traced copy of it, so the curve on screen is the equation in the text.

For the live e-mode anchor at roughly 12× leverage the loop becomes liquidation-eligible at 3.5% but does not begin losing until 7.4%: a 3.9-point band in which the de-peg produces pure throughput. The median across the live slice is 6.9%, and 8.8% debt-weighted. The closed form reproduces the cascade engine's onset to within one grid step, at most 0.02%, everywhere it was checked.

Breakpoint D-star versus loop leverage for two parameter pairs, with cascade-engine confirmations.D* = max( 1 − 1/HF₀ , 1 − (1+b)·LT/HF₀ )0%8%16%24%32%4×8×12×16×20×loop leverage L = C / (C − D)HF₀ = 1 at 5.26×live e-mode loop · ≈12× · LT 0.95eligible at 3.5% · loss only past 7.4%a 3.9-point band of pure volumee-mode LT 0.95 / 1% bonuswstETH reserve LT 0.81 / 6% bonus (to 5.3×, its HF₀ = 1 ceiling)cascade-engine onset (matches to ≤0.02%)Below the curve a de-peg is pure liquidation-eligible debt. Above it, volume can convert to loss, but only if the continuous path is broken.
Fig. 3:D* against loop leverage for the two live parameter pairs, with the cascade-engine onsets overlaid. More leverage shrinks the band; the live e-mode anchor still holds 3.9 points of it.

What actually converts volume into loss

Because every live loop sits in the solvent-buffer regime, a deeper continuous de-peg does not convert volume to loss. The loop is removed at its own crossing while still solvent, at any depth. Driven down a continuous glide the anchor loop books $0 from 2% through 20%. What converts is a break in the path, and each channel can be sized on the same engine.

The channel that bites soonest is not the dramatic one. A discrete gap wide enough to jump the boundary books nothing until 8%. Partial liquidation into a finite pool books $3.01M at a 5% gap where a full liquidation into a deep pool books zero, because the position sits underwater across several rounds while each forced sale depresses the price for the next. That figure is an upper bound on the channel: Aave promotes the close factor to 100% below a health-factor threshold, and a single full close avoids the multi-round depression. Measured June-2022 oracle staleness multiplies an 8% gap from $0.58M to $5.13M in the deep-staleness tail.

python/defi_param_stress/cascade_ref.pypython
# One liquidation call, inside the cascade to a fixed point.# Two caps carry the whole result. # Aave's cap. Note close_factor is promoted to 1.0 below a health-factor# threshold, so the partial-round case below is the worst case, not the only one.repay = position.debt * close_factorseize_value = repay * (1 + bonus)                          # bonus-grossed # A position cannot be seized for more collateral than it still holds,# so a loop liquidated over several rounds never over-seizes.available = position.collateral - position.seizedseized = min(seize_value, available) position.debt -= repayposition.seized += seized # Loss is the residual, and it only opens once the grossed seizure# stops being covered:  bad_debt = D - C(1 - f)/(1 + b)if seized < seize_value:    bad_debt += repay - seized / (1 + bonus) # Selling the seized collateral into a finite x*y=k pool moves the mark# for the next round. With close_factor < 1 the position sits underwater# across rounds, and that is the channel that books loss at gaps a# deep-pool boundary treats as harmless.price *= constant_product_impact(seized, pool_reserves)
The cascade kernel's inner step. Two caps do the work: a call is limited to the close factor of current debt, and a seizure is limited to the collateral the position still holds. Residual debt is what books as loss.
Protocol bad debt per hundred-million-dollar loop, by break channel and severity.BAD DEBT PER $100M LOOP, BY WHAT BREAKS THE PATH (GAP DEPTH, EXCEPT THE LAG ROW)Discrete price gapcf 1.0, deep pool, e-mode LT 0.95$05%$0.58M8%$4.54M12%$12.46M20%$27.31M35%Partial liquidation into a finite poolcf 0.5, $2B pool$3.01M5%$5.51M8%$8.87M12%$15.7M20%Oracle stalenessgap held at 8%, lag varied$0.58Mnone$0.86M+31bp$2.03M+159bp$5.13M+500bp
Fig. 4:Bad debt per $100M-scale loop, by the channel that breaks the continuous path. Both parameter pairs hold $0 on an unbroken glide, so everything here comes from the break.

Secondary-market depth sets the magnitude and pulls the onset earlier: for a dispersed cluster the onset falls from 6.05% against an infinitely deep pool to 2.10% once the pool is finite, and the realized loss at a fixed 15% gap rises from $30.2M to $695.7M against a $1B pool. The loops do not lose because they are liquidated. They lose if the market they are liquidated into is too thin to absorb the seize.

When the forced selling moves the peg itself

The sharpest objection to reading $1.38B as volume is that it assumes the selling clears without moving the peg. For a liquid-staking token the peg is a secondary-market price, so the collateral being dumped depresses the very ratio that marks it. Closing that loop endogenously on the live cluster, with the withdrawal queue frozen, a shock past the most-levered loop's 0.19% trigger runs the peg down to roughly 3% of par, a de-peg of about 97%.

Even there the protocol books $0, because each loop is removed at its own solvent crossing before the peg runs past it. The danger absent a backstop is the collapse of the token's market, which harms its holders, rather than a hole in the protocol's balance sheet. With the queue live, redemption arbitrage caps the de-peg below the runaway region entirely.

Reflexive de-peg outcomes under a frozen and a live redemption queue.WHEN THE FORCED SELLING MOVES THE PEG ITSELFREDEMPTIONS FROZENJune-2022 style, no backstopshock past the trigger0.19%peg runs to≈3% of parprotocol bad debt$0REDEMPTIONS LIVEthe 2023+ book, queue workingarbitrage caps the de-peg at1–3%runaway regionnever reachedprotocol bad debt$0The backstop bounds the peg collapse; the LT(1+b) invariant bounds the protocol loss. The liquidation threshold governs neither.
Fig. 5:The endogenous-peg fixed point on the live cluster. The backstop bounds the peg; the invariant bounds the loss. Pool depth is a labelled assumption, swept.

What this changes for a parameter committee

The paper's recommendation splits along the axis that decides which parameters even apply. A wstETH position held under the ETH-correlated category is governed by the category threshold, so a change to the reserve threshold does not reach it. Of 1,723 accounts holding wstETH collateral, 56 sit at an effective threshold only that category reaches; the remaining 1,667 are reserve-priced.

  • Cutting the reserve threshold from 0.81 toward 0.59 raises the 5%-de-peg at-risk of reserve-mode accounts from $16.9M to $144.1M, an eightfold rise, while removing no realized loss. There is none in that segment to remove.
  • The e-mode category is where the tail lives, and there the operational controls are the supply cap and a live withdrawal backstop, neither of which a threshold change provides.
  • Oracle deviation and heartbeat bounds govern the gap and lag channels, which is where loss actually enters.
  • Supply caps sized to secondary-market depth govern the partial-liquidation channel, the one that books loss at gaps a deep-pool boundary treats as harmless.

Stated plainly: the liquidation threshold is not the lever. It sets where volume starts, and volume is not what costs the protocol money.

Why the engine's zero can be trusted

A result of exactly zero invites the question of whether the engine can book a loss at all. It can, and the paper spends a part of its length demonstrating it rather than asserting it.

  • The same engine books large realized loss when the path is broken: sold into a finite $2B pool the dispersed cluster books $203M from roughly a 3% de-peg. Note which boundary that is past. It clears the finite-pool onset of 2.10%, not the deep-pool D* of 6.05%, because a finite pool pulls the onset earlier. So the $0 is the value of the curve below its breakpoint, not a floor the code cannot leave.
  • Three real episodes reconstruct to the same place. The demanding one is a pre-event Aave-v2 stETH book, taken from archival state before the June-2022 de-peg and stepped down the real peg path: it books $0 against the protocol's negligible realized loss. A separate receipt-reconstruction of the same episode also books $0 on the path, and there a single-trough snapshot would have raised a $25.1M false alarm. The two constructions are kept apart in the paper and they agree on the zero.
  • The March-2023 USDC counterfactual flags $0.3M across 26 positions against $0 realized, and those 26 reconcile exactly to the oracle-pin reconstruction.
  • The hot kernel is Rust with a Python reference and a Numba twin, numerically equivalent within 1e-9; the observed maximum absolute difference is about 2.3e-13 over a 300-case smoke.
  • Seeds are fixed at 20260609 and BLAS threads pinned to one before numpy is imported. The permutation schemes, the strengthening tests and their verdict rules are written down in a hash-locked analysis plan. The paper is careful about what that buys: the hash certifies the plan's integrity, not its priority in time, since the lock date is self-asserted and the files enter git history four days later. The pre-event-book test was added after the lock and is labelled as such.

The same engine, on a loss that did happen

In November 2022 an account borrowed 92 million CRV against USDC collateral and the position turned. As the token was squeezed upward, liquidation could not clear the debt fast enough: liquidators repaid 89.6M CRV, the collateral ran out, and 2.39M CRV of debt was left standing. The paper deliberately publishes no depth or slippage figure for this episode, because the price-impact fit during the squeeze had a negative R² and a number a constant would beat is worse than no number.

That residual, at the prevailing $0.693, is the loss: $1.65M modelled against roughly $1.60M realized on-chain, a 3.4% error. This is the mirror image of the de-peg result and it comes off the same kernel. When the cost to clear outruns the liquidation bonus, liquidation stalls and the residual becomes the protocol's loss. One engine reproduces both a real $1.6M hole and a real $0.

100M50M092.0MCRV borrowed−89.6MliquidatedCRV debt repaidresidual debt2.39M CRV unrepayable× $0.693 = $1.65M bad debtMODELED BY THE ENGINE$1.65MREALIZED ON-CHAIN$1.60M3.4% error
Fig. 6:The Aave-CRV episode reconstructed: 92M CRV borrowed, 89.6M clawed back, and the 2.39M unrepayable residual priced at the prevailing mark. Modelled $1.65M against realized ~$1.60M.

What the $0 does and does not cover

  • The strict claim is for the $1.357B pure-loop core. The remaining 1.76% is mixed-collateral positions whose other legs carry their own parameters; the invariant makes $0 the expected outcome there too, but it is disclosed rather than folded in.
  • The live snapshot carries each account's effective liquidation threshold but not its selected e-mode category, so membership is inferred from effective LT. The split is clean, with a wide empty gap between the two groups, but it is a proxy.
  • Zero protocol bad debt is not zero economic cost. The same liquidations pay the bonus to liquidators, impose real losses on the borrowers liquidated, and can trigger DAO compensation. The claim is about the protocol's balance sheet.
  • The reserve-threshold sweep applies a lower threshold to today's frozen book, so it measures the one-time transition cost, not the steady state a permanently lower threshold would settle into.

Reproducing it

The engine, the Rust kernel, the acquisition scripts, the parity harness, the frozen fixtures and the pre-registration are released in full. The market data is not redistributed: live state comes from a public archival RPC, event logs from Bitquery, and price marks from CoinGecko Pro, with credentials read from the environment. The headline numbers run on a fixed, hashed snapshot, so the claims are checkable against the inputs rather than re-pulled from a keyless clone. github.com/DaruFinance/defi-param-stress

Cite this

Related work

The de-peg engine’s credibility checks fit peaks-over-threshold GPD tails and a tail-dependence coefficient χ. That is the same machinery, implemented and demonstrated on its own, in Peaks-over-threshold and pairwise tail-coupling.