Due to an inefficient mechanism of pending stakes for pool holders in LiquidationPool, liquidations will be broken after a moderately large number of malicious holders who increased positions in 1 day.
The main invariant of collateralised stablecoins is: Issued stable coin value <= collateral value * collateral factor (or euro value < collateral value / collateralRate in terms of The Standard protocol), where 0 <= collateral factor <= 1. This invariant should be valid both for each position and for the whole protocol.
There are two cases of invariant break for a position:
collateral value * collateral factor < Issued stable coin value <= collateral value - in such cases, the position is liquidated, collateral is sold, and its value is enough to repay (burn) all issued stablecoins.
Issued stable coin value > collateral value - in such cases, the position is also liquidated, collateral is sold, but its value is not enough to repay (burn) all issued stablecoins:
X - issued stablecoins, Y - repaid and burnt during liquidation stablecoins where Y < X. X-Y is the protocol's bad debt. It means if all users close their positions, X-Y stablecoins are still issued. But this amount is backed by nothing.
When the protocol has bad debt, the fair price of a stablecoin = (Issued stablecoins - Bad debt) / Issued stablecoins and it is < 1. It means that the stablecoin tends to be unpegged.
To return the peg, the protocol must buy stablecoins from the market and burn them. It means that Bad debt is the direct protocol loss.
Declared protocol Compatibilities: Any EVM chains with live Chainlink data feeds and live Uniswap pools.
Many EVM blockchains have a block gas limit of several tens of millions.
In LiquidationPool, the stake of a user is delayed for 1 day (see LiquidationPool::increasePosition and LiquidationPool::consolidatePendingStakes).
Attack:
The pool is deployed.
Attacker calls LiquidationPool::increasePosition from 150 addresses with _eurosVal=1 in a short period of time (1 or several blocks).
The next day all methods which call LiquidationPool::consolidatePendingStakes require > 30mln of gas, which is more than the block gas limit of many EVM networks:
LiquidationPool::increasePosition
LiquidationPool::decreasePosition
LiquidationPool::distributeAssets, which is called during liquidations.
Liquidations are broken.
PoC. This test should be run with hardhat blockGasLimit: 20000000000 and placed in test/liquidationPoolManager.js inside describe('runLiquidation', async () => { block.
Output:
It shows that after an attack with 150 holders, LiquidationPoolManager::runLiquidation will require > 30mln of gas, which is more than the block gas limit of many EVM networks. This will lead to a permanent DDoS of liquidations (until upgrade).
Even in Arbitrum, with its huge block gas limit, sooner or later the liquidation transaction cost will be higher than the profit from liquidations.
Broken/delayed liquidations on a falling market will lead to loss of protocol funds due to undercollateralisation of minted EUROs.
Manual review
The stake delay mechanism must be rewritten. A two-phase mechanism could be used:
Holders add delayed stake.
After 1 day, stakers release hold from their stake.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.