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: 36/183
Findings: 6
Award: $327.20
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Maroutis
Also found by: 0x486776, 0xShitgem, 0xabhay, 0xleadwizard, 0xlemon, 0xnilay, 0xtankr, 3docSec, AM, Aamir, Abdessamed, Al-Qa-qa, AlexCzm, Circolors, CodeWasp, Daniel526, Egis_Security, Emmanuel, Giorgio, Honour, Hueber, Infect3d, Krace, KupiaSec, LeoGold, Limbooo, PoeAudits, SBSecurity, SpicyMeatball, T1MOH, The-Seraphs, TheSavageTeddy, TheSchnilch, Topmark, VAD37, ZanyBonzy, adam-idarrha, bhilare_, btk, carlitox477, cinderblock, dimulski, falconhoof, grearlake, gumgumzum, iamandreiski, itsabinashb, josephdara, ke1caM, kennedy1030, ljj, n0kto, n4nika, neocrao, oakcobalt, petro_1912, pontifex, poslednaya, shaflow2, shikhar229169, web3km, ych18, zhaojohnson, zigtur
0.2831 USDC - $0.28
https://github.com/code-423n4/2024-04-dyad/blob/main/script/deploy/Deploy.V2.s.sol#L62-L67 https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/VaultManagerV2.sol#L67-L78 https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/VaultManagerV2.sol#L80-L91 https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/VaultManagerV2.sol#L269-L286 https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/VaultManagerV2.sol#L250-L267
The total USD value of user's collateral can be calculated to be higher than the actual value, leading to dyad inflation.
KerosineManager
stores the list of non-kerosene vaults vaultLicenser
is intended to store the list of kerosene vaults.
https://github.com/code-423n4/2024-04-dyad/blob/main/script/deploy/Deploy.V2.s.sol#L62-L67
KerosineManager kerosineManager = new KerosineManager(); kerosineManager.add(address(ethVault)); kerosineManager.add(address(wstEth)); vaultManager.setKeroseneManager(kerosineManager); vaultLicenser.add(address(ethVault)); vaultLicenser.add(address(wstEth)); vaultLicenser.add(address(unboundedKerosineVault)); // vaultLicenser.add(address(boundedKerosineVault));
In VaultManagerV2
, however, the vaultsKerosene
is utilized to verify the licenses for kerosene vaults.(Line 88, 280)
https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/VaultManagerV2.sol#L80-L91
function addKerosene( uint id, address vault ) external isDNftOwner(id) { if (vaultsKerosene[id].length() >= MAX_VAULTS_KEROSENE) revert TooManyVaults(); L88 if (!keroseneManager.isLicensed(vault)) revert VaultNotLicensed(); if (!vaultsKerosene[id].add(vault)) revert VaultAlreadyAdded(); emit Added(id, vault); }
https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/VaultManagerV2.sol#L269-L286
function getKeroseneValue( uint id ) public view returns (uint) { uint totalUsdValue; uint numberOfVaults = vaultsKerosene[id].length(); for (uint i = 0; i < numberOfVaults; i++) { Vault vault = Vault(vaultsKerosene[id].at(i)); uint usdValue; L280 if (keroseneManager.isLicensed(address(vault))) { usdValue = vault.getUsdValue(id); } totalUsdValue += usdValue; } return totalUsdValue; }
So, users have the ability to add their non-kerosene vaults to their vaultsKerosene[id]
, resulting in the NonKeroseneValue
of the given ID being added twice. This increases the user's collateral ratio and leads to inflation of dyad.
Also, because unboundedKerosineVault
was added into vaultLicenser
in Deploy.V2.s.sol
, users can add their kerosineVault
to vaults[id]
. This may lead to similar issues.
https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/VaultManagerV2.sol#67-L78
function add( uint id, address vault ) external isDNftOwner(id) { if (vaults[id].length() >= MAX_VAULTS) revert TooManyVaults(); L75 if (!vaultLicenser.isLicensed(vault)) revert VaultNotLicensed(); if (!vaults[id].add(vault)) revert VaultAlreadyAdded(); emit Added(id, vault); }
https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/VaultManagerV2.sol#250-L267
function getNonKeroseneValue( uint id ) public view returns (uint) { uint totalUsdValue; uint numberOfVaults = vaults[id].length(); for (uint i = 0; i < numberOfVaults; i++) { Vault vault = Vault(vaults[id].at(i)); uint usdValue; L261 if (vaultLicenser.isLicensed(address(vault))) { usdValue = vault.getUsdValue(id); } totalUsdValue += usdValue; } return totalUsdValue; }
Manual review
A licenser for kerosene vaults should be used in place of KerosineManager
within VaultManagerV2
as follows. Additionally, I suggest using a more suitable name for KerosineManager
. I believe this name might cause confusion, as KerosineManager
actually stores the list of non-kerosene vaults.
https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/VaultManagerV2.sol
[...] - KerosineManager public keroseneManager; + Licenser public kerosineLicenser; [...] - function setKeroseneManager(KerosineManager _keroseneManager) + function setKeroseneLicenser(Licenser _kerosineLicenser) external initializer { - keroseneManager = _keroseneManager; + kerosineLicenser = _kerosineLicenser; } [...] - if (!keroseneManager.isLicensed(vault)) revert VaultNotLicensed(); + if (!kerosineLicenser.isLicensed(vault)) revert VaultNotLicensed(); [...] uint usdValue; - if (keroseneManager.isLicensed(address(vault))) { + if (kerosineLicenser.isLicensed(address(vault))) { usdValue = vault.getUsdValue(id); } [...]
https://github.com/code-423n4/2024-04-dyad/blob/main/script/deploy/Deploy.V2.s.sol
+ Licenser kerosineLicenser = new Licenser(); - vaultManager.setKeroseneManager(kerosineManager); + vaultManager.setKeroseneLicenser(kerosineLicenser); + kerosineLicenser.add(address(unboundedKerosineVault)); + // kerosineLicenser.add(address(boundedKerosineVault)); + kerosineLicenser.transferOwnership(MAINNET_OWNER); - vaultLicenser.add(address(unboundedKerosineVault));
Other
#0 - c4-pre-sort
2024-04-29T05:41:45Z
JustDravee marked the issue as duplicate of #966
#1 - c4-pre-sort
2024-04-29T08:37:46Z
JustDravee marked the issue as sufficient quality report
#2 - c4-judge
2024-05-04T09:46:31Z
koolexcrypto marked the issue as unsatisfactory: Invalid
#3 - c4-judge
2024-05-28T15:28:17Z
koolexcrypto marked the issue as duplicate of #1133
#4 - c4-judge
2024-05-29T14:02:57Z
koolexcrypto marked the issue as satisfactory
🌟 Selected for report: MrPotatoMagic
Also found by: 0x175, 0x486776, 0x77, 0xAkira, 0xAsen, 0xDemon, 0xabhay, 0xblack_bird, 0xlemon, 0xloscar01, 0xtankr, 3docSec, 4rdiii, Abdessamed, AlexCzm, Angry_Mustache_Man, BiasedMerc, Circolors, Cryptor, DMoore, DPS, DedOhWale, Dinesh11G, Dots, GalloDaSballo, Giorgio, Honour, Imp, Jorgect, Krace, KupiaSec, Mrxstrange, NentoR, Pechenite, PoeAudits, Ryonen, SBSecurity, Sabit, T1MOH, TheFabled, TheSavageTeddy, Tychai0s, VAD37, Vasquez, WildSniper, ZanyBonzy, adam-idarrha, alix40, asui, blutorque, btk, c0pp3rscr3w3r, caglankaan, carrotsmuggler, d_tony7470, dimulski, dinkras, djxploit, falconhoof, forgebyola, grearlake, imare, itsabinashb, josephdara, kartik_giri_47538, ke1caM, kennedy1030, koo, lionking927, ljj, niser93, pep7siup, poslednaya, ptsanev, sashik_eth, shaflow2, steadyman, turvy_fuzz, ubl4nk, valentin_s2304, web3km, xyz, y4y, zhaojohnson, zigtur
0.0234 USDC - $0.02
https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/VaultManagerV2.sol#L119-L153 https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/VaultManagerV2.sol#L94-L104 https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/VaultManagerV2.sol#L184-L202
Denial of service (DoS) during during withdraw()
, redeemDyad()
or remove()
.
Anyone can deposit assets for another user's id. However, deposits and withdrawals are prevented within the same block in all vaults.
https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/VaultManagerV2.sol#L119-L153
function deposit( uint id, address vault, uint amount ) external isValidDNft(id) { @> idToBlockOfLastDeposit[id] = block.number; [...] } function withdraw( uint id, address vault, uint amount, address to ) public isDNftOwner(id) { @> if (idToBlockOfLastDeposit[id] == block.number) revert DepositedInSameBlock(); [...] }
Consider the following scenario:
Additionally, redeemDyad or remove() would fail due to the 1 wei remaining in the vault.
https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/VaultManagerV2.sol#L94-L104
function remove( uint id, address vault ) external isDNftOwner(id) { @> if (Vault(vault).id2asset(id) > 0) revert VaultHasAssets(); if (!vaults[id].remove(vault)) revert VaultNotAdded(); emit Removed(id, vault); }
https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/VaultManagerV2.sol#L184-L202
function redeemDyad( uint id, address vault, uint amount, address to ) external isDNftOwner(id) returns (uint) { dyad.burn(id, msg.sender, amount); Vault _vault = Vault(vault); uint asset = amount * (10**(_vault.oracle().decimals() + _vault.asset().decimals())) / _vault.assetPrice() / 1e18; @> withdraw(id, vault, asset, to); emit RedeemDyad(id, vault, amount, to); return asset; }
Manual review
Only the owner of the ID should be granted the right to deposit for their own ID.
function deposit( uint id, address vault, uint amount ) external - isValidDNft(id) + isDNftOwner(id) { idToBlockOfLastDeposit[id] = block.number; [...] }
DoS
#0 - c4-pre-sort
2024-04-27T11:54:57Z
JustDravee marked the issue as duplicate of #489
#1 - c4-pre-sort
2024-04-29T09:32:41Z
JustDravee marked the issue as sufficient quality report
#2 - c4-judge
2024-05-05T20:39:25Z
koolexcrypto marked the issue as unsatisfactory: Invalid
#3 - c4-judge
2024-05-05T20:45:34Z
koolexcrypto marked the issue as unsatisfactory: Invalid
#4 - c4-judge
2024-05-05T21:47:05Z
koolexcrypto marked the issue as nullified
#5 - c4-judge
2024-05-05T21:47:13Z
koolexcrypto marked the issue as not nullified
#6 - c4-judge
2024-05-08T15:26:47Z
koolexcrypto marked the issue as duplicate of #1001
#7 - c4-judge
2024-05-11T19:49:26Z
koolexcrypto marked the issue as satisfactory
🌟 Selected for report: 0xAlix2
Also found by: 0xfox, 0xlemon, 0xnev, 3docSec, Aamir, Abdessamed, Dudex_2004, Egis_Security, Evo, FastChecker, Honour, Jorgect, KupiaSec, Limbooo, MrPotatoMagic, SpicyMeatball, TheSchnilch, alix40, bhilare_, favelanky, forgebyola, ke1caM, kennedy1030, koo, oakcobalt, petro_1912, shikhar229169
32.4128 USDC - $32.41
https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/VaultManagerV2.sol#L134-L153
When depositing, users can deposit into any vaults.
function deposit( uint id, address vault, uint amount ) external isValidDNft(id) { idToBlockOfLastDeposit[id] = block.number; Vault _vault = Vault(vault); _vault.asset().safeTransferFrom(msg.sender, address(vault), amount); _vault.deposit(id, amount); }
And, getNonKeroseneValue(id)
only calcualtes assets in the vaults which are added for the ID and are licensed in vaultLicenser
.
function getNonKeroseneValue( uint id ) public view returns (uint) { uint totalUsdValue; @> uint numberOfVaults = vaults[id].length(); for (uint i = 0; i < numberOfVaults; i++) { Vault vault = Vault(vaults[id].at(i)); uint usdValue; @> if (vaultLicenser.isLicensed(address(vault))) { usdValue = vault.getUsdValue(id); } totalUsdValue += usdValue; } return totalUsdValue; }
However, in the withdraw() function, the value of withdrawn assets is deducted regardless of whether the vault is Kerosene, added, or licensed.
function withdraw( uint id, address vault, uint amount, address to ) public isDNftOwner(id) { if (idToBlockOfLastDeposit[id] == block.number) revert DepositedInSameBlock(); uint dyadMinted = dyad.mintedDyad(address(this), id); Vault _vault = Vault(vault); @> uint value = amount * _vault.assetPrice() * 1e18 @> / 10**_vault.oracle().decimals() / 10**_vault.asset().decimals(); @> if (getNonKeroseneValue(id) - value < dyadMinted) revert NotEnoughExoCollat(); _vault.withdraw(id, to, amount); if (collatRatio(id) < MIN_COLLATERIZATION_RATIO) revert CrTooLow(); }
When the vault is Kerosene, this function will revert because Kerosene Vaults do not have an oracle()
function.
When the vault is unadded or unlicensed, the deduction is unfair because the asset in the vault was not added to the NonKeroseneValue
of the ID.
Manual review
Only the value of assets withdrawn from the non-Kerosene vaults added for the ID should be deducted in the collateral check.
function withdraw( uint id, address vault, uint amount, address to ) public isDNftOwner(id) { if (idToBlockOfLastDeposit[id] == block.number) revert DepositedInSameBlock(); uint dyadMinted = dyad.mintedDyad(address(this), id); Vault _vault = Vault(vault); + uint value; + if (vaults[id].contains(vault) && vaultLicenser.isLicensed(address(vault))) + { + value = amount * _vault.assetPrice() - uint value = amount * _vault.assetPrice() * 1e18 / 10**_vault.oracle().decimals() / 10**_vault.asset().decimals(); + } if (getNonKeroseneValue(id) - value < dyadMinted) revert NotEnoughExoCollat(); _vault.withdraw(id, to, amount); if (collatRatio(id) < MIN_COLLATERIZATION_RATIO) revert CrTooLow(); }
DoS
#0 - JustDravee
2024-04-26T21:28:19Z
Now now, this finding is a dup of 2 others, however it's mainly focused on the invalid check so I'll mark as dup with that for now
#1 - c4-pre-sort
2024-04-26T21:28:24Z
JustDravee marked the issue as duplicate of #397
#2 - c4-pre-sort
2024-04-29T08:48:11Z
JustDravee marked the issue as sufficient quality report
#3 - c4-judge
2024-05-11T19:22:14Z
koolexcrypto marked the issue as satisfactory
🌟 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/main/src/core/VaultManagerV2.sol#L205-L228
The lack of collateral and the decline of the DYAD value.
The value of DYAD is secured by non-Kerosene collateral, while the price of Kerosene assets can be manipulated by depositing a large amount of WETH. So, the USD value of non-Kerosene collateral should be bigger than the amount of DYAD minted. In the liquidate()
function, however, only the collateral ratio is checked.
function liquidate( uint id, uint to ) external isValidDNft(id) isValidDNft(to) { uint cr = collatRatio(id); @> if (cr >= MIN_COLLATERIZATION_RATIO) revert CrTooHigh(); dyad.burn(id, msg.sender, dyad.mintedDyad(address(this), id)); uint cappedCr = cr < 1e18 ? 1e18 : cr; uint liquidationEquityShare = (cappedCr - 1e18).mulWadDown(LIQUIDATION_REWARD); uint liquidationAssetShare = (liquidationEquityShare + 1e18).divWadDown(cappedCr); uint numberOfVaults = vaults[id].length(); for (uint i = 0; i < numberOfVaults; i++) { Vault vault = Vault(vaults[id].at(i)); uint collateral = vault.id2asset(id).mulWadUp(liquidationAssetShare); vault.move(id, to, collateral); } emit Liquidate(id, msg.sender, to); }
This make it possible for malicious users to delay liquidation. It also may lead the lack of collateral and the decline of the DYAD value. The main purpose is to build a stable coin, DYAD. The decline of the DYAD value means the failure of this protocol.
Manual review
The condition NonKeroseneValue >= mintedDyad
should be added in the liquidate()
function.
function liquidate( uint id, uint to ) external isValidDNft(id) isValidDNft(to) { uint cr = collatRatio(id); - if (cr >= MIN_COLLATERIZATION_RATIO) revert CrTooHigh(); + if (cr >= MIN_COLLATERIZATION_RATIO && getNonKeroseneValue(id) >= dyad.mintedDyad(address(this), id)) + revert NotLiquidatable(); dyad.burn(id, msg.sender, dyad.mintedDyad(address(this), id)); uint cappedCr = cr < 1e18 ? 1e18 : cr; uint liquidationEquityShare = (cappedCr - 1e18).mulWadDown(LIQUIDATION_REWARD); uint liquidationAssetShare = (liquidationEquityShare + 1e18).divWadDown(cappedCr); uint numberOfVaults = vaults[id].length(); for (uint i = 0; i < numberOfVaults; i++) { Vault vault = Vault(vaults[id].at(i)); uint collateral = vault.id2asset(id).mulWadUp(liquidationAssetShare); vault.move(id, to, collateral); } emit Liquidate(id, msg.sender, to); }
Other
#0 - c4-pre-sort
2024-04-28T10:22:34Z
JustDravee marked the issue as duplicate of #128
#1 - c4-pre-sort
2024-04-29T09:07:21Z
JustDravee marked the issue as sufficient quality report
#2 - c4-judge
2024-05-08T09:55:03Z
koolexcrypto marked the issue as not a duplicate
#3 - c4-judge
2024-05-08T09:55:11Z
koolexcrypto marked the issue as duplicate of #338
#4 - c4-judge
2024-05-11T12:20:33Z
koolexcrypto marked the issue as satisfactory
🌟 Selected for report: 0xAlix2
Also found by: 0x486776, 0xabhay, 0xlucky, 0xtankr, Abdessamed, Circolors, CodeWasp, DarkTower, Egis_Security, Giorgio, Infect3d, Krace, KupiaSec, Limbooo, Maroutis, NentoR, Ryonen, SpicyMeatball, T1MOH, TheFabled, TheSavageTeddy, TheSchnilch, VAD37, XDZIBECX, btk, carrotsmuggler, cu5t0mpeo, dimulski, gumgumzum, iamandreiski, imare, itsabinashb, ke1caM, kennedy1030, lian886, n4nika, oakcobalt, sashik_eth, shaflow2, steadyman, web3km, windhustler, zhaojohnson
3.8221 USDC - $3.82
https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/Vault.kerosine.unbounded.sol#L50-L68
All functions that call the assetPrice() function will always be reverted when the total collateral value is less than the total supply of DYAD.
The collatRatio()
will be reverted, which is the main function of this protocol. So liquidation, withdrawal, and redemption will also be reverted.
The tvl
could become less than dyad.totalSupply()
because of the decline of the ether value.
If the tvl
is less than dyad.totalSupply()
in the following code, assetPrice()
is reverted.
function assetPrice() public view override returns (uint) { uint tvl; address[] memory vaults = kerosineManager.getVaults(); uint numberOfVaults = vaults.length; for (uint i = 0; i < numberOfVaults; i++) { Vault vault = Vault(vaults[i]); tvl += vault.asset().balanceOf(address(vault)) * vault.assetPrice() * 1e18 / (10**vault.asset().decimals()) / (10**vault.oracle().decimals()); } @> uint numerator = tvl - dyad.totalSupply(); uint denominator = kerosineDenominator.denominator(); return numerator * 1e8 / denominator; }
The functions collatRatio()
, liquidate()
, withdraw()
and redeemDyad()
in VaultManagerV2 need the call of assetPrice()
. So all of them is reverted when tvl < dyad.totalSupply()
.
Manual review
function assetPrice() public view override returns (uint) { uint tvl; address[] memory vaults = kerosineManager.getVaults(); uint numberOfVaults = vaults.length; for (uint i = 0; i < numberOfVaults; i++) { Vault vault = Vault(vaults[i]); tvl += vault.asset().balanceOf(address(vault)) * vault.assetPrice() * 1e18 / (10**vault.asset().decimals()) / (10**vault.oracle().decimals()); } + if (tvl <= dyad.totalSupply()) return 0; uint numerator = tvl - dyad.totalSupply(); uint denominator = kerosineDenominator.denominator(); return numerator * 1e8 / denominator; }
Other
#0 - JustDravee
2024-04-28T19:34:45Z
Maybe it's better that it reverts actually Still linking to 224
#1 - c4-pre-sort
2024-04-28T19:34:52Z
JustDravee marked the issue as duplicate of #224
#2 - c4-pre-sort
2024-04-29T09:04:16Z
JustDravee marked the issue as sufficient quality report
#3 - c4-judge
2024-05-08T08:31:51Z
koolexcrypto marked the issue as duplicate of #308
#4 - c4-judge
2024-05-11T20:08:31Z
koolexcrypto marked the issue as satisfactory
🌟 Selected for report: 0xAlix2
Also found by: 0x175, 0x486776, 0xnev, 3docSec, 3th, Aamir, Abdessamed, AlexCzm, Angry_Mustache_Man, Circolors, DedOhWale, Emmanuel, Giorgio, Honour, Jorgect, KupiaSec, Maroutis, Myrault, SBSecurity, Stefanov, T1MOH, VAD37, Vasquez, adam-idarrha, alix40, ducanh2706, falconhoof, iamandreiski, ke1caM, kennedy1030, koo, lian886, ljj, miaowu, pontifex, sashik_eth, shikhar229169, vahdrak1
7.3026 USDC - $7.30
https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/VaultManagerV2.sol#L230-L286 https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/VaultManagerV2.sol#L205-L228
Liquidators may take fewer assets than expected during liquidation.
The collatRatio
of the ID is calculated by dividing the TotalUsdValue
of the ID by the total mintedDyad
.
function collatRatio( uint id ) public view returns (uint) { uint _dyad = dyad.mintedDyad(address(this), id); if (_dyad == 0) return type(uint).max; @> return getTotalUsdValue(id).divWadDown(_dyad); }
The TotalUsdValue
of the ID is the sum of the NonKeroseneValue
and the KeroseneValue
for the ID.
function getTotalUsdValue( uint id ) public view returns (uint) { @> return getNonKeroseneValue(id) + getKeroseneValue(id); }
The NonKeroseneValue
is the value of the NonKerosene
assets deposited for the given ID. And KeroseneValue
is the value of Kerosene
assets.
function getNonKeroseneValue( uint id ) public view returns (uint) { uint totalUsdValue; uint numberOfVaults = vaults[id].length(); for (uint i = 0; i < numberOfVaults; i++) { Vault vault = Vault(vaults[id].at(i)); uint usdValue; if (vaultLicenser.isLicensed(address(vault))) { usdValue = vault.getUsdValue(id); } totalUsdValue += usdValue; } return totalUsdValue; } function getKeroseneValue( uint id ) public view returns (uint) { uint totalUsdValue; uint numberOfVaults = vaultsKerosene[id].length(); for (uint i = 0; i < numberOfVaults; i++) { Vault vault = Vault(vaultsKerosene[id].at(i)); uint usdValue; if (keroseneManager.isLicensed(address(vault))) { usdValue = vault.getUsdValue(id); } totalUsdValue += usdValue; } return totalUsdValue; }
In VaultManagerV2.liquidate()
, however, only Kerosene
assets are moved from liquidatee to liquidator.
function liquidate( uint id, uint to ) external isValidDNft(id) isValidDNft(to) { uint cr = collatRatio(id); if (cr >= MIN_COLLATERIZATION_RATIO) revert CrTooHigh(); dyad.burn(id, msg.sender, dyad.mintedDyad(address(this), id)); @> uint cappedCr = cr < 1e18 ? 1e18 : cr; @> uint liquidationEquityShare = (cappedCr - 1e18).mulWadDown(LIQUIDATION_REWARD); @> uint liquidationAssetShare = (liquidationEquityShare + 1e18).divWadDown(cappedCr); @> uint numberOfVaults = vaults[id].length(); for (uint i = 0; i < numberOfVaults; i++) { Vault vault = Vault(vaults[id].at(i)); uint collateral = vault.id2asset(id).mulWadUp(liquidationAssetShare); vault.move(id, to, collateral); } emit Liquidate(id, msg.sender, to); }
This is unfair to the liquidator. Consider the following scenario:
Manual review
Kerosene assets should be moved during liquidation.
function liquidate( uint id, uint to ) external isValidDNft(id) isValidDNft(to) { uint cr = collatRatio(id); if (cr >= MIN_COLLATERIZATION_RATIO) revert CrTooHigh(); dyad.burn(id, msg.sender, dyad.mintedDyad(address(this), id)); uint cappedCr = cr < 1e18 ? 1e18 : cr; uint liquidationEquityShare = (cappedCr - 1e18).mulWadDown(LIQUIDATION_REWARD); uint liquidationAssetShare = (liquidationEquityShare + 1e18).divWadDown(cappedCr); uint numberOfVaults = vaults[id].length(); for (uint i = 0; i < numberOfVaults; i++) { Vault vault = Vault(vaults[id].at(i)); uint collateral = vault.id2asset(id).mulWadUp(liquidationAssetShare); vault.move(id, to, collateral); } + numberOfVaults = vaultsKerosene[id].length(); + for (uint i = 0; i < numberOfVaults; i++) { + Vault vault = Vault(vaultsKerosene[id].at(i)); + uint collateral = vault.id2asset(id).mulWadUp(liquidationAssetShare); + vault.move(id, to, collateral); + } emit Liquidate(id, msg.sender, to); }
I recommend modifying the liquidate() function to include additional parameters that allow liquidators to choose the ratio between NonKerosene and Kerosene they desire.
Other
#0 - c4-pre-sort
2024-04-28T10:25:14Z
JustDravee marked the issue as duplicate of #128
#1 - c4-pre-sort
2024-04-29T09:07:16Z
JustDravee marked the issue as sufficient quality report
#2 - c4-judge
2024-05-11T19:42:53Z
koolexcrypto marked the issue as satisfactory