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: 19/183
Findings: 6
Award: $500.57
🌟 Selected for report: 1
🚀 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.368 USDC - $0.37
https://github.com/code-423n4/2024-04-dyad/blob/cd48c684a58158de444b24854ffd8f07d046c31b/script/deploy/Deploy.V2.s.sol#L64-L65 https://github.com/code-423n4/2024-04-dyad/blob/cd48c684a58158de444b24854ffd8f07d046c31b/script/deploy/Deploy.V2.s.sol#L95-L96
Due to a design flaw in the protocol's management of vault licensing and the deploy script, a significant risk exists where collateral can be counted twice in the calculation of the Collateralization Ratio. This occurs because WETH vaults are incorrectly licensed to both the KeroseneManager
and VaultLicenser
, allowing users to register the same asset and their NFT id in both Kerosene vaults and normal vaults. This can allow users to exploit this to greatly inflate their CR calculations, misleading the protocol into considering a position more secure than it truly is, which can prevent necessary liquidations and pose significant risk to the protocol.
Moroever, a user can also register his ID and the keroseneVault
as a normal vault
because the script calls the licensing function for the kerosineVaults
using the VaultLicenser
rather than the kerosineManager
. This can lead to positions entirely collateralized with kerosene token. Which is not what protocol intends to do and is very risky as the kerosene token is endogenous and has a manipulable asset price.
Basically, there are two exploits with devastating impacts on the protocol :
DeployV2
script calls both contract licensing tx functions on the weth vault.kerosineManager.add(address(ethVault)); kerosineManager.add(address(wstEth));
And
vaultLicenser.add(address(ethVault)); vaultLicenser.add(address(wstEth));
The licensing via the kerosineManager
also has to be done, because the weth vaults will be used by the UnboundedKerosineVault
contract during the price calculation of the kerosine asset.
This can be exploited where users can call both VaultManagerV2::addKerosene
and VaultManagerV2::add
functions with their id and the weth vault as parameters. The VaultManagerV2::collatRatio
uses both vaults
and vaultsKerosene
mapping to calculate the value of the stored assets. Since, weth vault is added in both mappings the assets be counted twice.
Consider the following test :
function test_CanMintSameAmountAsDeposit() public { // address RECEIVER2 = makeAddr("Receiver2"); uint256 id = mintDNft(); uint256 id2 = mintDNft(); // Add vault in both contracts vaultManagerV2.add(id, address(wethVaultV2)); vaultManagerV2.add(id2, address(wethVaultV2)); vaultManagerV2.addKerosene(id, address(wethVaultV2)); // Deposits 1e25 USD of Weth depositV2(weth, id, address(wethVaultV2), 1e22);// Price weth 1000 // Mint 1e25 vaultManagerV2.mintDyad(id, 1e25, RECEIVER); // Protocol considers that User has deposited twice the amount in the collateral ratio calculation console.log("CR of position", vaultManagerV2.collatRatio(id)); // 200% // Position is not liquidatable even if it is only collateralized at 100% vm.expectRevert(IVaultManager.CrTooHigh.selector); vm.prank(RECEIVER); vaultManagerV2.liquidate(id, id2); }
DeployV2
script calls the licensing tx functions on the kerosine vaults using the vaultLicenser
:vaultLicenser.add(address(unboundedKerosineVault)); // vaultLicenser.add(address(boundedKerosineVault));
This has to be done via the vaultLicenser
instead of the kerosineManager
so that the kerosene vault assets would not be taken into account during the price calculation of the kerosine asset.
A user can just call the VaultManagerV2::add
function with their ID and kerosine vault as parameters. Since, the kerosine vault will be stored inside the vaults
mappings, some positions can be mostly or entirely collateralized with the kerosine asset, which is not what is intended by the protocol. In fact kerosene is a volatile asset and can cause stability and liquidity issues to the protocol.
Consider the following test :
function test_addKeroseneAsExoColl() public { uint256 id = mintDNft(); uint256 id2 = mintDNft(); // Follow script deployement. Weth Vault is licensed in both VaultManager and KerosineManager // A user can just add his id and the WethVault in the kerosine mapping and kerosineVault in the vault mapping vaultManagerV2.addKerosene(id, address(wethVaultV2)); vaultManagerV2.add(id, address(unboundedKerosineVault)); // Assume weth was deposited by other users depositV2(weth, id2, address(wethVaultV2), 1e24); //weth 1000 Usd // User deposits kerosine using id kerosineMock.mint(address(this), 1e20); kerosineMock.approve(address(vaultManagerV2), 1e20); vaultManagerV2.deposit(id, address(unboundedKerosineVault), 1e20); console.log("Kerosine price", unboundedKerosineVault.assetPrice()); //9999 //Then mint dyad vaultManagerV2.mintDyad(id, 1e19, RECEIVER); // => Position 150% collateralized with kerosine tokens // !! User cannot add kerosine bounded or unbounded vaults in the kerosine mapping in the vault Manager // !! and id and weth vault can be added in both kerosene and normal vaults which would make the amount deposited calculated twice in the collateralRatio }
Manual review and foundry test
The design of the licensing part needs to be rethinked. The issue here is that the vaults mapping of the KerosineManager
contract which is constructed via the method KerosineManager::add
is the same mapping that is used by the UnboundedKerosineVault::assetPrice
function. You can consider creating two separate mappings. One used only for the price calculation in the UnboundedKerosineVaultassetPrice
contract which would only include the classic vaults (weth ...). And another mapping used for the licensing part which would include the kerosene vaults.
Let's assume these were implemented, we have now two mappings. The DeployV2
should change as follow :
kerosineManager.addVaultForOracleCalculation(address(ethVault)); kerosineManager.addVaultForOracleCalculation(address(wstEth)); kerosineManager.add(address(unboundedKerosineVault)); kerosineManager.add(address(boundedKerosineVault));
Assuming the addVaultForOracleCalculation
feeds a mapping that will be used by UnboundedKerosineVault::assetPrice
while add
doesn't.
Other
#0 - c4-pre-sort
2024-04-28T07:02:27Z
JustDravee marked the issue as duplicate of #966
#1 - c4-pre-sort
2024-04-29T08:37:06Z
JustDravee marked the issue as sufficient quality report
#2 - c4-judge
2024-05-04T09:46:21Z
koolexcrypto marked the issue as unsatisfactory: Invalid
#3 - Maroutis
2024-05-16T22:16:57Z
I don't understand how this is not a problem from the sponsor's perspective. This issue clearly explains why the current design leads to two huge problems for the protocol.
The assuption made by #966 is wrong because the script is correct and the double licensing is necessary. The root cause is in the design due to how the vaults are added in the keroseneManager (for this reason I dont think this should be a dup of that issue).
Unlike #966, this submission clearly explains that the current deployment is not faulty as it is necessary to have the double licensing, due to how the contracts are designed. However, it leads to two major issues (check Proof of Concept). The first POC highlights the fact that double counting of collateral is possible due to design.
The second POC highlights the fact that a position could be 100% collateralized with Kerosene asset which goes against the invariant of the protocol. This second part is also mentioned by #872 and #679.
The mitigation given by #966 is wrong also, because not licensing normal vaults via KeroseneManager
will not allow UnboundedKerosineVault
to use these vaults for the price calculations of the kerosene asset. The logical solution here is to slightly alter the design which is what was proposed in this submission.
#4 - koolexcrypto
2024-05-24T07:01:35Z
Hi @Maroutis
Thank you for your detailed feedback.
I agree, the deployment script has no problem. The issue is in the validations in general.
Will revisit this in the end to evaluate all at once
#5 - Maroutis
2024-05-24T09:32:20Z
Hi @koolexcrypto, since you mentioned issues being split in two. I believe this one should be split into two issues. As described in this submission, here are both impacts mentioned (with their relative POC):
The issue #872 that you just validated show the second impact. While the first impact is mentioned by issues that this submission was first duped with.
Thus, this submission should be split and each half duped with the correct issue (probably with #872 for the second part and #966 for the first part or another issue that has the correct root cause).
#6 - c4-judge
2024-05-28T14:43:51Z
koolexcrypto marked the issue as not a duplicate
#7 - c4-judge
2024-05-28T14:44:37Z
koolexcrypto removed the grade
#8 - c4-judge
2024-05-28T14:44:43Z
koolexcrypto marked the issue as primary issue
#9 - c4-judge
2024-05-28T14:44:47Z
koolexcrypto marked the issue as satisfactory
#10 - koolexcrypto
2024-05-28T14:58:27Z
Hi @Maroutis
Thank you for you feedback.
The quality of this report suffices to be selected as a report. It describes impact that are not described by other issues. Will consider partial credits for issues that are not clearly describing the same impact here.
since you mentioned issues being split in two
This was for issues (judged already) that were combined by some wardens, so splitting it was necessary, it was not re-judged.
#11 - c4-judge
2024-05-28T14:58:48Z
koolexcrypto marked the issue as selected for report
#12 - thebrittfactor
2024-06-17T21:44:32Z
For transparency, the DYAD team (shafu) acknowledged this finding outside of github. The appropriate sponsor labeling has been added on their behalf.
🌟 Selected for report: MrPotatoMagic
Also found by: 0xtankr, ArmedGoose, Egis_Security, Giorgio, KYP, Maroutis, NentoR, OMEN, Sabit, Shubham, SpicyMeatball, T1MOH, d3e4, dimulski, peanuts
200.8376 USDC - $200.84
https://github.com/code-423n4/2024-04-dyad/blob/cd48c684a58158de444b24854ffd8f07d046c31b/src/core/VaultManagerV2.sol#L215 https://github.com/code-423n4/2024-04-dyad/blob/cd48c684a58158de444b24854ffd8f07d046c31b/src/core/VaultManagerV2.sol#L224-L225
Two similar but different issues are being discussed in this finding. The first one is that the protocol allows users to mint Dyad without any lower limit. Small positions like this can accumulate fast. And the second one is that liquidators will not engage in positions where the usd collateral value is smaller or close the the dyad loan value + any gas costs. The latter can happen frequently since a liquidator cannot chose to liquidate a specific amount but the whole position only. Also, the second issue concerns any position not just smaller ones. Unliquidatable positions like these can quickly accumulate and create a liquidity crisis event.
This makes the impact high and the likelihood of the event medium to high as well.
A position could exist with a very low collateral value. This user is undercollateralized and must be liquidated in order to ensure that the protocol remains overcollateralized. If a liquidator wishes to liquidate this user, they will first need to stake some collateral (weth or other) which involves gas cost. Because the value of the collateral is so low, after gas costs, liquidators will not make a profit. In the end these low value vaults will never get liquidated, leaving the protocol with bad debt. Also malicious users could knowingly creates multiple small positions in order to attack the protocol and depeg the Dyad token.
The function liquidate
expects the liquidator to fully liquidate a position, even if it's not economically viable. The absence of partial liquidation and loan interests could deter liquidators from engaging with less profitable positions. This particular issue regards all sizes small, medium and larges positions. An accumulation of these bad debts can actually trigger a liquidity crisis.
In both of these cases, the rule is simple, liquidators will only liquidate a position if :
vaultsAssets * liquidationAssetShare > mintedDyadLoan + Gas costs
By default any position with a CR=100%
or less will not be liquidated.
Not having the flexibity of partially liquidating a position so that a non profitable position becomes profitable increases the risk of bad debt.
Similarly, not having any interest payments also participates in the lack of incentives. As increasing interests worsens CR
and increases incentives.
Manual review
Consider implementing the following changes to significantly minimize bad debt :
Dyad
Other
#0 - thebrittfactor
2024-05-29T13:40:49Z
For transparency, the judge has requested that issue #891 be duplicated, as it contains two issues they deemed should be judged separately.
#1 - c4-judge
2024-05-29T13:51:38Z
koolexcrypto marked the issue as duplicate of #1097
#2 - koolexcrypto
2024-05-29T13:52:00Z
split from #891
#3 - c4-judge
2024-05-29T13:52:03Z
koolexcrypto marked the issue as satisfactory
#4 - c4-judge
2024-05-29T13:55:02Z
koolexcrypto changed the severity to 3 (High Risk)
🌟 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
The liquidation function only checks if the collateral ratio (CR) is greater than 150%, without validating if the non-Kerosene value remains at or above 100%. This oversight could allow scenarios where excessive endogenous (Kerosene) >50% collateralization
is used.
This goes against the protocol system as both mintDyad
and withdraw
functions have this check.
Secondly, having more kerosene collateral is more risky for the protocol as the kerosene asset can be considered slightly endougenous due to it's dependence to Dyad
token in the calculation of it's asset price and the rewarding system.
Lastly, a liquidator can find himself stuck and not being able to withdraw or mint because the kerosene tokens makes up more than 50% of his collateral if he liquidates a position that has a significant kerosene size.
The liquidate function calculates and checks the collateral ratio using the total value:
uint256 cr = collatRatio(id); if (cr >= MIN_COLLATERIZATION_RATIO) revert CrTooHigh();
In both mintDyad
and withdraw
functions, the exogenous collateral is checked to be 100%
.
if (getNonKeroseneValue(id) < newDyadMinted) revert NotEnoughExoCollat();
And
if (getNonKeroseneValue(id) - value < dyadMinted) revert NotEnoughExoCollat();
This is also mentioned in the docs :
These equations ensure that Notes can only withdraw non-Kerosene collateral and/ or mint more DYAD if there will be at least $1 of non-Kerosene collateral per DYAD minted in their Note upon the successful execution of the mint or withdrawal transaction.
Not having a check can break this invariant. A position can have a healthy CR
(>=150%
collateralized) but an exogenous collateral less than 100% of the debt. Having a signficant portion of kerosene tokens as collateral >50%
can be problematic due to its dependence to the Dyad token and especially in case where the asset price of the kerosene token is manipulated.
Manual review
Consider adapting the check at the top of the liquidate
function as follow :
uint256 cr = collatRatio(id); if (cr >= MIN_COLLATERIZATION_RATIO && getNonKeroseneValue(id) >= dyad.mintedDyad(address(this), id)) revert CrTooHigh();
This will make the position liquidatable if exogenous collateral is less than 100% or in case where cr < 150%.
Other
#0 - c4-pre-sort
2024-04-28T10:20:23Z
JustDravee marked the issue as duplicate of #128
#1 - c4-pre-sort
2024-04-29T09:03:30Z
JustDravee marked the issue as sufficient quality report
#2 - c4-judge
2024-05-08T09:42:44Z
koolexcrypto marked the issue as not a duplicate
#3 - c4-judge
2024-05-08T09:43:10Z
koolexcrypto marked the issue as duplicate of #338
#4 - c4-judge
2024-05-11T12:20:12Z
koolexcrypto marked the issue as satisfactory
#5 - c4-judge
2024-05-13T18:36:50Z
koolexcrypto changed the severity to 3 (High Risk)
🌟 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
The UnboundedKerosineVault::assetPrice()
function calculates the price of Kerosine based TVL in the protocol's vaults and the total supply of Dyad. An underflow can occur if the TVL falls below the total Dyad supply, resulting in a negative numerator in the price calculation. This scenario is particularly likely if the price of collateral assets such as WETH decreases sharply, or if Kerosine itself is used as collateral in significant amounts. Such an underflow prevents withdrawals of assets, the minting of new Dyad tokens and the liquidation of undercollateralized positions, which effectively traps users' assets.
Many scenarios can cause a sudden fall in TVL :
The following test scenario illustrates how an underflow can occur in the UnboundedKerosineVault::assetPrice()
function when oracle price of weth asset falls:
function test_KerosineAssetPriceUnderflow() public { uint256 id = mintDNft(); uint256 id2 = mintDNft(); // Follow script deployement. Weth Vault is licensed in both VaultManager and KerosineManager vaultManagerV2.add(id, address(unboundedKerosineVault)); vaultManagerV2.add(id, address(wethVaultV2)); // User deposits weth depositV2(weth, id, address(wethVaultV2), 1e22); //weth 1000 Usd // User deposits kerosine using id kerosineMock.mint(address(this), 1e20); kerosineMock.approve(address(vaultManagerV2), 1e20); vaultManagerV2.deposit(id, address(unboundedKerosineVault), 1e20); console.log("Kerosine price", unboundedKerosineVault.assetPrice()); //9999 //Then mint dyad vaultManagerV2.mintDyad(id, 1e24, RECEIVER); // Assume price decreases wethOracle.setPrice(99e8); // We now have tvl < mintedDyad. Many reasons can cause this. // 1. The protocol forces the users to have 100% in exo only. If price falls then so the tvl // 2. since kerosene vaults can be added directly into the vaults mappings (rather than kerosine vaults). Some positions will be 100% collateralized by kerosine. // Position cannot be liquidated nor can the user withdraw his collateral. // This can happen even if position is not liquidatable. vm.expectRevert(stdError.arithmeticError); vm.prank(RECEIVER); vaultManagerV2.liquidate(id, id2); }
In this scenario, the drop in WETH price leads to a TVL that is less than the Dyad issued, causing an arithmetic underflow when calculating the Kerosine price, which in turn prevents liquidation, withdrawals and mints.
Manual review and foundry test suite
UnboundedKerosineVault::assetPrice()
function to ensure that the numerator cannot go negative. Implementing a minimum floor for the asset price can prevent underflows.Under/Overflow
#0 - c4-pre-sort
2024-04-27T18:17:48Z
JustDravee marked the issue as duplicate of #958
#1 - c4-pre-sort
2024-04-29T08:39:32Z
JustDravee marked the issue as sufficient quality report
#2 - c4-judge
2024-05-05T13:48:47Z
koolexcrypto marked the issue as duplicate of #308
#3 - c4-judge
2024-05-11T20:10:04Z
koolexcrypto marked the issue as satisfactory
#4 - c4-judge
2024-05-13T18:34:03Z
koolexcrypto changed the severity to 3 (High Risk)
🌟 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
A liquidatable position is determined by the collatRatio
which is calculated using both vaults assets and kerosineVaults assets. A liquidator determines if a liquidation is profitable for him by calculating the collatRatio
determined using the whole USD
value of a position. However, the liquidation function does not transfer Kerosene-based vault assets to the liquidator, leading to potential losses for liquidators and diminishing the incentives of performing liquidations.
First, the VaultManagerV2::liquidate
function determines if a position is liquidatable :
uint256 cr = collatRatio(id); if (cr >= MIN_COLLATERIZATION_RATIO) revert CrTooHigh();
What's important to note here is that the collatRatio
is calculated using the getTotalUsdValue
which is the sum of kerosene and nonKerosene assets getNonKeroseneValue(id) + getKeroseneValue(id)
. getNonKeroseneValue
function uses the vaults
mapping while getKeroseneValue
uses the vaultsKerosene
.
However, when calculating the amount to be awarded to the liquidator, only a portion (or the whole position depending on the cr) of the vaults
mapping assets, thus the non kerosine assets are transfered:
uint256 numberOfVaults = vaults[id].length(); for (uint256 i = 0; i < numberOfVaults; i++) { Vault vault = Vault(vaults[id].at(i)); uint256 collateral = vault.id2asset(id).mulWadUp(liquidationAssetShare); vault.move(id, to, collateral); }
According to the implemention and sponsors, users can push down the explicit exogenous CR to 100% :
if (getNonKeroseneValue(id) < newDyadMinted) revert NotEnoughExoCollat();
And cover the rest with Kerosene :
if (collatRatio(id) < MIN_COLLATERIZATION_RATIO) revert CrTooLow();
This means that for a liquidatable position with a CR of 1.40
. Assuming for simplicity that kerosene assets make 0.5
of CR meaning exogenous assets make the rest i.e 0.9
. Liquidator would expect 77% of the whole collateral position as reward which would give him an additional of 8% additional value compared to the burned dyad. For details: whole_position * 77% - burned_Dyad = burned_Dyad (1+0.08). Since whole_position = burned_Dyad * 1.40
.
However, he will only get 77% of the non kerosine value, i.e 77% of the 0.9 assets that makes the CR. This means that he will burn his dyad but only get an equivalent value of 77%*0.9*(value of the burned dyad)
which is < burned_Dyad
. Thus losing value.
Manual review and foundry testing suite.
The protocol needs to move the kerosene assets to the liquidators as well to make sure that value is not lost when liquidating.
Other
#0 - c4-pre-sort
2024-04-28T10:19:21Z
JustDravee marked the issue as duplicate of #128
#1 - c4-pre-sort
2024-04-29T09:03:29Z
JustDravee marked the issue as sufficient quality report
#2 - c4-judge
2024-05-11T19:39:48Z
koolexcrypto marked the issue as satisfactory
#3 - c4-judge
2024-05-13T18:40:18Z
koolexcrypto changed the severity to 3 (High Risk)
🌟 Selected for report: dimulski
Also found by: 0xleadwizard, 0xlemon, Aamir, Al-Qa-qa, AvantGard, Bauchibred, Cryptor, DarkTower, Egis_Security, Giorgio, Maroutis, MrPotatoMagic, OMEN, Ocean_Sky, Ryonen, SBSecurity, Sabit, SpicyMeatball, Stefanov, T1MOH, Tigerfrake, WildSniper, atoko, bhilare_, darksnow, fandonov, grearlake, iamandreiski, igdbase, pontifex, web3km, xiao
4.8719 USDC - $4.87
https://github.com/code-423n4/2024-04-dyad/blob/cd48c684a58158de444b24854ffd8f07d046c31b/src/core/VaultManagerV2.sol#L215 https://github.com/code-423n4/2024-04-dyad/blob/cd48c684a58158de444b24854ffd8f07d046c31b/src/core/VaultManagerV2.sol#L224-L225
Two similar but different issues are being discussed in this finding. The first one is that the protocol allows users to mint Dyad without any lower limit. Small positions like this can accumulate fast. And the second one is that liquidators will not engage in positions where the usd collateral value is smaller or close the the dyad loan value + any gas costs. The latter can happen frequently since a liquidator cannot chose to liquidate a specific amount but the whole position only. Also, the second issue concerns any position not just smaller ones. Unliquidatable positions like these can quickly accumulate and create a liquidity crisis event.
This makes the impact high and the likelihood of the event medium to high as well.
A position could exist with a very low collateral value. This user is undercollateralized and must be liquidated in order to ensure that the protocol remains overcollateralized. If a liquidator wishes to liquidate this user, they will first need to stake some collateral (weth or other) which involves gas cost. Because the value of the collateral is so low, after gas costs, liquidators will not make a profit. In the end these low value vaults will never get liquidated, leaving the protocol with bad debt. Also malicious users could knowingly creates multiple small positions in order to attack the protocol and depeg the Dyad token.
The function liquidate
expects the liquidator to fully liquidate a position, even if it's not economically viable. The absence of partial liquidation and loan interests could deter liquidators from engaging with less profitable positions. This particular issue regards all sizes small, medium and larges positions. An accumulation of these bad debts can actually trigger a liquidity crisis.
In both of these cases, the rule is simple, liquidators will only liquidate a position if :
vaultsAssets * liquidationAssetShare > mintedDyadLoan + Gas costs
By default any position with a CR=100%
or less will not be liquidated.
Not having the flexibity of partially liquidating a position so that a non profitable position becomes profitable increases the risk of bad debt.
Similarly, not having any interest payments also participates in the lack of incentives. As increasing interests worsens CR
and increases incentives.
Manual review
Consider implementing the following changes to significantly minimize bad debt :
Dyad
Other
#0 - c4-pre-sort
2024-04-27T17:33:30Z
JustDravee marked the issue as duplicate of #1258
#1 - c4-pre-sort
2024-04-29T09:16:46Z
JustDravee marked the issue as sufficient quality report
#2 - c4-judge
2024-05-03T14:07:47Z
koolexcrypto changed the severity to QA (Quality Assurance)
#3 - c4-judge
2024-05-12T09:33:01Z
koolexcrypto marked the issue as grade-c
#4 - Maroutis
2024-05-16T22:37:30Z
I believe this issue should be a duplicate of #1097. I made sure to have all bad debt issues grouped here. This submission clearly mentions the problems that would arise from not having partial liquidations :
The absence of partial liquidation and loan interests could deter liquidators from engaging with less profitable positions. This particular issue regards all sizes small, medium and larges positions. An accumulation of these bad debts can actually trigger a liquidity crisis.
In both of these cases, the rule is simple, liquidators will only liquidate a position if :
vaultsAssets * liquidationAssetShare > mintedDyadLoan + Gas costs
By default any position with a CR=100% or less will not be liquidated.
Introduce partial liquidations
#5 - c4-judge
2024-05-22T14:26:07Z
This previously downgraded issue has been upgraded by koolexcrypto
#6 - c4-judge
2024-05-24T07:04:16Z
koolexcrypto marked the issue as satisfactory
#7 - Maroutis
2024-05-24T09:34:57Z
Hi @koolexcrypto, I believe this submission should be split into two issues since it mentions both partial liquidations and small debt problems. First half should be duped with #1258 and second with #1097.
#8 - c4-judge
2024-05-28T20:06:16Z
koolexcrypto marked the issue as duplicate of #175