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
PriceFeed state machines tolerate moderate Chainlink latency, but a stale feed will trip the staleness threshold and move the branch into a fallback price source. If both the primary feed and the canonical-rate 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 wstETH and rETH, the price is derived as Chainlink ETH/USD × canonical rate. If the LST market price diverges materially from the canonical rate (e.g. a depeg), the protocol's redemption-path pricing uses STETH_USD_DEVIATION_THRESHOLD = 1% to detect it and select the conservative side.
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.
Collateral-token risks
Blacklisting
If a collateral token (e.g. a future USDC-style asset) blacklists protocol contracts, multiple paths can revert:
| Blacklisted contract | What breaks |
|---|---|
ActivePool | Most unshielded paths: open, addColl, redemption payout, liquidation movement |
ActiveShieldedPool | All shielded paths |
DefaultPool | Redistribution movements |
StabilityPool | Liquidation offset, depositor gain payouts |
CollSurplusPool | Surplus claims |
Cross-branch effect: a revert in any single branch reverts the entire Aggregator.redeemCollateral call. So one blacklist-poisoned branch can DoS global redemption, until the bad branch is shut down or excluded.
Mitigation: prefer collaterals without blacklist semantics. If blacklisting is unavoidable, the affected branch can be shut down via BorrowerOperations.shutdown() once its TCR drops below SCR or via the Aggregator.shutdownBranches() sweep when the price feed flags failure.
Fee-on-transfer
The protocol assumes ERC-20 transfers move exact amounts. A fee-on-transfer collateral would silently shortchange ActivePool deposits and redemption payouts. The protocol does not currently mitigate this and would mis-account.
Mitigation: do not enable fee-on-transfer collaterals as branches.
Rebasing
A rebasing collateral (e.g. stETH-style) would cause balances in ActivePool to drift independently of trove accounting. The protocol uses wrapped wstETH precisely to avoid this, wstETH is non-rebasing.
Mitigation: only wrapped, non-rebasing variants are added as branches.
Trove risks
MinTrove griefing
The minimum-trove floor (MIN_NET_DEBT + RD_GAS_COMPENSATION = 2,000 RD) bounds the worst-case redemption walk and liquidation gas economics. An attacker spamming minimum-size troves to bloat the sorted list:
- Pays full borrow interest on each trove (real economic cost).
- Pays gas to open each one (real economic cost).
- Their troves are still walkable and liquidatable.
Effect: makes redemption / liquidation gas higher for everyone, marginally. Not a free attack.
Mini-dwarf accumulation
Redistribution can produce troves below the practical minimum (mini-dwarfs). These are skipped by redemption walks and swept on contact. If a large number accumulate without being swept, they bloat the sorted list.
Mitigation: any redemption walking past a mini-dwarf removes it; routine activity keeps the count bounded.
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
Cross-branch 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. Until then, integrators should be prepared to exclude branches in their hint construction and to detect which branch is reverting.
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 = 1 day. 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.
Governance / admin surface
The core contracts are immutable. Specifically, the following are not upgradeable or admin-changeable:
Aggregator,RDToken,FEEToken, fully immutable, no admin.RateParControl, immutable; controller constants are hardcoded.MarketOracle, immutable.- Per-branch core:
BorrowerOperations,TroveManager,Liquidations,Redemptions,Rewards,StabilityPool,InterestEngine,SortedTroves, all pool contracts.
The following have limited owner control within hard bounds:
PriceFeed(legacy),setAddresses(...)runs once and then ownership is renounced.- Per-branch parameters (MCR / CCR / SCR / liquidation penalties / redemption-fee floor) are set at deploy time and immutable thereafter.
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.
- Shutdown machinery: what happens when a branch goes down.
- Audits & security: disclosure and bug-bounty channel.