If PriceCalculator::tokenToEurAvg
/ the last Chainlink price
> SmartVaultManagerV5::collateralRate
, the protocol will incur bad debt after liquidation.
The main invariant of collateralised stablecoins: 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.
Key facts:
Two types of prices are used during liquidation:
PriceCalculator.tokenToEurAvg
is the average price for some period (currently 4h), used to determine if a vault can be liquidated:
SmartVaultV3::liquidate
-> SmartVaultV3::undercollateralised
-> SmartVaultV3::maxMintable
-> SmartVaultV3::euroCollateral
.
The latest Chainlink price during collateral distribution:
LiquidationPool::distributeAssets
.
On a falling/rising market, PriceCalculator::tokenToEurAvg
and the last Chainlink price could be different (with the current average duration of 4h - dramatically different).
If PriceCalculator::tokenToEurAvg
/ the last Chainlink price
> collateralRate
, the protocol will incur bad debt after liquidation. Given that the current collateralRate
is 110% and the average period is 4h, it's a realistic scenario, especially for volatile assets like BTC, which could fall more than 20% within 4 hours.
Possible scenario (all calculations are simplified for illustration):
Falling market (prices provided to simplify calculations)
The last BTC price (PriceCalculator::tokenToEur
) 10,000 EUR/BTC.
Average BTC price (PriceCalculator::tokenToEurAvg
) 12,000 EUR/BTC.
User deposits 1 BTC.
User mints 10_900 EUROs (maximum considering collateralRate
).
Average price (PriceCalculator::tokenToEurAvg
) falls to 11_500 eur/btc after some time.
Liquidation is triggered.
In LiquidationPool::distributeAssets()
:
10_000 EUROs are repaid and burnt (using the last price)
Not considering dividing by collateralRate
in the calculation of costInEuros
due to another finding.
If it were taken into account, the burnt amount would be even less.
As a result, 10_900 EUROs were minted, but only 10_000 EUROs were burnt, leaving 900 EUROs as bad debt.
This discrepancy in price calculations during liquidation could lead to the protocol incurring bad debt.
Manual review
Use PriceCalculator
as the sole source of price data in all places
As Chainlink uses the VWAP algorithm (https://chain.link/education-hub/twap-vs-vwap) for price calculations, manual average calculation could be skipped, using only the latest price.
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.