DYAD - y4y'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: 142/183

Findings: 2

Award: $3.84

🌟 Selected for report: 0

🚀 Solo Findings: 0

Lines of code

https://github.com/code-423n4/2024-04-dyad/blob/4a987e536576139793a1c04690336d06c93fca90/src/core/VaultManagerV2.sol#L119

Vulnerability details

Impact

An NFT owner's withdrawal can be persistently DoS'd by depositing 0 tokens.

Proof of Concept

The withdraw functions only lets the NFT owner to withdraw assets do the operation, and immediately checks if there are deposits at the same block, and reverts if so.

  function withdraw(
    uint    id,
    address vault,
    uint    amount,
    address to
  ) 
    public
      isDNftOwner(id)
  {
    if (idToBlockOfLastDeposit[id] == block.number) revert DepositedInSameBlock();
    // snip...

At the same time, we see in deposit function:

  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);
  }

idToBlockOfLastDeposit is updated each time there is a deposit, the only requirement to call this function is by providing a valid NFT id. Since there is no minimum amount of deposited, which makes the cost of depositing extremely low.

When a malicious actor observes the withdraw action, they can immediately front the transaction by depositing 0 tokens to the correspond id, and make the transaction revert. To some extreme extend, this DoS can even make the entire vault dysfunctional as the only way to withdraw from the vaults is through the vault managers.

Tools Used

Manual review

Add a minimum deposit amount, or allow a minimum amount withdrawable for each id.

Assessed type

DoS

#0 - c4-pre-sort

2024-04-27T11:55:30Z

JustDravee marked the issue as duplicate of #489

#1 - c4-pre-sort

2024-04-29T09:31:30Z

JustDravee marked the issue as sufficient quality report

#2 - c4-judge

2024-05-05T20:41:18Z

koolexcrypto marked the issue as unsatisfactory: Invalid

#3 - c4-judge

2024-05-05T20:42:04Z

koolexcrypto marked the issue as unsatisfactory: Invalid

#4 - c4-judge

2024-05-05T20:45:37Z

koolexcrypto marked the issue as unsatisfactory: Invalid

#5 - c4-judge

2024-05-05T21:55:18Z

koolexcrypto marked the issue as nullified

#6 - c4-judge

2024-05-05T21:55:22Z

koolexcrypto marked the issue as not nullified

#7 - c4-judge

2024-05-08T15:26:33Z

koolexcrypto marked the issue as duplicate of #1001

#8 - c4-judge

2024-05-11T19:48:44Z

koolexcrypto marked the issue as satisfactory

#9 - c4-judge

2024-05-13T18:34:30Z

koolexcrypto changed the severity to 3 (High Risk)

Awards

3.8221 USDC - $3.82

Labels

bug
3 (High Risk)
satisfactory
sufficient quality report
:robot:_52_group
duplicate-830

External Links

Lines of code

https://github.com/code-423n4/2024-04-dyad/blob/4a987e536576139793a1c04690336d06c93fca90/src/core/Vault.kerosine.unbounded.sol#L50 https://github.com/code-423n4/2024-04-dyad/blob/4a987e536576139793a1c04690336d06c93fca90/src/core/Vault.kerosine.sol#L60

Vulnerability details

Impact

The getKeroseneValue or getUsdValue function will potentially fail and revert if any of the Kerosine vaults are in the vaults list.

Proof of Concept

VaultManagerV2.getTotalUsdValue computes the USD value in both Kerosine and non-Kerosine vaults altogether. In both functions, vault.getUsdValue(id); is called to get single vault's price, in Kerosine vault, we can see the function is defined in Vault.kerosine.sol:

  function getUsdValue(
    uint id
  )
    public
    view 
    returns (uint) {
      return id2asset[id] * assetPrice() / 1e8;
  }

which calls assetPrice, which is implemented in the child contracts. For example, in unbounded Kerosine vault:

  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;
  }

And we see, from all vaults in the KerosineManager, assetPrice of each vault is called, and combined with vault.asset and vault.oracle.

The issue is, in both bounded and unbounded Kerosine contracts, oracle is not defined, and when other functions reference this variable, it will revert as the variable doesn't exsist in those contracts. This causes all functions which may potentially call Kerosine vault's assetPrice function to revert. One example would be VaultManagerV2.liquidate:

  function liquidate(
    uint id,
    uint to
  ) 
    external 
      isValidDNft(id)
      isValidDNft(to)
    {
      uint cr = collatRatio(id);
      // ... snip

Where:

  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);
  }

Which eventually calls assetPrice.

Tools Used

Manual review

Add oracle variable in Kerosine contracts.

Assessed type

Context

#0 - c4-pre-sort

2024-04-27T18:11:51Z

JustDravee marked the issue as duplicate of #1048

#1 - c4-pre-sort

2024-04-28T18:38:31Z

JustDravee marked the issue as duplicate of #830

#2 - c4-pre-sort

2024-04-29T08:43:47Z

JustDravee marked the issue as sufficient quality report

#3 - c4-judge

2024-05-11T20:04:14Z

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