Platform: Code4rena
Start Date: 18/04/2024
Pot Size: $36,500 USDC
Total HM: 19
Participants: 183
Period: 7 days
Judge: Koolex
Id: 367
League: ETH
Rank: 28/183
Findings: 2
Award: $405.81
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: shikhar229169
Also found by: 0x486776, 0xSecuri, 0xfox, 3th, Circolors, Honour, KupiaSec, Maroutis, Sancybars, Stormreckson, Strausses, ke1caM, kennedy1030
283.3687 USDC - $283.37
https://github.com/code-423n4/2024-04-dyad/blob/cd48c684a58158de444b24854ffd8f07d046c31b/src/core/Vault.kerosine.bounded.sol#L44-L50 https://github.com/code-423n4/2024-04-dyad/blob/cd48c684a58158de444b24854ffd8f07d046c31b/src/core/VaultManagerV2.sol#L205-L228 https://github.com/code-423n4/2024-04-dyad/blob/cd48c684a58158de444b24854ffd8f07d046c31b/src/core/VaultManagerV2.sol#L230
The project will have a position in which collateral will be less than issued tokens, and the project will be not able to liquidate the position.
As a result of POC there will be $2625 - 20%(liquidator) = $2100 collateral for 3000 Dyad tokens
! To make this explanation easier, we will ignore decimals for now.
Lets start with mechanism of how liquidation work.
In order to liquidate someone by calling liquidate
function in VaultManagerV2.sol
, the function checks if collatRatio
is lower than 150%, by calling getTotalUsdValue()
, in other words by combining getNonKeroseneValue(id) + getKeroseneValue(id)
.
getKeroseneValue(id)
will call each KeroseneValue
and get current assetPrice()
.
Meanwhile in BoundedKerosineVault
assetPrice()
function will returns unboundedKerosineVault.assetPrice() * 2;
so 750$ worth of kerosine will be counted as 1500$
Lets talk about a scenario, when the user deposited 1 ethereum ($3000.01), and $750 worth of kerosine
token in to BoundedKerosineVault
.
So current formula for getting getTotalUsdValue()
is $3000.01 + $750.
User will call mintDyad
function, and asks for 3000 of Dyad
tokens ~= $3000,
getNonKeroseneValue
will return $3000.01, which is more than 3000, and then collatRatio
will give a number 150%, which will pass the MIN_COLLATERIZATION_RATIO
check.
! To mention, here I am using such a numbers that barely pass the check, to show the idea of an exploit, the same idea will work with any other number's combination.
So current state of the contract after issuing 3000 Dyad tokens.
Eth price - $3000.01 per ether.
User put $750 of Kerosine tokens into BoundedKerosineVault
.
$3000.01(1eth) + $750(kerosine)*2(bounded price math) = $4500 = 150% of a collateral ration to 3000 Dyad 100% + 25%*2 = 150%.
So in case of liquidation, 100%(eth) + 25%(without bounded feature) - 20%(liquidator) = 125% - 20% = 105% left, worth of collateral.
After some time, ETH price drops to $1500. Users have been doing small deposits of kerosine while eth price was going down to keep the collateral ratio above 150%.
The current collateral is $1500(1eth) + $1500(kerosine)*2 = $4500 50%(eth) + 50%(kerosine)*2 = 150% collateral ratio But the real collateral of a position (without bounded feature) is 50%(eth) + 50%(kerosine) = 100% - 20%(liquidator fee) = 80% of collateral. So project already has a bad position. collateral will costs less that project issued in the beginning. And project will not be able to liquidate this position, because user will add more and more kerosine tokens, to keep the ratio above 150%.
After some more time, ETH price will drop to $750. User still will deposit more and more kerosine tokens, to not be liquidated. $750(eth) + 1875$(kerosine)*2 = $4500 25%(eth) + 62.5%(kerosine)*2 = 150% Current REAL collateral is $750+$1875 = $2625 for 3000 Dyad token. Current REAL ratio is 25(eth)%+62.5(kerosine)% = 87.5% - %20(liquidator) = 67.5%
Manual review
Be more accurate with BoundedKerosineVault
feature, and rewrite the code, otherwise token will gain more and more bad positions.
Math
#0 - c4-pre-sort
2024-04-29T06:12:04Z
JustDravee marked the issue as duplicate of #456
#1 - c4-pre-sort
2024-04-29T09:31:22Z
JustDravee marked the issue as sufficient quality report
#2 - c4-judge
2024-05-12T08:59:39Z
koolexcrypto marked the issue as not a duplicate
#3 - c4-judge
2024-05-12T08:59:57Z
koolexcrypto marked the issue as duplicate of #338
#4 - c4-judge
2024-05-12T09:20:02Z
koolexcrypto marked the issue as satisfactory
🌟 Selected for report: Circolors
Also found by: 0xtankr, AamirMK, Al-Qa-qa, FastChecker, Infect3d, SBSecurity, Strausses, T1MOH, VAD37, carrotsmuggler, gumgumzum
122.4433 USDC - $122.44
Missing to set a setting while deployment
Vault.kerosine.bounded.sol
has the setUnboundedKerosineVault
function, which sets a source of an assetPrice
variable.
This function should be called while contracts initializations
Manual review
BoundedKerosineVault boundedKerosineVault = new BoundedKerosineVault( vaultManager, Kerosine(MAINNET_KEROSENE), kerosineManager ); + boundedKerosineVault.setUnboundedKerosineVault(unboundedKerosineVault); KerosineDenominator kerosineDenominator = new KerosineDenominator( Kerosine(MAINNET_KEROSENE) );
Error
#0 - c4-pre-sort
2024-04-29T07:03:26Z
JustDravee marked the issue as duplicate of #829
#1 - c4-pre-sort
2024-04-29T09:22:46Z
JustDravee marked the issue as sufficient quality report
#2 - c4-judge
2024-05-05T10:52:11Z
koolexcrypto marked the issue as unsatisfactory: Invalid
#3 - c4-judge
2024-05-29T12:33:34Z
koolexcrypto marked the issue as satisfactory