Platform: Code4rena
Start Date: 06/09/2022
Pot Size: $90,000 USDC
Total HM: 33
Participants: 168
Period: 9 days
Judge: GalloDaSballo
Total Solo HM: 10
Id: 157
League: ETH
Rank: 67/168
Findings: 3
Award: $142.36
π Selected for report: 0
π Solo Findings: 0
https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/token/metadata/MetadataRenderer.sol#L179-L194 https://github.com/code-423n4/2022-09-nouns-builder/blob/7e9fddbbacdd7d7812e912a369cfd862ee67dc03/src/token/metadata/MetadataRenderer.sol#L93
If the owner adds more than 16 properties, it will cause a denial of service.
If the owner, due to a failure to re-prioritize a tx, resubmits the addProperties
transaction, or directly send a transaction with more than 16 _names
calling MetadataRenderer.addProperties
, these properties will remain stored forever without the possibility of being removed from the array properties
.
for (uint256 i = 0; i < numNewProperties; ++i) { // Append storage space properties.push(); // Get the new property id uint256 propertyId = numStoredProperties + i; // Store the property name properties[propertyId].name = _names[i]; emit PropertyAdded(propertyId, _names[i]); }
Later, when onMinted
is called on the token, service will be denied since the array of tokenAttributes
is a maximum of 16 entries.
uint16[16] storage tokenAttributes = attributes[_tokenId]; // Cache the total number of properties available uint256 numProperties = properties.length; // Store the total as reference in the first slot of the token's array of attributes tokenAttributes[0] = uint16(numProperties); unchecked { // For each property: for (uint256 i = 0; i < numProperties; ++i) { // Get the number of items to choose from uint256 numItems = properties[i].items.length; // Use the token's seed to select an item tokenAttributes[i + 1] = uint16(seed % numItems); // Adjust the randomness seed >>= 16; } }
attributes
or limit the properties in MetadataRenderer.addProperties
.#0 - GalloDaSballo
2022-09-20T18:42:57Z
Dup of #523
π Selected for report: Lambda
Also found by: 0x1337, 0x1f8b, 0x4non, 0x85102, 0xA5DF, 0xNazgul, 0xSmartContract, 0xbepresent, 0xc0ffEE, 8olidity, Aymen0909, B2, Bnke0x0, CRYP70, Captainkay, CertoraInc, Ch_301, Chom, ChristianKuri, CodingNameKiki, Deivitto, Diana, DimitarDimitrov, ElKu, EthLedger, Franfran, Funen, GimelSec, JansenC, Jeiwan, Jujic, Lead_Belly, MEP, MasterCookie, MiloTruck, Noah3o6, PPrieditis, PaludoX0, Picodes, PwnPatrol, R2, Randyyy, RaymondFam, Respx, ReyAdmirado, Rolezn, Samatak, Tointer, Tomo, V_B, Waze, _Adam, __141345__, a12jmx, ak1, asutorufos, azephiar, ballx, bharg4v, bin2chen, bobirichman, brgltd, bulej93, c3phas, cccz, ch0bu, cloudjunky, cryptonue, cryptostellar5, cryptphi, csanuragjain, d3e4, datapunk, davidbrai, delfin454000, dharma09, dic0de, dipp, djxploit, eierina, erictee, fatherOfBlocks, gogo, hansfriese, hyh, imare, indijanc, izhuer, jonatascm, ladboy233, leosathya, lucacez, lukris02, m9800, martin, minhtrng, ne0n, neumo, oyc_109, p_crypt0, pashov, pauliax, pcarranzav, pedr02b2, peritoflores, pfapostol, rbserver, ret2basic, robee, rvierdiiev, sach1r0, sahar, scaraven, sikorico, simon135, slowmoses, sorrynotsorry, tnevler, tonisives, volky, yixxas, zkhorse, zzzitron
60.8947 USDC - $60.89
The pragma version used are:
pragma solidity ^0.8.15; pragma solidity 0.8.15; pragma solidity ^0.8.4; pragma solidity ^0.8.0;
Note that mixing pragma are not recommended. Because different compiler versions have different meanings and behaviors, it also significantly raises maintenance costs. As a result, depending on the compiler version selected for any given file, deployed contracts may have security issues.
The minimum required version must be 0.8.16; otherwise, contracts will be affected by the following important bug fixes:
abi.encodeCall
in place of fixed bytes arguments.calldatasize()
in all cases.bytes
arrays.Apart from these, there are several minor bug fixes and improvements.
payable
unnecessarilyThe following methods have been marked as payable
, this may cause both administrators and users calling them to accidentally deposit ether with no recovery possible.
This may be done to save gas, but may result in more loss than benefit in case of one human error.
Affected source code:
Keep in mind that the version of solidity used, despite being greater than 0.8
, does not prevent integer overflows during casting, it only does so in mathematical operations.
It is necessary to safely convert between the different numeric types.
Example:
A dangerous example is with the quorum
method.
proposal.quorumVotes = uint32(quorum());
This method depends the token's totalSupply
, so it could overflow with certains tokens.
function quorum() public view returns (uint256) { unchecked { return (settings.token.totalSupply() * settings.quorumThresholdBps) / 10_000; } }
Recommendation:
Use a safeCast from Open Zeppelin.
Affected source code:
The following methods have a lack of checks if the received argument is an address, it's good practice in order to reduce human error to check that the address specified in the constructor or initialize is different than address(0)
.
Affected source code for address(0)
:
It wasn't checked _delay
max value (E.g. more than 1 month...):
The updateVotingDelay
, updateVotingPeriod
, updateProposalThresholdBps
, updateQuorumThresholdBps
, updateVetoer
, burnVetoer
methods in the Governor
contract have the onlyOwner
modifier, but to be fully decentralized, these methods must verify that the address is the same as the contract himself to force a proposal to call these methods.
Affected source code:
Negative states must be processed before positive states, otherwise, a proposal that is Expired
or Executed
, but is Active
or Pending
will be returned as Active
or Pending
, this makes it necessary to check that the state is correct from outside this method. I mean, changing outside the variables that alter this state in the correct way.
function state(bytes32 _proposalId) public view returns (ProposalState) { // Get a copy of the proposal Proposal memory proposal = proposals[_proposalId]; // Ensure the proposal exists if (proposal.voteStart == 0) revert PROPOSAL_DOES_NOT_EXIST(); // If the proposal was executed: if (proposal.executed) { return ProposalState.Executed; // Else if the proposal was canceled: } else if (proposal.canceled) { return ProposalState.Canceled; // Else if the proposal was vetoed: } else if (proposal.vetoed) { return ProposalState.Vetoed; // Else if voting has not started: } else if (block.timestamp < proposal.voteStart) { return ProposalState.Pending; + // Else if the proposal can no longer be executed: + } else if (settings.treasury.isExpired(_proposalId)) { + return ProposalState.Expired; - // Else if voting has not ended: - } else if (block.timestamp < proposal.voteEnd) { - return ProposalState.Active; // Else if the proposal failed (outvoted OR didn't reach quorum): } else if (proposal.forVotes < proposal.againstVotes || proposal.forVotes < proposal.quorumVotes) { return ProposalState.Defeated; + // Else if voting has not ended: + } else if (block.timestamp < proposal.voteEnd) { + return ProposalState.Active; // Else if the proposal has not been queued: } else if (settings.treasury.timestamp(_proposalId) == 0) { return ProposalState.Succeeded; - // Else if the proposal can no longer be executed: - } else if (settings.treasury.isExpired(_proposalId)) { - return ProposalState.Expired; // Else the proposal is queued } else { return ProposalState.Queued; } }
Affected source code:
There are three main considerations when using a timestamp to execute a critical function in a contract, especially when actions involve fund transfer.
block.timestamp
.block.number
as a timestamp
block.number
property and average block time, however this is not future proof as block times may change (such as fork reorganisations and the difficulty bomb). In a sale spanning days, the 15-second rule allows one to achieve a more reliable estimate of time.
See SWC-116Reference:
Affected source code:
abstract
for base contractsabstract
contracts are contracts that have at least one function without its implementation. An instance of an abstract cannot be created.
Reference:
Affected source code:
Use Address.toBytes32 instead of replicate the logic.
Affected source code:
It is not good practice to hardcode values, but if you are dealing with addresses much less, these can change between implementations, networks or projects, so it is convenient to remove these addresses from the source code.
use selectors:
Use immutable
with the formula, instead of the result:
The MetadataRenderer.addProperties
method takes advantage when ipfsData
is not defined to transfer the owner to the treasury, however this logic should be done in an initialization method and not in a configuration one, since the user does not expect this type of change when calling to this type of methods, it is convenient to adapt the logic or mention this behavior in a comment.
Affected source code:
In the Auction._handleOutgoingTransfer
method, before throwing the INSOLVENT
error, check if the contract has WETH
, because maybe it's possible to send WETH
instead of an throwing an error.
function _handleOutgoingTransfer(address _to, uint256 _amount) private { // Ensure the contract has enough ETH to transfer if (address(this).balance < _amount) revert INSOLVENT(); // Used to store if the transfer succeeded bool success; assembly { // Transfer ETH to the recipient // Limit the call to 50,000 gas success := call(50000, _to, _amount, 0, 0, 0, 0) } // If the transfer failed: if (!success) { // Wrap as WETH IWETH(WETH).deposit{ value: _amount }(); // Transfer WETH instead IWETH(WETH).transfer(_to, _amount); } }
Affected source code:
#0 - GalloDaSballo
2022-09-15T23:31:35Z
Pretty good, I disagree with the timestamp as while a fluctuation can happen all blocks are coded to have t(n+1) > t(n) meaning that in this case no risk (beside wasting up to 12 seconds post merge) is present
#1 - GalloDaSballo
2022-09-18T20:55:11Z
NC
Disagree as they wanted to save gas, could go either way
L
L
Disagree, I believe admin is supposed to be set to the Governor, will double check
R
Disagree in this case, a 15 second fluctuation doesn't cause any risk nor allow to re-execute the operation
R
NC
NC
This could have been better developed, I'll mark invalid at this time
Disagree as WETH is used as fallback
#2 - GalloDaSballo
2022-09-27T14:28:27Z
2L 2R 3NC
π Selected for report: pfapostol
Also found by: 0x1f8b, 0x4non, 0x5rings, 0xA5DF, 0xSmartContract, 0xc0ffEE, 0xkatana, Aymen0909, Bnke0x0, CertoraInc, Chandr, CodingNameKiki, Cr4ckM3, Deivitto, DimSon, Franfran, JAGADESH, JC, Jeiwan, Lambda, LeoS, Matin, Metatron, Migue, MiloTruck, PPrieditis, PaludoX0, R2, RaymondFam, Respx, ReyAdmirado, Rolezn, Saintcode_, Samatak, SnowMan, StevenL, Tointer, TomJ, Tomo, WatchDogs, Waze, _Adam, __141345__, ajtra, asutorufos, ballx, brgltd, bulej93, c3phas, ch0bu, dharma09, djxploit, durianSausage, easy_peasy, fatherOfBlocks, gianganhnguyen, gogo, imare, leosathya, lucacez, martin, oyc_109, pauliax, peiw, prasantgupta52, ret2basic, rfa, robee, sikorico, simon135, tofunmi, volky, wagmi, zishansami
46.6937 USDC - $46.69
Using compound assignment operators for state variables (like State += X
or State -= X
...) it's more expensive than using operator assignment (like State = State + X
or State = State - X
...).
Proof of concept (without optimizations):
pragma solidity 0.8.15; contract TesterA { uint private _a; function testShort() public { _a += 1; } } contract TesterB { uint private _a; function testLong() public { _a = _a + 1; } }
Gas saving executing: 13 per entry
TesterA.testShort: 43507 TesterB.testLong: 43494
Affected source code:
Shifting one to the right will calculate a division by two.
he SHR
opcode only requires 3 gas, compared to the DIV
opcode's consumption of 5. Additionally, shifting is used to get around Solidity's division operation's division-by-0 prohibition.
Proof of concept (without optimizations):
pragma solidity 0.8.15; contract TesterA { function testDiv(uint a) public returns (uint) { return a / 2; } } contract TesterB { function testShift(uint a) public returns (uint) { return a >> 1; } }
Gas saving executing: 172 per entry
TesterA.testDiv: 21965 TesterB.testShift: 21793
Affected source code:
An empty block or an empty method generally indicates a lack of logic that itβs unnecessary and should be eliminated, call a method that literally does nothing only consumes gas unnecessarily, if it is a virtual
method which is expects it to be filled by the class that implements it, it is better to use abstract
contracts with just the definition.
Sample code:
pragma solidity >=0.4.0 <0.7.0; abstract contract Feline { function utterance() public virtual returns (bytes32); }
Reference:
Affected source code:
calldata
instead of memory
Some methods are declared as external
but the arguments are defined as memory
instead of as calldata
.
By marking the function as external
it is possible to use calldata
in the arguments shown below and save significant gas.
Recommended change:
- function updateContractImage(string memory _newContractImage) external onlyOwner { + function updateContractImage(string calldata _newContractImage) external onlyOwner { emit ContractImageUpdated(settings.contractImage, _newContractImage); settings.contractImage = _newContractImage; }
Affected source code:
bool
to uint256
can save gasBecause each write operation requires an additional SLOAD
to read the slot's contents, replace the bits occupied by the boolean, and then write back, booleans
are more expensive than uint256
or any other type that uses a complete word. This cannot be turned off because it is the compiler's defense against pointer aliasing and contract upgrades.
Reference:
Also, this is applicable to integer types different from uint256
or int256
.
Affected source code for booleans
:
constants
expressions are expressions, not constants
Due to how constant variables are implemented (replacements at compile-time), an expression assigned to a constant variable is recomputed each time that the variable is used, which wastes some gas.
If the variable was immutable instead: the calculation would only be done once at deploy time (in the constructor), and then the result would be saved and read directly at runtime rather than being recalculated.
Reference:
Consequences: each usage of a "constant" costs ~100gas more on each access (it is still a little better than storing the result in storage, but not much..). since these are not real constants, they can't be referenced from a real constant environment (e.g. from assembly, or from another library).
Affected source code:
ERC721Votes.delegateBySig
logicIt's possible to optimize the method ERC721Votes.delegateBySig
as follows:
Recommended changes:
function delegateBySig( address _from, address _to, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s ) external { // Ensure the signature has not expired if (block.timestamp > _deadline) revert EXPIRED_SIGNATURE(); + if (_from == address(0)) revert INVALID_SIGNATURE(); // Used to store the digest bytes32 digest; // Cannot realistically overflow unchecked { // Compute the hash of the domain seperator with the typed delegation data digest = keccak256( - abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR(), keccak256(abi.encode(DELEGATION_TYPEHASH, _from, _to, nonces[_from]++, _deadline))) + abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR(), keccak256(abi.encode(DELEGATION_TYPEHASH, _from, _to, ++nonces[_from], _deadline))) ); } // Recover the message signer address recoveredAddress = ecrecover(digest, _v, _r, _s); // Ensure the recovered signer is the voter - if (recoveredAddress == address(0) || recoveredAddress != _from) revert INVALID_SIGNATURE(); + if (recoveredAddress != _from) revert INVALID_SIGNATURE(); // Update the delegate _delegate(_from, _to); }
Affected source code:
MetadataRenderer.initialize
logicIt's possible to optimize the method MetadataRenderer.initialize
as follows:
Recommended changes:
function initialize( bytes calldata _initStrings, address _token, address _founder, address _treasury ) external initializer { // Ensure the caller is the contract manager if (msg.sender != address(manager)) revert ONLY_MANAGER(); // Decode the token initialization strings + (settings.name, , settings.description, settings.contractImage, settings.rendererBase) = abi.decode( - (string memory _name, , string memory _description, string memory _contractImage, string memory _rendererBase) = abi.decode( _initStrings, (string, string, string, string, string) ); // Store the renderer settings - settings.name = _name; - settings.description = _description; - settings.contractImage = _contractImage; - settings.rendererBase = _rendererBase; settings.token = _token; settings.treasury = _treasury; // Grant initial ownership to a founder __Ownable_init(_founder); }
Affected source code:
If a variable is not set/initialized, the default value is assumed (0, false
, 0x0 ... depending on the data type). You are simply wasting gas if you directly initialize it with its default value.
Proof of concept (without optimizations):
pragma solidity 0.8.15; contract TesterA { function testInit() public view returns (uint) { uint a = 0; return a; } } contract TesterB { function testNoInit() public view returns (uint) { uint a; return a; } }
Gas saving executing: 8 per entry
TesterA.testInit: 21392 TesterB.testNoInit: 21384
Affected source code:
MetadataRenderer.onMinted
logicIt's possible to optimize the method MetadataRenderer.onMinted
as follows:
Recommended changes:
function onMinted(uint256 _tokenId) external returns (bool) { ... unchecked { // For each property: - for (uint256 i = 0; i < numProperties; ++i) { + for (uint256 i = 0; i < numProperties;) { // Get the number of items to choose from uint256 numItems = properties[i].items.length; // Use the token's seed to select an item + ++i; + tokenAttributes[i] = uint16(seed % numItems); - tokenAttributes[i + 1] = uint16(seed % numItems); // Adjust the randomness seed >>= 16; } } return true; }
Affected source code:
Having a method that always returns the same value is not correct in terms of consumption, if you want to modify a value, and the method will perform a revert
in case of failure, it is not necessary to return a true
, since it will never be false
. It is less expensive not to return anything, and it also eliminates the need to double-check the returned value by the caller.
Affected source code:
MetadataRenderer.getAttributes
It's possible to optimize the method MetadataRenderer.getAttributes
as follows:
Recommended changes:
+ string private immutable _queryStringPath = abi.encodePacked("?contractAddress=",Strings.toHexString(uint256(uint160(address(this))), 20),"&tokenId="); + function getAttributes(uint256 _tokenId) public view returns (bytes memory aryAttributes, bytes memory queryString) { // Get the token's query string queryString = abi.encodePacked( - "?contractAddress=", - Strings.toHexString(uint256(uint160(address(this))), 20), - "&tokenId=", + _queryStringPath, Strings.toString(_tokenId) ); ...
Affected source code:
MetadataRenderer
It's possible to optimize some methods in MetadataRenderer
contract changing the argument type as follows:
Recommended changes:
- function initialize(address _governor, uint256 _delay) external initializer { + function initialize(address _governor, uint128 _delay) external initializer { // Ensure the caller is the contract manager if (msg.sender != address(manager)) revert ONLY_MANAGER(); // Ensure a governor address was provided if (_governor == address(0)) revert ADDRESS_ZERO(); // Grant ownership to the governor __Ownable_init(_governor); // Store the time delay - settings.delay = SafeCast.toUint128(_delay); + settings.delay = _delay; // Set the default grace period settings.gracePeriod = 2 weeks; emit DelayUpdated(0, _delay); } ... - function updateDelay(uint256 _newDelay) external { + function updateDelay(uint128 _newDelay) external { // Ensure the caller is the treasury itself if (msg.sender != address(this)) revert ONLY_TREASURY(); emit DelayUpdated(settings.delay, _newDelay); // Update the delay - settings.delay = SafeCast.toUint128(_newDelay); + settings.delay = _newDelay; } ... - function updateGracePeriod(uint256 _newGracePeriod) external { + function updateGracePeriod(uint128 _newGracePeriod) external { // Ensure the caller is the treasury itself if (msg.sender != address(this)) revert ONLY_TREASURY(); emit GracePeriodUpdated(settings.gracePeriod, _newGracePeriod); // Update the grace period - settings.gracePeriod = SafeCast.toUint128(_newGracePeriod); + settings.gracePeriod = _newGracePeriod; }
Affected source code:
Token.initialize
It is not necessary to send more information than necessary to the initialize
method. It's possible to optimize the method Token.initialize
as follows:
Recommended changes:
function initialize( IManager.FounderParams[] calldata _founders, bytes calldata _initStrings, address _metadataRenderer, address _auction ) external initializer { // Ensure the caller is the contract manager if (msg.sender != address(manager)) revert ONLY_MANAGER(); // Initialize the reentrancy guard __ReentrancyGuard_init(); // Store the founders and compute their allocations _addFounders(_founders); // Decode the token name and symbol - (string memory _name, string memory _symbol, , , ) = abi.decode(_initStrings, (string, string, string, string, string)); + (string memory _name, string memory _symbol) = abi.decode(_initStrings, (string, string)); // Initialize the ERC-721 token __ERC721_init(_name, _symbol); // Store the metadata renderer and auction house settings.metadataRenderer = IBaseMetadata(_metadataRenderer); settings.auction = _auction; }
Affected source code:
There are mathematical operations that can be eliminated and not affect the logic of the contract.
Recommended changes:
function _addFounders(IManager.FounderParams[] calldata _founders) internal { ... unchecked { // For each founder: for (uint256 i; i < numFounders; ++i) { ... // For each token to vest: for (uint256 j; j < founderPct; ++j) { ... // Update the base token id - (baseTokenId += schedule) % 100; + baseTokenId += schedule; } } ... } }
Affected source code:
The Token
contract uses a map to store the founders, but the key is the index, it is better to use an array instead of a mapping because there is no need to store the length and it will also be cheaper to return the array without iterating through all the entries.
function getFounders() external view returns (Founder[] memory) { // Cache the number of founders uint256 numFounders = settings.numFounders; // Get a temporary array to hold all founders Founder[] memory founders = new Founder[](numFounders); // Cannot realistically overflow unchecked { // Add each founder to the array for (uint256 i; i < numFounders; ++i) founders[i] = founder[i]; } return founders; }
Affected source code:
Auction
It's possible to optimize some methods in Auction
contract changing the argument type as follows:
Recommended changes:
- function setDuration(uint256 _duration) external onlyOwner { - settings.duration = SafeCast.toUint40(_duration); + function setDuration(uint40 _duration) external onlyOwner { + settings.duration = _duration; emit DurationUpdated(_duration); } /// @notice Updates the time buffer of each auction /// @param _timeBuffer The new time buffer - function setTimeBuffer(uint256 _timeBuffer) external onlyOwner { - settings.timeBuffer = SafeCast.toUint40(_timeBuffer); + function setTimeBuffer(uint40 _timeBuffer) external onlyOwner { + settings.timeBuffer = _timeBuffer; emit TimeBufferUpdated(_timeBuffer); } /// @notice Updates the minimum bid increment of each subsequent bid /// @param _percentage The new percentage - function setMinimumBidIncrement(uint256 _percentage) external onlyOwner { - settings.minBidIncrement = SafeCast.toUint8(_percentage); + function setMinimumBidIncrement(uint40 _percentage) external onlyOwner { + settings.minBidIncrement = _percentage; emit MinBidIncrementPercentageUpdated(_percentage); }
Affected source code:
Governor
It's possible to optimize some methods in Governor
contract changing the argument type as follows:
Recommended changes:
function initialize( address _treasury, address _token, address _vetoer, - uint256 _votingDelay, - uint256 _votingPeriod, - uint256 _proposalThresholdBps, - uint256 _quorumThresholdBps + uint48 _votingDelay, + uint48 _votingPeriod, + uint16 _proposalThresholdBps, + uint16 _quorumThresholdBps ) external initializer { // Ensure the caller is the contract manager if (msg.sender != address(manager)) revert ONLY_MANAGER(); // Ensure non-zero addresses are provided if (_treasury == address(0)) revert ADDRESS_ZERO(); if (_token == address(0)) revert ADDRESS_ZERO(); // Store the governor settings settings.treasury = Treasury(payable(_treasury)); settings.token = Token(_token); settings.vetoer = _vetoer; - settings.votingDelay = SafeCast.toUint48(_votingDelay); - settings.votingPeriod = SafeCast.toUint48(_votingPeriod); - settings.proposalThresholdBps = SafeCast.toUint16(_proposalThresholdBps); - settings.quorumThresholdBps = SafeCast.toUint16(_quorumThresholdBps); + settings.votingDelay = _votingDelay; + settings.votingPeriod = _votingPeriod; + settings.proposalThresholdBps = _proposalThresholdBps; + settings.quorumThresholdBps = _quorumThresholdBps; // Initialize EIP-712 support __EIP712_init(string.concat(settings.token.symbol(), " GOV"), "1"); // Grant ownership to the treasury __Ownable_init(_treasury); } ... - function updateVotingDelay(uint256 _newVotingDelay) external onlyOwner { + function updateVotingDelay(uint48 _newVotingDelay) external onlyOwner { emit VotingDelayUpdated(settings.votingDelay, _newVotingDelay); - settings.votingDelay = SafeCast.toUint48(_newVotingDelay); + settings.votingDelay = _newVotingDelay; } /// @notice Updates the voting period /// @param _newVotingPeriod The new voting period - function updateVotingPeriod(uint256 _newVotingPeriod) external onlyOwner { + function updateVotingPeriod(uint48 _newVotingPeriod) external onlyOwner { emit VotingPeriodUpdated(settings.votingPeriod, _newVotingPeriod); - settings.votingPeriod = SafeCast.toUint48(_newVotingPeriod); + settings.votingPeriod = _newVotingPeriod; } /// @notice Updates the minimum proposal threshold /// @param _newProposalThresholdBps The new proposal threshold basis points - function updateProposalThresholdBps(uint256 _newProposalThresholdBps) external onlyOwner { + function updateProposalThresholdBps(uint16 _newProposalThresholdBps) external onlyOwner { emit ProposalThresholdBpsUpdated(settings.proposalThresholdBps, _newProposalThresholdBps); - settings.proposalThresholdBps = SafeCast.toUint16(_newProposalThresholdBps); + settings.proposalThresholdBps = _newProposalThresholdBps; } /// @notice Updates the minimum quorum threshold /// @param _newQuorumVotesBps The new quorum votes basis points - function updateQuorumThresholdBps(uint256 _newQuorumVotesBps) external onlyOwner { + function updateQuorumThresholdBps(uint16 _newQuorumVotesBps) external onlyOwner { emit QuorumVotesBpsUpdated(settings.quorumThresholdBps, _newQuorumVotesBps); - settings.quorumThresholdBps = SafeCast.toUint16(_newQuorumVotesBps); + settings.quorumThresholdBps = _newQuorumVotesBps; }
Affected source code:
#0 - GalloDaSballo
2022-09-18T16:34:31Z
Roughly 600 gas saved