Risks
This page is a structured list of things that can break or behave badly in the protocol, organized by surface. None of these are speculative, they're properties of the current code and design that users, integrators, and auditors should be aware of.
Oracle risks
Stale Chainlink feeds
PriceFeeds tolerate moderate oracle latency, but a stale primary trips the staleness threshold and the feed falls back to its secondary source. If both the primary and fallback fail, the branch is callable for shutdown via shutdownFromOracleFailure().
Effect on user: ICR computation freezes at the last-good price. New borrows and adjustments revert. Existing positions are not seized.
LST canonical-rate divergence
For LSTs like wstETH and rETH, the price is derived as ETH/USD × canonical rate. If the LST market price diverges materially from the canonical rate (e.g. a depeg), the redemption-path pricing detects it and selects the conservative side, gated by a per-asset deviation threshold (STETH_USD_DEVIATION_THRESHOLD = 1% for wstETH, RETH_ETH_DEVIATION_THRESHOLD = 2% for rETH).
Effect on user: Redemptions on a wstETH branch during stETH depeg pay you out at the lower of the two prices, you may receive less collateral per RD than the spot market suggests.
MarketOracle pool drain
MarketOracle reads a curated Balancer pool. If the pool's liquidity (TWAL) drops too low, the controller's read becomes unreliable. The contract returns validity = false and RateParControl refuses to commit a new update.
Effect on protocol: Par and rate freeze at their last committed values. No catastrophic outcome, but the controller can't respond to market moves until liquidity is restored.
Same-block manipulation
The MarketOracle reads only finalized observations with _MIN_PRICE_AGE = 60s. A flash-loaner moving the pool price in the same block as the read cannot poison the controller, the controller doesn't consume same-block ticks.
This is a known design defense, not a residual risk. Same-block manipulation is mitigated.
Liquidation risks
SP empty during cascade
If a price crash triggers a cascade of liquidations and the Stability Pool runs dry, the protocol falls back to redistribution. Every active trove on the branch inherits a pro-rata slice of the liquidated debt and collateral. Surviving troves' effective ICR drops.
Effect: surviving borrowers can be liquidated in subsequent cascades they didn't trigger. This is intrinsic to the redistribution mechanism; same risk profile as Liquity v1.
Negative-yield SP deposit during crash
If collateral price drops below the SP's effective offset price (e.g. a 20% drop in seconds), depositors absorb troves at a loss, collateral received is worth less than RD burned. Liquity v1 has seen this happen.
Mitigation: depositors should size their position with the awareness that a black-swan crash can produce a nominal loss.
Below-MCR trove not liquidated
The protocol can carry an undercollateralized trove for some time if no one calls liquidate(...). Anyone can do it, but if the gas economics don't favor a liquidator (very small trove, congested chain), liquidation may be delayed. The trove's debt is still RD-denominated, the protocol carries the loss until liquidation completes.
Mitigation: keepers, bots, and front ends are incentivized via the 200 RD gas comp + collateral kicker. In practice liquidations happen within blocks of MCR breach.
Redemption risks
Redemption revert
A single branch reverting during Aggregator.redeemCollateral reverts the entire transaction. This is the biggest operational risk for redeemers and integrators: a bug or DoS on one branch can stall global redemption.
Mitigation: shutdown of the affected branch removes it from the basket.
Bootstrap-period absence of price floor
For the first 14 days, redemption is disabled. RD's price floor mechanism doesn't exist during this window. If RD trades meaningfully below par during bootstrap, the controller has only the borrow rate as a tool.
Mitigation: borrow rate's response is calibrated to be aggressive on the under-peg side (Kp_under = 3 × Kp_over). Initial liquidity should be sized assuming no redemption floor.
Quota exhaustion
The redemption quota refills with time. A large redemption can deplete the bucket, and subsequent redemptions revert until refill. There's no admin override.
Mitigation: read Aggregator.redemptionQuotaAvailable() before submitting.
Controller risks
Stale-poke after long inactivity
If RateParControl.update(...) has not been called for many days, the accumulated dt is capped at MAX_CONTROL_DT = 12 hours. The controller will not apply a single huge step, it will take subsequent updates to fully catch up.
Effect: in deep tail scenarios (no one updating the controller), rate and par lag the true equilibrium.
Mitigation: keeper rewards on update() calls incentivize regular updates. Any user-initiated borrowing or adjustment can trigger a refresh.
Asymmetric gains
Under-peg gains are 3× over-peg gains. This is intentional, RD-below-par is the more dangerous mode, but it means the controller responds faster when correcting down and slower when correcting up. During a sudden over-peg event, expect the rate to fall less aggressively than it would rise in the symmetric case.
Immutability
The contracts are immutable and effectively ownerless: no admin keys, no upgrade path, and no owner able to change any parameter after deploy. Nothing below is upgradeable or admin-changeable:
Aggregator,RDToken,FEEToken,RateParControl,MarketOracle, fully immutable;RateParControl's controller constants are hardcoded.- Per-branch core:
BorrowerOperations,TroveManager,Liquidations,Redemptions,Rewards,StabilityPool,InterestEngine,SortedTroves, and all pool contracts. - Per-branch parameters (MCR / CCR / SCR / liquidation penalties / redemption-fee floor) are set at deploy time and immutable thereafter.
Every contract built on an ownable base either renounces ownership at deploy or never initializes an owner at all, so the owner is the zero address and owner-gated functions can never be called. Deploy-time wiring is frozen permanently.
There is no admin path that can:
- Mint RD outside the borrowing protocol.
- Freeze user assets.
- Shut down a branch without trigger conditions being met.
- Change ratios on an existing branch.
Adding a new branch is a fresh contract deployment, not an upgrade.
Where to read next
- Architecture: which contract owns which behavior.
- Collateral shutdown: what happens when a branch goes down.
- Audits & security: disclosure and bug-bounty channel.