DYAD - Strausses's results

The first capital efficient overcollateralized stablecoin.

General Information

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

DYAD

Findings Distribution

Researcher Performance

Rank: 28/183

Findings: 2

Award: $405.81

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

Labels

bug
3 (High Risk)
satisfactory
sufficient quality report
edited-by-warden
:robot:_11_group
duplicate-338

Awards

283.3687 USDC - $283.37

External Links

Lines of code

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

Vulnerability details

Impact

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

Proof of Concept

! 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%

Tools Used

Manual review

Be more accurate with BoundedKerosineVault feature, and rewrite the code, otherwise token will gain more and more bad positions.

Assessed type

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

Findings Information

Labels

bug
2 (Med Risk)
satisfactory
sufficient quality report
:robot:_78_group
duplicate-829

Awards

122.4433 USDC - $122.44

External Links

Lines of code

https://github.com/code-423n4/2024-04-dyad/blob/cd48c684a58158de444b24854ffd8f07d046c31b/script/deploy/Deploy.V2.s.sol#L35-L113

Vulnerability details

Impact

Missing to set a setting while deployment

Proof of Concept

Vault.kerosine.bounded.sol has the setUnboundedKerosineVault function, which sets a source of an assetPrice variable. This function should be called while contracts initializations

Tools Used

Manual review

BoundedKerosineVault boundedKerosineVault     = new BoundedKerosineVault(
      vaultManager,
      Kerosine(MAINNET_KEROSENE), 
      kerosineManager
    );
+ boundedKerosineVault.setUnboundedKerosineVault(unboundedKerosineVault);

    KerosineDenominator kerosineDenominator       = new KerosineDenominator(
      Kerosine(MAINNET_KEROSENE)
    );

Assessed type

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

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter