SIZE contest - Rolezn's results

An on-chain sealed bid auction protocol.

General Information

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

SIZE

Findings Distribution

Researcher Performance

Rank: 62/88

Findings: 1

Award: $21.13

Gas:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

21.132 USDC - $21.13

Labels

bug
G (Gas Optimization)
grade-b
edited-by-warden
G-10

External Links

Summary<a name="Summary">

Gas Optimizations

IssueContextsEstimated Gas Saved
GAS‑1++i/i++ Should Be unchecked{++i}/unchecked{i++} When It Is Not Possible For Them To Overflow, As Is The Case When Used In For- And While-loops2-
GAS‑2abi.encode() is less efficient than abi.encodepacked()3-
GAS‑3Use calldata instead of memory for function parameters1300
GAS‑4Public Functions To External1-
GAS‑5Optimize names to save gas122
GAS‑6Use local variable instead of storage variable for event emit3300
GAS‑7Cache/precompute repeated calculations2200

Total: 11 contexts over 6 issues

Gas Optimizations

<a href="#Summary">[GAS‑1]</a><a name="GAS&#x2011;1"> ++i/i++ Should Be unchecked{++i}/unchecked{i++} When It Is Not Possible For Them To Overflow, As Is The Case When Used In For- And While-loops

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

<ins>Proof Of Concept</ins>
244: for (uint256 i; i < bidIndices.length; i++) {

https://github.com/code-423n4/2022-11-size/blob/main/src/SizeSealed.sol#L244

302: for (uint256 i; i < seenBidMap.length - 1; i++) {

https://github.com/code-423n4/2022-11-size/blob/main/src/SizeSealed.sol#L302

<a href="#Summary">[GAS‑2]</a><a name="GAS&#x2011;2"> abi.encode() is less efficient than abi.encodepacked()

See for more information: https://github.com/ConnorBlockchain/Solidity-Encode-Gas-Comparison

<ins>Proof Of Concept</ins>
467: return keccak256(abi.encode(message));

https://github.com/code-423n4/2022-11-size/blob/main/src/SizeSealed.sol#L467

26: bytes memory data = abi.encode(point, scalar);

https://github.com/code-423n4/2022-11-size/blob/main/src/util/ECCMath.sol#L26

61: return keccak256(abi.encode(point));

https://github.com/code-423n4/2022-11-size/blob/main/src/util/ECCMath.sol#L61

<a href="#Summary">[GAS‑3]</a><a name="GAS&#x2011;3"> Use calldata instead of memory for function parameters

In some cases, having function arguments in calldata instead of memory is more optimal.

Consider the following generic example:

contract C { function add(uint[] memory arr) external returns (uint sum) { uint length = arr.length; for (uint i = 0; i < arr.length; i++) { sum += arr[i]; } } }

In the above example, the dynamic array arr has the storage location memory. When the function gets called externally, the array values are kept in calldata and copied to memory during ABI decoding (using the opcode calldataload and mstore). And during the for loop, arr[i] accesses the value in memory using a mload. However, for the above example this is inefficient. Consider the following snippet instead:

contract C { function add(uint[] calldata arr) external returns (uint sum) { uint length = arr.length; for (uint i = 0; i < arr.length; i++) { sum += arr[i]; } } }

In the above snippet, instead of going via memory, the value is directly read from calldata using calldataload. That is, there are no intermediate memory operations that carries this value.

Gas savings: In the former example, the ABI decoding begins with copying value from calldata to memory in a for loop. Each iteration would cost at least 60 gas. In the latter example, this can be completely avoided. This will also reduce the number of instructions and therefore reduces the deploy time cost of the contract.

In short, use calldata instead of memory if the function argument is only read.

Note that in older Solidity versions, changing some function arguments from memory to calldata may cause "unimplemented feature error". This can be avoided by using a newer (0.8.*) Solidity compiler.

Examples Note: The following pattern is prevalent in the codebase:

function f(bytes memory data) external { (...) = abi.decode(data, (..., types, ...)); }

Here, changing to bytes calldata will decrease the gas. The total savings for this change across all such uses would be quite significant.

<ins>Proof Of Concept</ins>
function finalize(uint256 auctionId, uint256[] memory bidIndices, uint128 clearingBase, uint128 clearingQuote) public atState(idToAuction[auctionId], States.RevealPeriod) {

https://github.com/code-423n4/2022-11-size/blob/main/src/SizeSealed.sol#L217

<a href="#Summary">[GAS‑4]</a><a name="GAS&#x2011;4"> Public Functions To External

The following functions could be set external to save gas and improve code quality. External call cost is less expensive than of public functions.

<ins>Proof Of Concept</ins>
function computeCommitment(bytes32 message) public pure returns (bytes32) {

https://github.com/code-423n4/2022-11-size/blob/main/src/SizeSealed.sol#L466

<a href="#Summary">[GAS‑5]</a><a name="GAS&#x2011;5"> Optimize names to save gas

public/external function names and public member variable names can be optimized to save gas. See this link for an example of how it works. Method IDs that have two leading zero bytes can save 128 gas each during deployment, and renaming functions to have lower method IDs will save 22 gas per call, per sorted position shifted

<ins>Proof Of Concept</ins>
File: \2022-11-size\src\SizeSealed.sol

https://github.com/code-423n4/2022-11-size/blob/main/src/SizeSealed.sol

<a href="#Summary">[GAS‑6]</a><a name="GAS&#x2011;6"> Use local variable instead of storage variable for event emit

<ins>Proof Of Concept</ins>
[-] emit AuctionCreated(auctionId, msg.sender, auctionParams, timings, encryptedSellerPrivKey); [+] emit AuctionCreated(auctionId, msg.sender, a.params, a.timings, encryptedSellerPrivKey);

https://github.com/code-423n4/2022-11-size/blob/main/src/SizeSealed.sol#L107

[-] msg.sender, auctionId, bidIndex, quoteAmount, commitment, pubKey, encryptedMessage, encryptedPrivateKey [+] msg.sender, auctionId, bidIndex, ebid.quoteAmount, ebid.commitment, ebid.pubKey, ebid.encryptedMessage, encryptedPrivateKey

https://github.com/code-423n4/2022-11-size/blob/main/src/SizeSealed.sol#L166

[-] emit RevealedKey(auctionId, privateKey); [+] emit RevealedKey(auctionId, a.data.privKey);

https://github.com/code-423n4/2022-11-size/blob/main/src/SizeSealed.sol#L193

<a href="#Summary">[GAS‑7]</a><a name="GAS&#x2011;7"> Cache/precompute repeated calculations

<ins>Proof Of Concept</ins>
290: baseAmount = data.totalBaseAmount - data.filledBase; 319: uint128 unsoldBase = data.totalBaseAmount - data.filledBase;

https://github.com/code-423n4/2022-11-size/blob/main/src/SizeSealed.sol#L290 https://github.com/code-423n4/2022-11-size/blob/main/src/SizeSealed.sol#L319

302: seenBidMap.length - 1 309: seenBidMap.length - 1

https://github.com/code-423n4/2022-11-size/blob/main/src/SizeSealed.sol#L302 https://github.com/code-423n4/2022-11-size/blob/main/src/SizeSealed.sol#L309

#0 - c4-judge

2022-11-10T02:19:29Z

0xean marked the issue as grade-b

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter