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