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: 100/183
Findings: 4
Award: $11.38
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 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/cd48c684a58158de444b24854ffd8f07d046c31b/src/core/VaultManagerV2.sol#L119 https://github.com/code-423n4/2024-04-dyad/blob/cd48c684a58158de444b24854ffd8f07d046c31b/src/core/VaultManagerV2.sol#L134
In VaultManagerV2, when the user calls the deposit function, the current block.number will be recorded. When the user calls the withdraw function, the current block.number will be checked to prevent reentrancy.
//deposit idToBlockOfLastDeposit[id] = block.number; //withdraw if (idToBlockOfLastDeposit[id] == block.number) revert DepositedInSameBlock();
When the user calls the withdraw function, the attacker can call the deposit function in advance, with the parameter amount being 0, which will prevent the user from making withdrawals
foundry, Manual audit
Define a variable Lock to prevent reentrancy.
+ uint private Lock; function deposit( uint id, address vault, uint amount ) external isValidDNft(id) { - idToBlockOfLastDeposit[id] = block.number; + Lock = 1; Vault _vault = Vault(vault); _vault.asset().safeTransferFrom(msg.sender, address(vault), amount); _vault.deposit(id, amount); + Lock = 0; } /// @inheritdoc IVaultManager function withdraw( uint id, address vault, uint amount, address to ) public isDNftOwner(id) { - if (idToBlockOfLastDeposit[id] == block.number) revert DepositedInSameBlock(); + if (Lock == 1) 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(); }
DoS
#0 - c4-pre-sort
2024-04-27T11:48:23Z
JustDravee marked the issue as duplicate of #489
#1 - c4-pre-sort
2024-04-29T09:31:38Z
JustDravee marked the issue as sufficient quality report
#2 - c4-judge
2024-05-05T20:38:14Z
koolexcrypto marked the issue as unsatisfactory: Invalid
#3 - c4-judge
2024-05-05T20:39:24Z
koolexcrypto marked the issue as unsatisfactory: Invalid
#4 - c4-judge
2024-05-05T21:22:23Z
koolexcrypto marked the issue as nullified
#5 - c4-judge
2024-05-05T21:22:29Z
koolexcrypto marked the issue as not nullified
#6 - c4-judge
2024-05-08T15:28:10Z
koolexcrypto marked the issue as duplicate of #1001
#7 - c4-judge
2024-05-11T19:50:26Z
koolexcrypto marked the issue as satisfactory
#8 - c4-judge
2024-05-13T18:34:30Z
koolexcrypto changed the severity to 3 (High Risk)
🌟 Selected for report: Circolors
Also found by: 0x175, 0x486776, 0xAlix2, 0xSecuri, 0xShitgem, 0xfox, 0xlemon, 0xnilay, 3th, 4rdiii, Aamir, Al-Qa-qa, AlexCzm, Egis_Security, Evo, Honour, Infect3d, Josh4324, Limbooo, Mahmud, SBSecurity, TheSchnilch, ahmedaghadi, alix40, amaron, bbl4de, bhilare_, btk, carrotsmuggler, cinderblock, d3e4, dimulski, dinkras, ducanh2706, iamandreiski, itsabinashb, ke1caM, ljj, sashik_eth, shaflow2, steadyman, web3km, y4y
3.8221 USDC - $3.82
Users can deposit or withdraw Kerosine to UnboundedKerosineVault through the deposit() and withdraw() methods of VaultManagerV2. When the user calls the withdraw() method to take out Kerosine, the transaction will be reverted. The reason why the transaction will be reverted is that the pricing methods of Vault and UnboundedKerosineVault are different. In Vault, the price is obtained through the oracle instance, but in UnboundedKerosineVault, there is no oracle instance. In withdraw(), the oracle is called to obtain the decimal value of the price when calculating the price, but there is no oracle instance in UnboundedKerosineVault, which causes the transaction to be reverted.
function testVaultManagerWithdraw() public { testLicenseVaultManager(); address addr1 = makeAddr("addr1"); deal(MAINNET_KEROSENE, addr1, 100e18); vm.startPrank(MAINNET_OWNER); uint NFTid = DNft(MAINNET_DNFT).mintInsiderNft(addr1); vm.stopPrank(); vm.startPrank(addr1); IERC20(MAINNET_KEROSENE).approve(address(contracts.vaultManager), 100e18); vm.ro11(1); contracts.vaultManager.deposit( NFTid, address(contracts.unboundedKerosineVault), 100e18 ); vm.ro11(2); vm.expectRevert(); contracts.vaultManager.withdraw( NFTid, address(contracts.unboundedKerosineVault), 100e18, addr1 ); vm.stopPrank(); }
foundry, Manual audit
Add a bool variable to distinguish whether the user wants to retrieve Kerosine.
function withdraw( uint id, address vault, uint amount, address to, bool isKerosine ) public isDNftOwner(id) { if (idToBlockOfLastDeposit[id] == block.number) revert DepositedInSameBlock(); uint dyadMinted = dyad.mintedDyad(address(this), id); Vault _vault = Vault(vault); if(!isKerosine) { uint value = amount * _vault.assetPrice() * 1e18 / 10**_vault.oracle().decimals() / 10**_vault.asset().decimals(); if (getNonKeroseneValue(id) - value < dyadMinted) revert NotEnoughExoCollat(); }esle{ if (getNonKeroseneValue(id) - amount< dyadMinted) revert NotEnoughExoCollat(); } _vault.withdraw(id, to, amount); if (collatRatio(id) < MIN_COLLATERIZATION_RATIO) revert CrTooLow(); }
Math
#0 - c4-pre-sort
2024-04-26T20:55:35Z
JustDravee marked the issue as duplicate of #1048
#1 - c4-pre-sort
2024-04-28T18:38:35Z
JustDravee marked the issue as duplicate of #830
#2 - c4-pre-sort
2024-04-29T08:44:04Z
JustDravee marked the issue as sufficient quality report
#3 - c4-judge
2024-05-11T20:04:26Z
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
When using DeployV2, the deployed Dyad contract is used, which will cause TVL > DYAD totalsupply to not be established, resulting in system DoS. The reason why Dos occurs is because the total supply of Dyad on the Ethereum mainnet is very large, but all vaults are redeployed, resulting in TVL being 0. At this time, TVL < DYAD totalsupply, so when using the assetPrice function in the UnboundedKerosineVault contract to calculate the price, it will An underflow occurred.
uint numerator = tvl - dyad.totalSupply();
This will result in users using UnboundedKerosineVault being unable to perform any operations. Any attempt to interact with it will fail.
//The reason why the getNonKeroseneValue function was chosen for testing is because there is a serious problem with the addKerosene of VaultManagerV2, which prevents the Kerosene vault from being added. //However, add can add the Kerosene vault, so the getNonKeroseneValue function is //selected for testing. function testgetNonKeroseneValueRevert () public { testLicenseVaultManager(); address addr1 = makeAddr ("addr1"); vm.startPrank(MAINNET_OWNER); uint NFTid = DNft(MAINNET_DNFT).mintInsiderNft(addr1); IERC20(MAINNET_KEROSENE).transfer(addr1, 100e18); vm.stopPrank(); v.startPrank(addr1); contracts.vaultManager.add(NFTid, address(contracts.unboundedKerosineVault)); IERC20(MAINNET_KEROSENE).approve(address(contracts.vaultManager), 10e18); contracts.vaultManager.deposit( NFTid, address(contracts.unboundedKerosineVault), 10e18 ); vm.expectRevert(); contracts.vaultManager.getNonKeroseneValue(NFTid) vm.stopPrank(); }
foundry, Manual audit
Redeploy a Dyad contract and provide a method for users to transfer their old Dyad Token and assets in the old vault.
DoS
#0 - c4-pre-sort
2024-04-29T07:30:52Z
JustDravee marked the issue as duplicate of #308
#1 - c4-pre-sort
2024-04-29T09:04:51Z
JustDravee marked the issue as sufficient quality report
#2 - c4-judge
2024-05-11T20:08:54Z
koolexcrypto marked the issue as satisfactory
#3 - c4-judge
2024-05-13T18:34:04Z
koolexcrypto changed the severity to 3 (High Risk)
🌟 Selected for report: carrotsmuggler
Also found by: 0xAlix2, 0xSecuri, 0xleadwizard, 0xlemon, 0xtankr, 3th, Aamir, Abdessamed, Bauchibred, Circolors, Egis_Security, Evo, Hueber, Mahmud, SBSecurity, TheSavageTeddy, TheSchnilch, Tychai0s, alix40, bbl4de, btk, d3e4, ducanh2706, falconhoof, itsabinashb, ke1caM, lian886, n4nika, oakcobalt, pontifex, sashik_eth, steadyman, tchkvsky, zhuying
3.7207 USDC - $3.72
In the addKerosene function of VaultManagerV2, it is checked whether the vault is added by keroseneManager.
However, the Vaults in keroseneManager are used to store ordinary vaults used to calculate LVT, not Kerosene vaults.
In Deploy.V2.sol, all vaults are added to the License, and ethVault and wstEth are added to the kerosineManager.
This will result in:
function testAddError() public { testLicenseVaultManager(); address addr1 = makeAddr("addr1"); deal(MAINNET_WETH, addr1, 100e18); vm.startPrank(MAINNET_OWNER); uint256 NFTid = DNft(MAINNET_DNFT).mintInsiderNft(addr1); IERC20(MAINNET_KEROSENE).transfer(addr1, 100e18); vm.stopPrank(); vm.startPrank(addr1); contracts.vaultManager.add(NFTid, address(contracts.ethVault)); IERC20(MAINNET_WETH).approve(address(contracts.vaultManager), 10e18); contracts.vaultManager.deposit(NFTid, address(contracts.ethVault), 10e18); console.log("Before repeat add ethVault totalValue:"); console.log(contracts.vaultManager.getTotalUsdValue(NFTid)); contracts.vaultManager.addKerosene(NFTid, address(contracts.ethVault)); console, log("After repeat add ethVault totalValue:"); console. log(contracts.vaultManager.getTotalUsdValue(NFTid)); vm.stopPrank(); }
foundry, Manual audit
Add mapping for managing Kerosene treasury in Licenser contract
Licenser.sol
+ mapping (address => bool) public isKeroseneLicensed; + function addKerosene (address vault) external onlyOwner { isKeroseneLicensed[vault] = true; } + function removeKerosene (address vault) external onlyOwner { isKeroseneLicensed[vault] = false; }
VaultManagerV2.sol
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 (!isKeroseneLicensed.isLicensed(vault)) revert VaultNotLicensed(); if (!vaultsKerosene[id].add(vault)) revert VaultAlreadyAdded(); emit Added(id, vault); }
Other
#0 - c4-pre-sort
2024-04-29T05:26:51Z
JustDravee marked the issue as duplicate of #70
#1 - c4-pre-sort
2024-04-29T12:01:58Z
JustDravee marked the issue as sufficient quality report
#2 - c4-judge
2024-05-11T19:58:43Z
koolexcrypto marked the issue as satisfactory
#3 - c4-judge
2024-05-13T18:36:27Z
koolexcrypto changed the severity to 2 (Med Risk)