Platform: Code4rena
Start Date: 12/08/2022
Pot Size: $35,000 USDC
Total HM: 10
Participants: 126
Period: 3 days
Judge: Justin Goro
Total Solo HM: 3
Id: 154
League: ETH
Rank: 67/126
Findings: 2
Award: $44.86
๐ Selected for report: 0
๐ Solo Findings: 0
๐ Selected for report: oyc_109
Also found by: 0x1f8b, 0x52, 0xDjango, 0xLovesleep, 0xNazgul, 0xNineDec, 0xbepresent, 0xmatt, 0xsolstars, Aymen0909, Bahurum, Bnke0x0, CertoraInc, Chom, CodingNameKiki, DecorativePineapple, Deivitto, Dravee, ElKu, Funen, GalloDaSballo, IllIllI, JC, JohnSmith, Junnon, KIntern_NA, Lambda, LeoS, MiloTruck, Noah3o6, PaludoX0, RedOneN, Respx, ReyAdmirado, Rohan16, RoiEvenHaim, Rolezn, Ruhum, Sm4rty, TomJ, Vexjon, Waze, Yiko, __141345__, a12jmx, ajtra, ak1, apostle0x01, asutorufos, auditor0517, bin2chen, bobirichman, brgltd, bulej93, byndooa, c3phas, cRat1st0s, cryptphi, csanuragjain, d3e4, defsec, delfin454000, djxploit, durianSausage, ellahi, erictee, exd0tpy, fatherOfBlocks, gogo, jonatascm, ladboy233, medikko, mics, natzuu, neumo, p_crypt0, paribus, pfapostol, rbserver, reassor, ret2basic, robee, rokinot, rvierdiiev, sach1r0, saneryee, seyni, sikorico, simon135, sseefried, wagmi, wastewa
29.8918 USDC - $29.89
Blocklist.sol,15,16
manager = _manager; ve = _ve;
VotingEscrow.sol, 107,120,121,141,148,155
Blocklist.sol,39-41
assembly { size := extcodesize(addr) }
we have the following form codes.
size = address(addr).code.length
#0 - gititGoro
2022-09-04T04:49:05Z
Duplicate of #255
๐ Selected for report: IllIllI
Also found by: 0x040, 0x1f8b, 0xDjango, 0xHarry, 0xLovesleep, 0xNazgul, 0xNineDec, 0xSmartContract, 0xackermann, 0xbepresent, 2997ms, Amithuddar, Aymen0909, Bnke0x0, CRYP70, CertoraInc, Chom, CodingNameKiki, Deivitto, Dravee, ElKu, Fitraldys, Funen, GalloDaSballo, JC, JohnSmith, Junnon, LeoS, Metatron, MiloTruck, Noah3o6, NoamYakov, PaludoX0, RedOneN, Respx, ReyAdmirado, Rohan16, Rolezn, Ruhum, Sm4rty, SooYa, SpaceCake, TomJ, Tomio, Waze, Yiko, __141345__, a12jmx, ajtra, ak1, apostle0x01, asutorufos, bobirichman, brgltd, bulej93, c3phas, cRat1st0s, carlitox477, chrisdior4, csanuragjain, d3e4, defsec, delfin454000, djxploit, durianSausage, ellahi, erictee, fatherOfBlocks, gerdusx, gogo, ignacio, jag, ladboy233, m_Rassska, medikko, mics, natzuu, newfork01, oyc_109, paribus, pfapostol, rbserver, reassor, ret2basic, robee, rokinot, rvierdiiev, sach1r0, saian, sashik_eth, sikorico, simon135
14.9678 USDC - $14.97
Custom errors from Solidity 0.8.4 are cheaper than revert strings (cheaper deployment cost and runtime cost when the revert condition is met) while providing the same amount of information, as explained https://blog.soliditylang.org/2021/04/21/custom-errors/. Custom errors are defined using the error statement.
features/Blocklist.sol, 24, b' require(msg.sender == manager, "Only manager");' features/Blocklist.sol, 25, b' require(isContract(addr), "Only contracts");' VotingEscrow.sol, 140, b' require(msg.sender == owner, "Only owner");' VotingEscrow.sol, 147, b' require(msg.sender == owner, "Only owner");' VotingEscrow.sol, 154, b' require(msg.sender == owner, "Only owner");' VotingEscrow.sol, 162, b' require(msg.sender == owner, "Only owner");' VotingEscrow.sol, 171, b' require(msg.sender == blocklist, "Only Blocklist");' VotingEscrow.sol, 412, b' require(value > 0, "Only non zero amount");' VotingEscrow.sol, 413, b' require(locked.amount == 0, "Lock exists");' VotingEscrow.sol, 414, b' require(unlock_time >= locked.end, "Only increase lock end"); // from using quitLock, user should increaseAmount instead' VotingEscrow.sol, 415, b' require(unlock_time > block.timestamp, "Only future lock end");' VotingEscrow.sol, 416, b' require(unlock_time <= block.timestamp + MAXTIME, "Exceeds maxtime");' VotingEscrow.sol, 425, b' require(' VotingEscrow.sol, 448, b' require(value > 0, "Only non zero amount");' VotingEscrow.sol, 449, b' require(locked.amount > 0, "No lock");' VotingEscrow.sol, 450, b' require(locked_.end > block.timestamp, "Lock expired");' VotingEscrow.sol, 469, b' require(locked_.amount > 0, "Delegatee has no lock");' VotingEscrow.sol, 470, b' require(locked_.end > block.timestamp, "Delegatee lock expired");' VotingEscrow.sol, 485, b' require(' VotingEscrow.sol, 502, b' require(locked_.amount > 0, "No lock");' VotingEscrow.sol, 503, b' require(unlock_time > locked_.end, "Only increase lock end");' VotingEscrow.sol, 504, b' require(unlock_time <= block.timestamp + MAXTIME, "Exceeds maxtime");' VotingEscrow.sol, 511, b' require(oldUnlockTime > block.timestamp, "Lock expired");' VotingEscrow.sol, 529, b' require(locked_.amount > 0, "No lock");' VotingEscrow.sol, 530, b' require(locked_.end <= block.timestamp, "Lock not expired");' VotingEscrow.sol, 531, b' require(locked_.delegatee == msg.sender, "Lock delegated");' VotingEscrow.sol, 546, b' require(token.transfer(msg.sender, value), "Transfer failed");' VotingEscrow.sol, 563, b' require(!IBlocklist(blocklist).isBlocked(addr), "Blocked contract");' VotingEscrow.sol, 564, b' require(locked.amount > 0, "No lock");' VotingEscrow.sol, 565, b' require(locked_.delegatee != addr, "Already delegated");' VotingEscrow.sol, 587, b' require(toLocked.amount > 0, "Delegatee has no lock");' VotingEscrow.sol, 588, b' require(toLocked.end > block.timestamp, "Delegatee lock expired");' VotingEscrow.sol, 589, b' require(toLocked.end >= fromLocked.end, "Only delegate to longer lock");' VotingEscrow.sol, 635, b' require(locked.amount > 0, "No lock");' VotingEscrow.sol, 636, b' require(locked_.end > block.timestamp, "Lock expired");' VotingEscrow.sol, 637, b' require(locked_.delegatee == msg.sender, "Lock delegated");' VotingEscrow.sol, 657, b' require(token.transfer(msg.sender, remainingAmount), "Transfer failed");' VotingEscrow.sol, 676, b' require(token.transfer(penaltyRecipient, amount), "Transfer failed");' VotingEscrow.sol, 776, b' require(_blockNumber <= block.number, "Only past block number");' VotingEscrow.sol, 877, b' require(_blockNumber <= block.number, "Only past block number");'
0 is less gas efficient than !0 if you enable the optimizer at 10k AND youโre in a require statement. Detailed explanation with the opcodes https://twitter.com/gzeon/status/1485428085885640706
features/Blocklist.sol, 42, b' return size > 0;' VotingEscrow.sol, 288, b' if (epoch > 0) {' VotingEscrow.sol, 412, b' require(_value > 0, "Only non zero amount");' VotingEscrow.sol, 448, b' require(_value > 0, "Only non zero amount");'
prefix increment ++i is more cheaper than postfix i++
VotingEscrow.sol, 309, b' for (uint256 i = 0; i < 255; i++) {' VotingEscrow.sol, 717, b' for (uint256 i = 0; i < 128; i++) {' VotingEscrow.sol, 739, b' for (uint256 i = 0; i < 128; i++) {' VotingEscrow.sol, 834, b' for (uint256 i = 0; i < 255; i++) {'
VotingEscrow.sol, 654, b' penaltyAccumulated += penaltyAmount;'
// Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled.
features/Blocklist.sol, 10, b' mapping(address => bool) private _blocklist;'
resign the default value to the variables will cost more gas.
VotingEscrow.sol, 229, b' int128 oldSlopeDelta = 0;' VotingEscrow.sol, 230, b' int128 newSlopeDelta = 0;' VotingEscrow.sol, 298, b' uint256 blockSlope = 0; // dblock/dt' VotingEscrow.sol, 313, b' int128 dSlope = 0;' VotingEscrow.sol, 309, b' for (uint256 i = 0; i < 255; i++) {' VotingEscrow.sol, 714, b' uint256 min = 0;' VotingEscrow.sol, 717, b' for (uint256 i = 0; i < 128; i++) {' VotingEscrow.sol, 737, b' uint256 min = 0;' VotingEscrow.sol, 739, b' for (uint256 i = 0; i < 128; i++) {' VotingEscrow.sol, 793, b' uint256 dBlock = 0;' VotingEscrow.sol, 794, b' uint256 dTime = 0;' VotingEscrow.sol, 836, b' int128 dSlope = 0;' VotingEscrow.sol, 834, b' for (uint256 i = 0; i < 255; i++) {' VotingEscrow.sol, 889, b' uint256 dTime = 0;'
We can save getter function of public constants.
VotingEscrow.sol, 46-48
Custom errors are available from solidity version 0.8.4. Custom errors save ~50 gas each time theyโre hitby avoiding having to allocate and store the revert string. Not defining the strings also save deployment gas
VotingEscrow.sol, 140,147,154,162
If a function modifier such as onlyOwner is used, the function will revert if a normal user tries to pay the function. Marking the function as payable will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided. The extra opcodes avoided are CALLVALUE(2),DUP1(3),ISZERO(3),PUSH2(3),JUMPI(10),PUSH1(3),DUP1(3),REVERT(0),JUMPDEST(1),POP(2), which costs an average of about 21 gas per call to the function, in addition to the extra deployment cost
VotingEscrow.sol, 140,147,154,162 Blocklist.sol, 23
Use a solidity version of at least 0.8.2 to get simple compiler automatic inlining Use a solidity version of at least 0.8.3 to get better struct packing and cheaper multiple storage reads Use a solidity version of at least 0.8.4 to get custom errors, which are cheaper at deployment than revert()/require() strings Use a solidity version of at least 0.8.10 to have external calls skip contract existence checks if the external call has a return value.
The unchecked keyword is new in solidity version 0.8.0, so this only applies to that version or higher, which these instances are. This saves 30-40 gas per loop
VotingEscrow.sol, 309, b' for (uint256 i = 0; i < 255; i++) {' VotingEscrow.sol, 717, b' for (uint256 i = 0; i < 128; i++) {' VotingEscrow.sol, 739, b' for (uint256 i = 0; i < 128; i++) {' VotingEscrow.sol, 834, b' for (uint256 i = 0; i < 255; i++) {'