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: 65/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
The following gas optimization issues were found during the code audit:
calldata
instead of memory
(8 instances)<array>.length
(2 instances)unchecked{}
to suppress overflow/underflow check (2 instances)++i
/--i
instead of i++
/i--
(2 instances)abi.encodePacked()
instead of abi.encode()
(3 instances)Total 6 instances of 19 issues.
calldata
instead of memory
(8 instances)When a function with a memory
array is called externally, the abi.decode()
step has to use a for loop to copy each index of the calldata
to the memory
index. This overhead can be optimized by using calldata
directly.
src/SizeSealed.sol::217 => function finalize(uint256 auctionId, uint256[] memory bidIndices, uint128 clearingBase, uint128 clearingQuote) src/SizeSealed.sol::474 => function getTimings(uint256 auctionId) external view returns (Timings memory timings) { src/SizeSealed.sol::478 => function getAuctionData(uint256 auctionId) external view returns (AuctionData memory data) { src/util/ECCMath.sol::18 => function publicKey(uint256 privateKey) internal view returns (Point memory) { src/util/ECCMath.sol::25 => function ecMul(Point memory point, uint256 scalar) internal view returns (Point memory) { src/util/ECCMath.sol::37 => function encryptMessage(Point memory encryptToPub, uint256 encryptWithPriv, bytes32 message) src/util/ECCMath.sol::51 => function decryptMessage(Point memory sharedPoint, bytes32 encryptedMessage) src/util/ECCMath.sol::60 => function hashPoint(Point memory point) internal pure returns (bytes32) {
<array>.length
(2 instances)If <array>.length
is used as for loop termination condition, then the .length
method will be called in each iteration. Caching it in a local variable can save gas.
src/SizeSealed.sol::244 => for (uint256 i; i < bidIndices.length; i++) { src/SizeSealed.sol::302 => for (uint256 i; i < seenBidMap.length - 1; i++) {
unchecked{}
to suppress overflow/underflow check (2 instances)Starting from version 0.8.0, Solidity does overflow/underflow checks by default. It is a good feature to prevent vulnerabilities but it has a significant overhead, especially when used in for loop. When using uint256/int256, it is extremely hard to trigger overflow, so it makes sense to skip these checks. To suppress the overflow/underflow checks, use unchecked {}
. For increment situations, just use unchecked {}
directly; for decrement situations, add a require()
statement before decrementing to prevent underflow.
src/SizeSealed.sol::244 => for (uint256 i; i < bidIndices.length; i++) { src/SizeSealed.sol::302 => for (uint256 i; i < seenBidMap.length - 1; i++) {
++i
/--i
instead of i++
/i--
(2 instances)Using ++i
/--i
saves 6 gas per loop.
src/SizeSealed.sol::244 => for (uint256 i; i < bidIndices.length; i++) { src/SizeSealed.sol::302 => for (uint256 i; i < seenBidMap.length - 1; i++) {
abi.encodePacked()
instead of abi.encode()
(3 instances)abi.encodePacked()
is more efficient than abi.encode()
.
src/SizeSealed.sol::467 => return keccak256(abi.encode(message)); src/util/ECCMath.sol::26 => bytes memory data = abi.encode(point, scalar); src/util/ECCMath.sol::61 => return keccak256(abi.encode(point));
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.
src/SizeSealed.sol::241 => uint256[] memory seenBidMap = new uint256[]((bidIndices.length/256)+1); src/SizeSealed.sol::249 => uint256 bitmapIndex = bidIndex / 256;
#0 - c4-judge
2022-11-10T02:25:43Z
0xean marked the issue as grade-b