Platform: Code4rena
Start Date: 04/11/2022
Pot Size: $42,500 USDC
Total HM: 9
Participants: 88
Period: 4 days
Judge: 0xean
Total Solo HM: 2
Id: 180
League: ETH
Rank: 60/88
Findings: 1
Award: $21.13
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0x1f8b
Also found by: 0xSmartContract, 0xdeadbeef, Aymen0909, B2, Bnke0x0, Deivitto, Diana, Dinesh11G, JC, RaymondFam, ReyAdmirado, Rolezn, Sathish9098, TomJ, ajtra, aviggiano, chaduke, cryptostellar5, djxploit, gianganhnguyen, gogo, halden, karanctf, leosathya, lukris02, mcwildy, oyc_109, ret2basic, skyle, slowmoses
21.132 USDC - $21.13
Caching the array length outside a loop saves reading it on each iteration, as long as the array's length is not changed during the loop.
2022-11-size/src/SizeSealed.sol::244 => for (uint256 i; i < bidIndices.length; i++) { 2022-11-size/src/SizeSealed.sol::302 => for (uint256 i; i < seenBidMap.length - 1; i++) {
A division/multiplication by any number x being a power of 2 can be calculated by shifting log2(x) to the right/left.
While the DIV opcode uses 5 gas, the SHR opcode only uses 3 gas. Furthermore, Solidity's division operation also includes a division-by-0 prevention which is bypassed using shifting.
2022-11-size/src/SizeSealed.sol::241 => uint256[] memory seenBidMap = new uint256[]((bidIndices.length/256)+1); 2022-11-size/src/SizeSealed.sol::249 => uint256 bitmapIndex = bidIndex / 256;
Use calldata instead of memory for function parameters saves gas if the function argument is only read.
2022-11-size/src/util/ECCMath.sol::25 => function ecMul(Point memory point, uint256 scalar) internal view returns (Point memory) { 2022-11-size/src/util/ECCMath.sol::60 => function hashPoint(Point memory point) internal pure returns (bytes32) {
When using elements that are smaller than 32 bytes, your contract’s gas usage may be higher. This is because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller than that, the EVM must use more operations in order to reduce the size of the element from 32 bytes to the desired size.
2022-11-size/src/SizeSealed.sol::205 => uint128 totalBaseAmount; 2022-11-size/src/SizeSealed.sol::206 => uint128 filledBase; 2022-11-size/src/SizeSealed.sol::365 => uint128 baseAmount = b.filledBaseAmount; 2022-11-size/src/SizeSealed.sol::370 => uint128 baseTokensAvailable = tokensAvailableForWithdrawal(auctionId, baseAmount); 2022-11-size/src/interfaces/ISizeSealed.sol::42 => uint128 quoteAmount; 2022-11-size/src/interfaces/ISizeSealed.sol::43 => uint128 filledBaseAmount; 2022-11-size/src/interfaces/ISizeSealed.sol::44 => uint128 baseWithdrawn; 2022-11-size/src/interfaces/ISizeSealed.sol::56 => uint32 startTimestamp; 2022-11-size/src/interfaces/ISizeSealed.sol::57 => uint32 endTimestamp; 2022-11-size/src/interfaces/ISizeSealed.sol::58 => uint32 vestingStartTimestamp; 2022-11-size/src/interfaces/ISizeSealed.sol::59 => uint32 vestingEndTimestamp; 2022-11-size/src/interfaces/ISizeSealed.sol::60 => uint128 cliffPercent; 2022-11-size/src/interfaces/ISizeSealed.sol::65 => uint128 lowestBase; 2022-11-size/src/interfaces/ISizeSealed.sol::66 => uint128 lowestQuote; 2022-11-size/src/interfaces/ISizeSealed.sol::80 => uint128 totalBaseAmount; 2022-11-size/src/interfaces/ISizeSealed.sol::81 => uint128 minimumBidQuote;
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
2022-11-size/src/SizeSealed.sol::84 => uint256 auctionId = ++currentAuctionId; 2022-11-size/src/SizeSealed.sol::244 => for (uint256 i; i < bidIndices.length; i++) { 2022-11-size/src/SizeSealed.sol::302 => for (uint256 i; i < seenBidMap.length - 1; i++) {
use <x> = <x> + <y> or <x> = <x> - <y> instead to save gas
2022-11-size/src/SizeSealed.sol::294 => data.filledBase += baseAmount; 2022-11-size/src/SizeSealed.sol::373 => b.baseWithdrawn += baseTokensAvailable;
use abi.encodePacked() where possible to save gas
2022-11-size/src/SizeSealed.sol::467 => return keccak256(abi.encode(message)); 2022-11-size/src/util/ECCMath.sol::26 => bytes memory data = abi.encode(point, scalar); 2022-11-size/src/util/ECCMath.sol::61 => return keccak256(abi.encode(point));
++i costs less gas than i++, especially when it's used in for-loops (--i/i-- too) Saves 5 gas PER LOOP
2022-11-size/src/SizeSealed.sol::244 => for (uint256 i; i < bidIndices.length; i++) { 2022-11-size/src/SizeSealed.sol::302 => for (uint256 i; i < seenBidMap.length - 1; i++) {
It is not necessary to have both a named return and a return statement.
2022-11-size/src/util/ECCMath.sol::54 => returns (bytes32 decryptedMessage)
When fetching data from a storage location, assigning the data to a memory variable causes all fields of the struct/array to be read from storage, which incurs a Gcoldsload (2100 gas) for each field of the struct/array. If the fields are read from the new memory variable, they incur an additional MLOAD rather than a cheap stack read.
Instead of declearing the variable with the memory keyword, declaring the variable with the storage keyword and caching any fields that need to be re-read in stack variables, will be much cheaper, only incuring the Gcoldsload for the fields actually read. The only time it makes sense to read the whole struct/array into a memory variable, is if the full struct/array is being returned by the function, is being passed to a function that requires memory, or if the array/struct is being read from another memory array/struct
2022-11-size/src/SizeSealed.sol::241 => uint256[] memory seenBidMap = new uint256[]((bidIndices.length/256)+1);
Not inlining costs 20 to 40 gas because of two extra JUMP instructions and additional stack operations needed for function calls.
2022-11-size/src/util/ECCMath.sol::18 => function publicKey(uint256 privateKey) internal view returns (Point memory) {
#0 - c4-judge
2022-11-10T02:20:36Z
0xean marked the issue as grade-b