Platform: Code4rena
Start Date: 07/07/2022
Pot Size: $75,000 USDC
Total HM: 32
Participants: 141
Period: 7 days
Judge: HardlyDifficult
Total Solo HM: 4
Id: 144
League: ETH
Rank: 76/141
Findings: 2
Award: $99.45
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: xiaoming90
Also found by: 0x1f8b, 0x29A, 0x52, 0xA5DF, 0xDjango, 0xNazgul, 0xNineDec, 0xf15ers, 0xsanson, 0xsolstars, 242, 8olidity, Amithuddar, Aymen0909, Bnke0x0, BowTiedWardens, David_, Deivitto, ElKu, Funen, Hawkeye, IllIllI, JC, Kaiziron, Keen_Sheen, Kthere, Kulk0, Kumpa, Lambda, MEP, ReyAdmirado, Rohan16, Ruhum, Sm4rty, TomJ, Tomio, Treasure-Seeker, TrungOre, Tutturu, Viksaa39, Waze, _Adam, __141345__, ak1, apostle0x01, asutorufos, async, ayeslick, aysha, bbrho, benbaessler, berndartmueller, c3phas, cccz, chatch, cloudjunky, codexploder, cryptphi, delfin454000, dipp, durianSausage, dy, exd0tpy, fatherOfBlocks, hake, hansfriese, horsefacts, hubble, joestakey, jonatascm, kebabsec, kenzo, kyteg, mektigboy, neumo, oyc_109, pashov, pedr02b2, peritoflores, rajatbeladiya, rbserver, robee, rokinot, s3cunda, sach1r0, sahar, sashik_eth, scaraven, shenwilly, simon135, sorrynotsorry, sseefried, svskaushik, unforgiven, z3s, zzzitron
61.9383 USDC - $61.94
For a function, when multiple inputs are arrays, and their items correspond to each other, the lengths of these arrays should be checked to be the same.
src\Vault.sol:73 -> function install(bytes4[] memory _selectors, address[] memory _plugins) src\modules\Buyout.sol:417-149 -> uint256[] calldata _ids, uint256[] calldata _values, bytes32[] calldata _erc1155BatchTransferProof src\modules\protoforms\BaseVault.sol:61-62 -> address[] calldata _tokens, uint256[] calldata _amounts src\modules\protoforms\BaseVault.sol:80-81 -> address[] calldata _tokens, uint256[] calldata _ids src\modules\protoforms\BaseVault.sol:101-104 -> address[] calldata _tokens, uint256[] calldata _ids, uint256[] calldata _amounts, bytes[] calldata _datas src\targets\Transfer.sol:478-479 -> uint256[] calldata, /*_ids*/ uint256[] calldata /*_amounts*/
Addresses should be checked against address(0).
src\modules\Buyout.sol:43-45 -> address _registry, address _supply, address _transfer src\modules\Migration.sol:54-56 -> address _buyout, address _registry, address _supply src\modules\Minter.sol:17 -> constructor(address _supply) src\modules\protoforms\BaseVault.sol:24 -> constructor(address _registry, address _supply) Minter(_supply) src\references\SupplyReference.sol:15 -> constructor(address _registry) src\targets\Supply.sol:16 -> constructor(address _registry) src\utils\Metadata.sol:16 -> constructor(address _token)
To improve readability and maintainability, constants can be used instead of magic numbers.
src\modules\Buyout.sol:86-87 -> uint256 buyoutPrice = (msg.value * 100) / (100 - ((depositAmount * 100) / totalSupply)); src\modules\Buyout.sol:209-211 -> (tokenBalance * 1000) / IVaultRegistry(registry).totalSupply(_vault) > 500
To monitor token transfers that may fail silently, the return value of transferFrom
can be checked. Alternatively, safeTransferFrom
can be used instead of transferFrom
.
src\modules\protoforms\BaseVault.sol:65 -> IERC20(_tokens[i]).transferFrom(_from, _to, _amounts[i]);
To improve readability and maintainability, the same code that are used in multiple functions can be in a reusable modifier.
src\modules\Buyout.sol:318-326 -> src\modules\Buyout.sol:350-358 -> src\modules\Buyout.sol:386-394 -> src\modules\Buyout.sol:423-431 -> // Reverts if address is not a registered vault (, uint256 id) = IVaultRegistry(registry).vaultToToken(_vault); if (id == 0) revert NotVault(_vault); // Reverts if auction state is not successful (, address proposer, State current, , , ) = this.buyoutInfo(_vault); State required = State.SUCCESS; if (current != required) revert InvalidState(required, current); // Reverts if caller is not the auction winner if (msg.sender != proposer) revert NotWinner();
The following event is missing indexed fields:
src\interfaces\IVault.sol:29 -> event InstallPlugin(bytes4[] _selectors, address[] _plugins);
As a best practice, only capital letters should be used for constants.
src\targets\Transfer.sol:366 -> ERC1155_SAFE_TRANSFER_FROM_signature
#0 - HardlyDifficult
2022-08-15T01:10:28Z
Merging with https://github.com/code-423n4/2022-07-fractional-findings/issues/374, #384
🌟 Selected for report: joestakey
Also found by: 0x1f8b, 0x29A, 0xA5DF, 0xKitsune, 0xNazgul, 0xNineDec, 0xalpharush, 0xkatana, 0xsanson, 0xsolstars, 8olidity, Avci, Bnke0x0, BowTiedWardens, Chom, Deivitto, ElKu, Fitraldys, Funen, IllIllI, JC, Kaiziron, Lambda, Limbooo, MEP, NoamYakov, PwnedNoMore, RedOneN, ReyAdmirado, Rohan16, Ruhum, Saintcode_, Sm4rty, TomJ, Tomio, TrungOre, Tutturu, Waze, _Adam, __141345__, ajtra, apostle0x01, asutorufos, benbaessler, brgltd, c3phas, codexploder, cryptphi, delfin454000, dharma09, djxploit, durianSausage, fatherOfBlocks, giovannidisiena, gogo, horsefacts, hrishibhat, hyh, ignacio, jocxyen, jonatascm, karanctf, kebabsec, kyteg, m_Rassska, mektigboy, oyc_109, pedr02b2, rbserver, robee, rokinot, sach1r0, sashik_eth, simon135, slywaters
37.5142 USDC - $37.51
Explicitly initializing a variable with its default value costs more gas than uninitializing it.
src\Vault.sol:78 -> for (uint256 i = 0; i < length; i++) src\Vault.sol:104 -> for (uint256 i = 0; i < length; i++) src\modules\protoforms\BaseVault.sol:64 -> for (uint256 i = 0; i < _tokens.length; ) src\modules\protoforms\BaseVault.sol:83 -> for (uint256 i = 0; i < _tokens.length; ) src\modules\protoforms\BaseVault.sol:107 -> for (uint256 i = 0; i < _tokens.length; ++i) src\utils\MerkleBase.sol:51 -> for (uint256 i = 0; i < _proof.length; ++i)
Caching the array length outside of the loop and using the cached length in the loop costs less gas than reading the array length for each iteration.
src\modules\Buyout.sol:454 -> for (uint256 i; i < permissions.length; ) src\modules\protoforms\BaseVault.sol:64 -> for (uint256 i = 0; i < _tokens.length; ) src\modules\protoforms\BaseVault.sol:83 -> for (uint256 i = 0; i < _tokens.length; ) src\modules\protoforms\BaseVault.sol:107 -> for (uint256 i = 0; i < _tokens.length; ++i) src\modules\protoforms\BaseVault.sol:130 -> for (uint256 i; i < _modules.length; ++i) src\modules\protoforms\BaseVault.sol:132 -> for (uint256 j; j < leaves.length; ++j) src\utils\MerkleBase.sol:51 -> for (uint256 i = 0; i < _proof.length; ++i) { src\utils\MerkleBase.sol:110 -> for (uint256 i; i < result.length; ++i) {
++variable costs less gas than variable++.
src\Vault.sol:78 -> for (uint256 i = 0; i < length; i++) src\Vault.sol:104 -> for (uint256 i = 0; i < length; i++) src\utils\MerkleBase.sol:188 -> ceil++;
Explicitly unchecking arithmetic operations that do not overflow by wrapping these in unchecked {}
costs less gas than implicitly checking these.
For loops, if increasing the counter variable is very unlikely to overflow, then unchecked {++i}
at the end of the loop block can be used, where i
is the counter variable.
src\Vault.sol:78 -> for (uint256 i = 0; i < length; i++) src\Vault.sol:104 -> for (uint256 i = 0; i < length; i++)
x = x + y and x = x -y costs less gas than x += y and x -= y.
src\FERC1155.sol:86 -> totalSupply[_id] += _amount; src\FERC1155.sol:271 -> balanceOf[_to][_id] += _amount; src\FERC1155.sol:62 -> totalSupply[_id] -= _amount; src\FERC1155.sol:270 -> balanceOf[_from][_id] -= _amount; src\modules\Buyout.sol:176 -> buyoutInfo[_vault].ethBalance += msg.value; src\modules\Buyout.sol:139 -> buyoutInfo[_vault].ethBalance -= ethAmount; src\modules\Buyout.sol:139 -> buyoutInfo[_vault].ethBalance -= ethAmount; src\modules\Migration.sol:123 -> proposal.totalEth += msg.value; src\modules\Migration.sol:124 -> userProposalEth[_proposalId][msg.sender] += msg.value; src\modules\Migration.sol:134 -> proposal.totalFractions += _amount; src\modules\Migration.sol:135 -> userProposalFractions[_proposalId][msg.sender] += _amount; src\modules\Migration.sol:497 -> treeLength += IModule(_modules[i]).getLeafNodes().length; src\modules\Migration.sol:156 -> proposal.totalFractions -= amount; src\modules\Migration.sol:160 -> proposal.totalEth -= ethAmount; src\utils\MerkleBase.sol:147 -> for (uint256 i; i < length - 1; i += 2) src\utils\MerkleBase.sol:190 -> ceil -= pOf2;
Revert reason strings that are longer than 32 bytes need more memory space and more mstore operations than these that are shorter than 32 bytes.
src\utils\MerkleBase.sol:62 -> require(_data.length > 1, "wont generate root for single leaf"); src\utils\MerkleBase.sol:78 -> require(_data.length > 1, "wont generate proof for single leaf");
Custom errors are more gas-efficient than revert reason strings.
src\FERC1155.sol:263-268 -> require( msg.sender == _from || isApprovedForAll[_from][msg.sender] || isApproved[_from][msg.sender][_id], "NOT_AUTHORIZED" ); src\FERC1155.sol:275-286 -> require( _to.code.length == 0 ? _to != address(0) : INFTReceiver(_to).onERC1155Received( msg.sender, _from, _id, _amount, _data ) == INFTReceiver.onERC1155Received.selector, "UNSAFE_RECIPIENT" ); src\FERC1155.sol:297 -> require(metadata[_id] != address(0), "NO METADATA"); src\utils\MerkleBase.sol:62 -> require(_data.length > 1, "wont generate root for single leaf"); src\utils\MerkleBase.sol:78 -> require(_data.length > 1, "wont generate proof for single leaf"); src\utils\Multicall.sol:23 -> if (result.length == 0) revert();
For constants that are used only inside the contract, they can be private than public to cost less gas.
src\utils\SafeSend.sol:11 -> address payable public constant WETH_ADDRESS
Converting a variable's type when not needed costs more unnecessary gas.
src\VaultFactory.sol:87 -> address(vault)
When state values need to be read and accessed for multiple times, caching them in memory can save gas.
For the following code, nextId[_token]
can be cached and then increased by 1 in memory. Afterwards, the cached value can be assigned to the corresponding state variable and used as the input for VaultInfo
. On the last line, the cheaper mload operation can be used instead of the sload operation by loading the cached value.
src\VaultRegistry.sol:172-176 -> vaultToToken[vault] = VaultInfo(_token, ++nextId[_token]); ... emit VaultDeployed(vault, _token, nextId[_token]);