When price of accepted token drop gradually for a long time, attack can abusing it to make profit by abusing difference between real price and price returned from function
For checking value of token list in the vault, function euroCollateral()
use average price of token in 4 hours to calculate price:
function euroCollateral() private view returns (uint256 euros) {
ITokenManager.Token[] memory acceptedTokens = getTokenManager().getAcceptedTokens();
for (uint256 i = 0; i < acceptedTokens.length; i++) {
ITokenManager.Token memory token = acceptedTokens[i];
euros += calculator.tokenToEurAvg(token, getAssetBalance(token.symbol, token.addr)); // <---
}
}
Function tokenToEurAvg()
:
function tokenToEurAvg(ITokenManager.Token memory _token, uint256 _tokenValue) external view returns (uint256) {
Chainlink.AggregatorV3Interface tokenUsdClFeed = Chainlink.AggregatorV3Interface(_token.clAddr);
uint256 scaledCollateral = _tokenValue * 10 ** getTokenScaleDiff(_token.symbol, _token.addr);
uint256 collateralUsd = scaledCollateral * avgPrice(4, tokenUsdClFeed); // <---- using avgPrice() function with average price = 4 hours
(, int256 eurUsdPrice,,,) = clEurUsd.latestRoundData();
return collateralUsd / uint256(eurUsdPrice);
}
Consider scenario for attacker to exploit:
Price of WBTC gradually reduce from 40000
to 30000
in 4 hours, price returned from function tokenToEurAvg()
is 35000
Attacker deposit WBTC to the vault, minting token, get profit by price differences and leaving the vault under collateralized later
The profit will depend on collateralRate
and minting fee, but with current settings of variable (collateral rate ~ 120%, minting fee ~ 0-5%), this exploit can be used in good condition
function mint()
:
function mint(address _to, uint256 _amount) external onlyOwner ifNotLiquidated {
uint256 fee = _amount * ISmartVaultManagerV3(manager).mintFeeRate() / ISmartVaultManagerV3(manager).HUNDRED_PC();
require(fullyCollateralised(_amount + fee), UNDER_COLL); // <--- using `fullyCollateralised()` to check
minted = minted + _amount + fee;
EUROs.mint(_to, _amount);
EUROs.mint(ISmartVaultManagerV3(manager).protocol(), fee);
emit EUROsMinted(_to, _amount, fee);
}
function fullyCollateralised()
:
function fullyCollateralised(uint256 _amount) private view returns (bool) {
return minted + _amount <= maxMintable(); // <--- using maxMintable() to check
}
function maxMintable()
:
function maxMintable() private view returns (uint256) {
return euroCollateral() * ISmartVaultManagerV3(manager).HUNDRED_PC() / ISmartVaultManagerV3(manager).collateralRate(); // <-- using euroCollateral() to check, which using average price, not actual price
}
Attacker can get profit by mint more EUROs token than he should, which later can break peg stability of the token because value of collateral is not equal to total token minted.
Manual review
Using tokenToEur()
function to calculate total value of token in the vault instead of tokenToEurAvg()
, and settings minting fee higher than deviation threshold of the chainlink feed to make attack non profitable
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.