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: 114/141
Findings: 1
Award: $55.16
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 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
55.1597 USDC - $55.16
Optimizations | Occurances |
---|---|
1. Variables can be made immutable | 8 |
2. Use calldata instead of memory | 15 |
3. Use ++i instead of i++ & Add unchecked { ++i; } in loops | 2 |
4. Remove unnecessary variables | 3 |
5. Use storage pointer to set value | 1 |
6. Internal functions which are used only once can be inlined | 2 |
7. Mark function calls from known callers as payable to bypass the check | 6 |
8. Use >>1 instead of /2 | 3 |
9. Add unchecked block for where arithmetic overflow cannot happen | 5 |
10. Using a = a + b instead of a += b for state variables saves gas | 2 |
11. Looking up array length from memory every time in a loop costs more gas | 1 |
registry
in the BaseVault.solregistry
in Buyout.solsupply
in Buyout.soltransfer
in Buyout.solsupply
in Minter.solbuyout
in Migration.solregistry
in Migration.colimplementation
in VaultFactory.solcalldata
instead of memory in++i
instead of i++
& Add unchecked { ++i }
in all following for loops.- for (uint256 i = 0; i < length; i++) { - methods[_selectors[i]] = _plugins[i]; - } + for (uint256 i = 0; i < length; ) { + methods[_selectors[i]] = _plugins[i]; + unchecked { ++i; } + }
- for (uint256 i = 0; i < length; i++) { - methods[_selectors[i]] = address(0); - } + for (uint256 i = 0; i < length; ) { + methods[_selectors[i]] = address(0); + unchecked { ++i; } + }
VaultFactory.sol#L68 data
is not necessary is variable
- bytes memory data = abi.encodePacked(); - vault = implementation.clone(salt, data); + vault = implementation.clone(salt, abi.encodePacked());
Vault.sol#L60 leaf
is not necessary and can be replaced by
- bytes32 leaf = keccak256(abi.encode(msg.sender, _target, selector)); - if (!MerkleProof.verify(_proof, merkleRoot, leaf)) { + if (!MerkleProof.verify(_proof, merkleRoot, keccak256(abi.encode(msg.sender, _target, selector)))) {
VaultFactory#L46 'data' is not necessary and can be replaced by
- bytes32 data = keccak256( - abi.encodePacked(bytes1(0xff), address(this), salt, creationHash) - ); - vault = address(uint160(uint256(data))); + vault = address(uint160(uint256(keccak256( + abi.encodePacked(bytes1(0xff), address(this), salt, creationHash) + ))));
-migrationInfo[_vault][_proposalId].fractionsMigrated = true; +proposal.fractionsMigrated = true;
Functions with onlyController
& onlyRegistry
modifier in FERC1155.sol
- function setContractURI(string calldata _uri) external onlyController { + function setContractURI(string calldata _uri) external payable onlyController { - function setMetadata(address _metadata, uint256 _id) external onlyController { + function setMetadata(address _metadata, uint256 _id) external payable onlyController { - function setRoyalties( uint256 _id, address _receiver, uint256 _percentage ) external onlyController { + function setRoyalties(uint256 _id, address _receiver, uint256 _percentage ) external payable onlyController { - function transferController(address _newController) external onlyController { + function transferController(address _newController) external payable onlyController { - function burn(address _from, uint256 _id, uint256 _amount) external onlyRegistry { + function burn(address _from, uint256 _id, uint256 _amount) external payable onlyRegistry { - function mint(address _to, uint256 _id, uint256 _amount, bytes memory _data) external onlyRegistry { + function mint(address _to, uint256 _id, uint256 _amount, bytes memory _data) external payable onlyRegistry {
>> 1
instead of / 2
to save gas. Right shift x >> y is same as x / 2**y.- result = new bytes32[](length / 2 + 1); + result = new bytes32[]((length >> 2) + 1); . . - result = new bytes32[](length / 2); + result = new bytes32[](length >> 2); . . - _node = _node / 2; + _node = _node >> 2;
unchecked
block for where arithmetic overflow cannot happenIn cash()
function in Buyout.sol#L244:
+ unchecked { + uint256 totalSupply = IVaultRegistry(registry).totalSupply(_vault); + uint256 buyoutShare = (tokenBalance * ethBalance) / + (totalSupply + tokenBalance); + _sendEthOrWeth(msg.sender, buyoutShare); + // Emits event for cashing out of buyout pool + emit Cash(_vault, msg.sender, buyoutShare); + }
In sellFractions()
function in Buyout.sol#L112
+ unchecked { + uint256 endTime = startTime + PROPOSAL_PERIOD; + if (block.timestamp > endTime) + revert TimeExpired(block.timestamp, endTime); + }
In buyFractions()
function in Buyout.sol#L149
+ unchecked { + uint256 endTime = startTime + REJECTION_PERIOD; + if (block.timestamp > endTime) + revert TimeExpired(block.timestamp, endTime); + }
In end()
function in Buyout.sol#L184
+ unchecked { + uint256 endTime = startTime + REJECTION_PERIOD; + if (block.timestamp <= endTime) + revert TimeNotElapsed(block.timestamp, endTime); + + uint256 tokenBalance = IERC1155(token).balanceOf(address(this), id); + // Checks totalSupply of auction pool to determine if buyout is successful or not + if ( + (tokenBalance * 1000) / + IVaultRegistry(registry).totalSupply(_vault) > + 500 + ) { + // Initializes vault transaction + bytes memory data = abi.encodeCall( + ISupply.burn, + (address(this), tokenBalance) + ); + // Executes burn of fractional tokens from pool + IVault(payable(_vault)).execute(supply, data, _burnProof); + // Sets buyout state to successful + buyoutInfo[_vault].state = State.SUCCESS; + // Emits event for ending successful auction + emit End(_vault, State.SUCCESS, proposer); + } else { + // Deletes auction info + delete buyoutInfo[_vault]; + // Transfers fractions and ether back to proposer of the buyout pool + IERC1155(token).safeTransferFrom( + address(this), + proposer, + id, + tokenBalance, + "" + ); + _sendEthOrWeth(proposer, ethBalance); + // Emits event for ending unsuccessful auction + emit End(_vault, State.INACTIVE, proposer); + }
In the proposal()
function in Migration.sol#L72
+ unchecked { + Proposal storage proposal = migrationInfo[_vault][++nextId]; + proposal.startTime = block.timestamp; + proposal.targetPrice = _targetPrice; + proposal.modules = _modules; + proposal.plugins = _plugins; + proposal.selectors = _selectors; + proposal.oldFractionSupply = IVaultRegistry(registry).totalSupply( + _vault + ); + proposal.newFractionSupply = _newFractionSupply; + }
a = a + b
instead of a += b
for state variables saves gas- proposal.totalEth += msg.value; + proposal.totalEth = proposal.totalEth + msg.value;
- proposal.totalFractions += msg.value; + proposal.totalFractions = proposal.totalFractions + msg.value;
- for (uint256 i; i < permissions.length; ) { + uint256 length; + for (uint256 i; i < length; ) {