Platform: Code4rena
Start Date: 31/01/2023
Pot Size: $90,500 USDC
Total HM: 47
Participants: 169
Period: 7 days
Judge: LSDan
Total Solo HM: 9
Id: 211
League: ETH
Rank: 47/169
Findings: 2
Award: $320.08
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0xNineDec
Also found by: 0xBeirao, 0xNazgul, 0xRajkumar, Blockian, Breeje, CRYP70, Josiah, KIntern_NA, MyFDsYours, Qeew, RaymondFam, Ruhum, UdarTeam, chaduke, giovannidisiena, gjaldon, immeas, koxuan, nadin, peanuts, rbserver, rvi0x, savi0ur
14.2839 USDC - $14.28
https://github.com/code-423n4/2023-01-popcorn/blob/main/src/vault/Vault.sol#L298-L300 https://github.com/code-423n4/2023-01-popcorn/blob/main/src/vault/Vault.sol#L134-L158
The first deposit with a totalSupply
of zero shares will mint shares equal to the deposited amount.
File: src/vault/Vault.sol 298: supply == 0 299: ? assets 300: : assets.mulDiv(supply, totalAssets(), Math.Rounding.Down);
File: src/vault/Vault.sol function deposit(uint256 assets, address receiver) public nonReentrant whenNotPaused syncFeeCheckpoint returns (uint256 shares) { if (receiver == address(0)) revert InvalidReceiver(); uint256 feeShares = convertToShares( assets.mulDiv(uint256(fees.deposit), 1e18, Math.Rounding.Down) ); shares = convertToShares(assets) - feeShares; if (feeShares > 0) _mint(feeRecipient, feeShares); _mint(receiver, shares); asset.safeTransferFrom(msg.sender, address(this), assets); adapter.deposit(assets, address(this)); emit Deposit(msg.sender, receiver, assets, shares); }
For calculation Simplicity let's take fees to be zero as of now.
Problems with the code:
It can lead to some part of Fund getting stolen from First Depositor.
Consider the following situation:
Have a look at this table to understand the complete PoC:
Before | Before | After | After | ||
---|---|---|---|---|---|
Tx | totalSupply | balanceOf | sharesToMint | totalSupply | balanceOf |
BeforeAttacker deposits 1 wei of WETH. | 0 | 0 | 1 | 1 | 1 |
Attacker transfers 100 WETH to the contract. | 1 | 1 | N/A | 1 | 1 + 100 x 10^18 |
Victim deposits 200 WETH. | 1 | 1 + 100 x 10^18 | =1.99 = 1 | 2 | 1 + 300 x 10^18 |
Attacker withdraws 1 share. | 2 | 1 + 300 x 10^18 | N/A | 1 | 1 + 150 x 10^18 |
Manual Review
#0 - c4-judge
2023-02-16T03:31:14Z
dmvt marked the issue as duplicate of #15
#1 - c4-sponsor
2023-02-18T11:54:55Z
RedVeil marked the issue as sponsor confirmed
#2 - c4-judge
2023-02-23T00:10:39Z
dmvt marked the issue as satisfactory
🌟 Selected for report: IllIllI
Also found by: 0x3b, 0xAgro, 0xBeirao, 0xMirce, 0xNineDec, 0xRobocop, 0xSmartContract, 0xTraub, 0xWeiss, 2997ms, 41i3xn, Awesome, Aymen0909, Bauer, Bnke0x0, Breeje, Cryptor, DadeKuma, Deathstore, Deekshith99, DevABDee, DevTimSch, Dewaxindo, Diana, Ermaniwe, Guild_3, H0, IceBear, Inspectah, JDeryl, Kaiziron, Kaysoft, Kenshin, Mukund, Praise, RaymondFam, Rickard, Rolezn, Ruhum, Sathish9098, SkyWalkerMan, SleepingBugs, UdarTeam, Udsen, Walter, aashar, adeolu, apvlki, arialblack14, ast3ros, btk, chaduke, chandkommanaboyina, chrisdior4, climber2002, codetilda, cryptonue, cryptostellar5, csanuragjain, ddimitrov22, descharre, dharma09, doublesharp, eccentricexit, ethernomad, fs0c, georgits, halden, hansfriese, hashminer0725, immeas, lukris02, luxartvinsec, matrix_0wl, merlin, mookimgo, mrpathfindr, nadin, olegthegoat, pavankv, rbserver, rebase, savi0ur, sayan, scokaf, seeu, shark, simon135, tnevler, tsvetanovv, ulqiorra, ustas, waldenyan20, y1cunhui, yongskiws, yosuke
305.798 USDC - $305.80
Issue | Instances | |
---|---|---|
L-1 | USE OF FLOATING PRAGMA | 35 |
L-2 | RETURN VALUE OF LOW-LEVEL CALLS NOT CHECKED | 1 |
L-3 | MISSING EXISTANCE CHECK IN toggleTemplateEndorsement METHOD | 1 |
L-4 | super KEYWORD NOT USED TO CALL PARENT FUNCTIONS | 4 |
NC-1 | FUNCTION STATE MUTABILITY CAN BE RESTRICTED TO VIEW | 4 |
NC-2 | AVOID VARIABLE NAMES THAT CAN SHADE | 10 |
NC-3 | UNUSED LOCAL VARIABLE | 1 |
NC-4 | UNUSED FUNCTION PARAMETER | 1 |
NC-5 | ERROR DECLARED BUT NOT USED | 1 |
NC-6 | MISSING CHECKS FOR address(0) OUTSIDE OF AUTOMATED FINDINGS | 1 |
NC-7 | INTERFACE CODE DEVELOPED BUT NOT IMPLEMENTED | 1 |
Impact: swcregistry
Instances (35):
All File in Scope uses pragma solidity ^0.8.15;
Floating pragma Solidity Version.
Instance (1):
File: src/vault/adapter/abstracts/AdapterBase.sol 444: address(strategy).delegatecall( 445: abi.encodeWithSignature("harvest()") 446: );
toggleTemplateEndorsement
METHODIn toggleTemplateEndorsement
method of TemplateRegistry
contract, there is only one check !templateExists[templateId]
which checks if the templateId exists or not.
But it is not necessary that the given templateId is mapped to templateCategory which was passed through function parameter. So it is important to check this by adding:
if(templates[templateCategory][templateId].implementation == address(0)) revert CategoryIdMismatch(templateCategory, templateId);
super
KEYWORD NOT USED TO CALL PARENT FUNCTIONSIt is recommended to use the super
keyword to call a function in parent contract.
Instances (4):
File: src/utils/MultiRewardStaking.sol 76: return deposit(_amount, msg.sender); 80: return mint(_amount, msg.sender); 84: return withdraw(_amount, msg.sender, msg.sender); 88: return redeem(_amount, msg.sender, msg.sender);
Following function instances do not change state of the contract. So it is recommended to restrict its mutability to view only.
Instances (4):
File: src/utils/MultiRewardStaking.sol 351: function _calcRewardsEnd(
File: src/vault/VaultController.sol 242: function _encodeAdapterData(DeploymentArgs memory adapterData, bytes memory baseAdapterData) 667: function _verifyCreatorOrOwner(address vault) internal returns (VaultMetadata memory metadata) {
File: src/vault/strategy/StrategyBase.sol 12: function verifyAdapterSelectorCompatibility(bytes4[8] memory sigs) public {
_totalAssets
shades with _totalAssets
function. Other shades are self explanatory.
Instance (10):
File: src/vault/adapter/abstracts/AdapterBase.sol 388: uint256 _totalAssets = totalAssets();
File: src/utils/MultiRewardEscrow.sol 117: token: token, 118: start: start, 123: account: account
File: src/utils/MultiRewardStaking.sol 267: escrowPercentage: escrowPercentage, 268: escrowDuration: escrowDuration, 269: offset: offset 280: ONE: ONE, 281: rewardsPerSecond: rewardsPerSecond, 282: rewardsEndTimestamp: rewardsEndTimestamp,
Local variable should be removed if not used. Either use this variable in place of state variable to save gas or remove this variable.
Instance (1)
File: src/vault/Vault.sol 448: uint256 highWaterMark_ = highWaterMark;
Unused parameter can be removed.
Instance (1):
File: src/vault/VaultController.sol 390: function _registerVault(address vault, VaultMetadata memory metadata) internal {
Error should be removed as it is not used.
Instance (1):
File: src/vault/CloneFactory.sol 32: error NotEndorsed(bytes32 templateKey);
address(0)
OUTSIDE OF AUTOMATED FINDINGSInstance (1):
File: src/vault/CloneRegistry.sol 44: address clone
The contracts listed below implements all the methods of the interface IPermissionRegistry
, IVaultRegistry
, IMultiRewardEscrow
and IMultiRewardStaking
respectively. But those interfaces are not added in is
.
File: src/vault/PermissionRegistry.sol 14: contract PermissionRegistry is Owned {
File: src/vault/VaultRegistry.sol 15: contract VaultRegistry is Owned {
File: src/utils/MultiRewardEscrow.sol 21: contract MultiRewardEscrow is Owned {
File: src/utils/MultiRewardStaking.sol 26: contract MultiRewardStaking is ERC4626Upgradeable, OwnedUpgradeable {
#0 - c4-judge
2023-02-28T17:24:33Z
dmvt marked the issue as grade-a