code4rena-2022-08-nounsdao-g03

[G‑03] Structs can be packed into fewer storage slots

보고서

Summary

struct 구조가 최적화되어 있지 않음을 지적했다. struct를 최적화하여 적은 slot을 사용하고, 이로인해 gas를 절약하라 제안했다.

Keyword

gas optimization, storage optimization, struct

Vulnerability

/// @audit Variable ordering with 15 slots instead of the current 16:
///           uint256(32):id, uint256(32):proposalThreshold, uint256(32):quorumVotes, uint256(32):eta, address[](32):targets, uint256[](32):values, string[](32):signatures, bytes[](32):calldatas, uint256(32):startBlock, uint256(32):endBlock, uint256(32):forVotes, uint256(32):againstVotes, uint256(32):abstainVotes, mapping(32):receipts, address(20):proposer, bool(1):canceled, bool(1):vetoed, bool(1):executed
174       struct Proposal {
175           /// @notice Unique id for looking up a proposal
176           uint256 id;
177           /// @notice Creator of the proposal
178           address proposer;
179           /// @notice The number of votes needed to create a proposal at the time of proposal creation. *DIFFERS from GovernerBravo
180           uint256 proposalThreshold;
181           /// @notice The number of votes in support of a proposal required in order for a quorum to be reached and for a vote to succeed at the time of proposal creation. *DIFFERS from GovernerBravo
182           uint256 quorumVotes;
183           /// @notice The timestamp that the proposal will be available for execution, set once the vote succeeds
184           uint256 eta;
185           /// @notice the ordered list of target addresses for calls to be made
186           address[] targets;
187           /// @notice The ordered list of values (i.e. msg.value) to be passed to the calls to be made
188           uint256[] values;
189           /// @notice The ordered list of function signatures to be called
190           string[] signatures;
191           /// @notice The ordered list of calldata to be passed to each call
192           bytes[] calldatas;
193           /// @notice The block at which voting begins: holders must delegate their votes prior to this block
194           uint256 startBlock;
195           /// @notice The block at which voting ends: votes must be cast prior to this block
196           uint256 endBlock;
197           /// @notice Current number of votes in favor of this proposal
198           uint256 forVotes;
199           /// @notice Current number of votes in opposition to this proposal
200           uint256 againstVotes;
201           /// @notice Current number of votes for abstaining for this proposal
202           uint256 abstainVotes;
203           /// @notice Flag marking whether the proposal has been canceled
204           bool canceled;
205           /// @notice Flag marking whether the proposal has been vetoed
206           bool vetoed;
207           /// @notice Flag marking whether the proposal has been executed
208           bool executed;
209           /// @notice Receipts of ballots for the entire set of voters
210           mapping(address => Receipt) receipts;
211:      }
 
/// @audit Variable ordering with 17 slots instead of the current 18:
///           uint256(32):id, uint256(32):proposalThreshold, uint256(32):quorumVotes, uint256(32):eta, address[](32):targets, uint256[](32):values, string[](32):signatures, bytes[](32):calldatas, uint256(32):startBlock, uint256(32):endBlock, uint256(32):forVotes, uint256(32):againstVotes, uint256(32):abstainVotes, mapping(32):receipts, uint256(32):totalSupply, uint256(32):creationBlock, address(20):proposer, bool(1):canceled, bool(1):vetoed, bool(1):executed
274       struct Proposal {
275           /// @notice Unique id for looking up a proposal
276           uint256 id;
277           /// @notice Creator of the proposal
278           address proposer;
279           /// @notice The number of votes needed to create a proposal at the time of proposal creation. *DIFFERS from GovernerBravo
280           uint256 proposalThreshold;
281           /// @notice The number of votes in support of a proposal required in order for a quorum to be reached and for a vote to succeed at the time of proposal creation. *DIFFERS from GovernerBravo
282           uint256 quorumVotes;
283           /// @notice The timestamp that the proposal will be available for execution, set once the vote succeeds
284           uint256 eta;
285           /// @notice the ordered list of target addresses for calls to be made
286           address[] targets;
287           /// @notice The ordered list of values (i.e. msg.value) to be passed to the calls to be made
288           uint256[] values;
289           /// @notice The ordered list of function signatures to be called
290           string[] signatures;
291           /// @notice The ordered list of calldata to be passed to each call
292           bytes[] calldatas;
293           /// @notice The block at which voting begins: holders must delegate their votes prior to this block
294           uint256 startBlock;
295           /// @notice The block at which voting ends: votes must be cast prior to this block
296           uint256 endBlock;
297           /// @notice Current number of votes in favor of this proposal
298           uint256 forVotes;
299           /// @notice Current number of votes in opposition to this proposal
300           uint256 againstVotes;
301           /// @notice Current number of votes for abstaining for this proposal
302           uint256 abstainVotes;
303           /// @notice Flag marking whether the proposal has been canceled
304           bool canceled;
305           /// @notice Flag marking whether the proposal has been vetoed
306           bool vetoed;
307           /// @notice Flag marking whether the proposal has been executed
308           bool executed;
309           /// @notice Receipts of ballots for the entire set of voters
310           mapping(address => Receipt) receipts;
311           /// @notice The total supply at the time of proposal creation
312           uint256 totalSupply;
313           /// @notice The block at which this proposal was created
314           uint256 creationBlock;
315:      }
 
/// @audit Variable ordering with 12 slots instead of the current 13:
///           uint256(32):id, uint256(32):proposalThreshold, uint256(32):quorumVotes, uint256(32):eta, uint256(32):startBlock, uint256(32):endBlock, uint256(32):forVotes, uint256(32):againstVotes, uint256(32):abstainVotes, uint256(32):totalSupply, uint256(32):creationBlock, address(20):proposer, bool(1):canceled, bool(1):vetoed, bool(1):executed
368       struct ProposalCondensed {
369           /// @notice Unique id for looking up a proposal
370           uint256 id;
371           /// @notice Creator of the proposal
372           address proposer;
373           /// @notice The number of votes needed to create a proposal at the time of proposal creation. *DIFFERS from GovernerBravo
374           uint256 proposalThreshold;
375           /// @notice The minimum number of votes in support of a proposal required in order for a quorum to be reached and for a vote to succeed at the time of proposal creation. *DIFFERS from GovernerBravo
376           uint256 quorumVotes;
377           /// @notice The timestamp that the proposal will be available for execution, set once the vote succeeds
378           uint256 eta;
379           /// @notice The block at which voting begins: holders must delegate their votes prior to this block
380           uint256 startBlock;
381           /// @notice The block at which voting ends: votes must be cast prior to this block
382           uint256 endBlock;
383           /// @notice Current number of votes in favor of this proposal
384           uint256 forVotes;
385           /// @notice Current number of votes in opposition to this proposal
386           uint256 againstVotes;
387           /// @notice Current number of votes for abstaining for this proposal
388           uint256 abstainVotes;
389           /// @notice Flag marking whether the proposal has been canceled
390           bool canceled;
391           /// @notice Flag marking whether the proposal has been vetoed
392           bool vetoed;
393           /// @notice Flag marking whether the proposal has been executed
394           bool executed;
395           /// @notice The total supply at the time of proposal creation
396           uint256 totalSupply;
397           /// @notice The block at which this proposal was created
398           uint256 creationBlock;
399:      }

struct 를 최적화하여 슬롯을 덜 사용하게 조정하자 제안했다. 1슬롯 줄일 때마다 처음 struct 셋팅시 Gsset (20000 gas)이 줄어 가스를 절약할 수 있다고 한다. 이후의 R/W에도 가스를 줄일 수 있다고 한다.

Impact

struct 이 더 많은 slot을 사용하며, 이로 인해 gas를 더 소비하게 된다.

Mitigation

struct을 최적화하여 사용 slot을 줄인다.


tags: bughunting, nouns dao, smart contract, solidity, gas optimization, gas, solidity storage, solidity struct, solidity storage optimization, severity gas