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: 52/168
Findings: 3
Award: $247.93
π Selected for report: 0
π Solo Findings: 0
70.6842 USDC - $70.68
Judge has assessed an item in Issue #399 as Medium risk. The relevant finding follows:
#0 - GalloDaSballo
2022-09-27T14:57:45Z
possible dos with uints and uint32 and block.timestamp since snapshot is uint256 and is unchecked the value where block.timestamp is large and voting delay is large and so the uint32 is overflowed but since its uint256 its ok but then when its casting it overflows which makes it zero which can cause loss of funds because the VoteStart and voteEnd would be zero and which which shouldnt happen uint256 snapshot; uint256 deadline;
// Cannot realistically overflow unchecked { // Compute the snapshot and deadline snapshot = block.timestamp + settings.votingDelay; deadline = snapshot + settings.votingPeriod; } // Store the proposal data proposal.voteStart = uint32(snapshot); proposal.voteEnd = uint32(deadline); proposal.proposalThreshold = uint32(currentProposalThreshold); proposal.quorumVotes = uint32(quorum()); proposal.proposer = msg.sender; proposal.timeCreated = uint32(block.timestamp);
reccomend to make sure that deadline and snapshot < uint32(max) or make it bigger types like uint96 to make it more future proof.
Dup of #482
π 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
131.8319 USDC - $131.83
snapshot
is uint256 and is unchecked the value where block.timestamp is large and voting delay is large and so the uint32 is overflowed but since its uint256 its ok but then when its casting it overflows which makes it zero which can cause loss of funds because the VoteStart
and voteEnd
would be zero and which which shouldnt happenuint256 snapshot; uint256 deadline; // Cannot realistically overflow unchecked { // Compute the snapshot and deadline snapshot = block.timestamp + settings.votingDelay; deadline = snapshot + settings.votingPeriod; } // Store the proposal data proposal.voteStart = uint32(snapshot); proposal.voteEnd = uint32(deadline); proposal.proposalThreshold = uint32(currentProposalThreshold); proposal.quorumVotes = uint32(quorum()); proposal.proposer = msg.sender; proposal.timeCreated = uint32(block.timestamp);
to make sure that deadline and snapshot < uint32(max) or make it bigger types like uint96 to make it more future proof.
manager.sol->deploy()
and if not have comments to show what you should do if you cant change variablesif the deployer makes bad init then attacker can take advantage for it or cause the depolyer a loss of gas. https://github.com/code-423n4/2022-09-nouns-builder/blob/debe9b792cc70510eadf9b3728cde5b0f2ec9a1f/src/manager/Manager.sol#L97
ex:manager.sol where there is no way to geting the eth out.
./src/auction/Auction.sol:39: constructor(address _manager, address _weth) payable initializer { ./src/governance/governor/Governor.sol:41: constructor(address _manager) payable initializer { ./src/governance/treasury/Treasury.sol:32: constructor(address _manager) payable initializer { ./src/lib/proxy/ERC1967Proxy.sol:21: constructor(address _logic, bytes memory _data) payable { ./src/manager/Manager.sol:55: constructor( ./src/token/metadata/MetadataRenderer.sol:32: constructor(address _manager) payable initializer { ./src/token/Token.sol:30: constructor(address _manager) payable initializer {
ex: Token.sol->burn : 207
./src/token/Token.sol:207: function burn(uint256 _tokenId) external {
when we compute the random hash we use this: everything in this function is guess able by the miner so the attacker/miner can get what every seed they want and possiblity get the most expensive seed.
return uint256(keccak256(abi.encode(_tokenId, blockhash(block.number), block.coinbase, block.timestamp)));
you can have a a miner guess the tokenid an blockhash because its only 256 blocks of hashs before and block.coinbase and block.timestamp is all guessable so a miner can figure out what seed someone is getting and they can frontrun it and get the higher seed then someone else https://github.com/code-423n4/2022-09-nouns-builder/blob/debe9b792cc70510eadf9b3728cde5b0f2ec9a1f/src/token/metadata/MetadataRenderer.sol#L251
onMinted()
the attacker/owner addProperties
and pushes alot of stuff on to the properites and makes it so high that when a user mints it will dos and cost alot of gas
for (uint256 i = 0; i < numProperties; ++i) { // Get the number of items to choose from uint256 numItems = properties[i].items.length;
function updateDelay(uint256 _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); }
https://github.com/code-423n4/2022-09-nouns-builder/blob/debe9b792cc70510eadf9b3728cde5b0f2ec9a1f/src/governance/treasury/Treasury.sol#L210 same thing with setting.gracePeriod there is a possiblity of settings.gracePeriod getting set to high the queue will never expire Treasury.sol:67
unchecked
unchecked { return block.timestamp > (timestamps[_proposalId] + settings.gracePeriod); }
because timestamps[_proposalId]=uint256
+ graceperiod then it will never reach block.timestamp and that is one issue where it will always expire
but if it is a big enough number then it will oveflow and be zero and always beable to execute.
https://github.com/code-423n4/2022-09-nouns-builder/blob/debe9b792cc70510eadf9b3728cde5b0f2ec9a1f/src/governance/treasury/Treasury.sol#L74-L77
because since updateDelay
is not called inside the contract and the check you have to pass is msg.sender != address(this)
and since it will always pass,this address can't call it.
either use this function for owner or manager/ take out the function.
Treasury.sol:211,223
Governor.sol
initilaize
which if protocols are not tought on best practices for Goverance.sol they can be rektonly goes up to 65,535 and if the project gets bigger then this will be problem and you will be easly be able to pass a proposal if a user makes 65,000 and then the value never changes and there is alot of tokens then the system might get dosed easily with proposals coming in every block
settings.proposalThresholdBps = SafeCast.toUint16(_proposalThresholdBps); settings.quorumThresholdBps = SafeCast.toUint16(_quorumThresholdBps);
Governor.sol:95
__EIP712_init(string.concat(settings.token.symbol(), " GOV"), "1");
like uniswap they use unchecked block for block.timesmtap in smart contract solidity verision ^0.8.0 Governor.sol:144
if (getVotes(msg.sender, block.timestamp - 1) < proposalThreshold())
_description
is more then 32 bytesWhich can cause a the proposal
description to be cut off and can lead to issues.
Governor.sol:144
bytes32 descriptionHash = keccak256(bytes(_description));
then it can dos the governace and cause fund loss which
then an attacker can make proposal.proposer=attacker
because of how snaphsot is uint256 trying to fix into a uint32 and it can be onwers fault that votingDelay or votingPeriod is to high and that can cause the overflow
so just be carefull of what the depolyer makes settings.votingDelay and setting.VotingPeriod
unchecked { // Compute the snapshot and deadline snapshot = block.timestamp + settings.votingDelay; deadline = snapshot + settings.votingPeriod; } // Store the proposal data proposal.voteStart = uint32(snapshot); proposal.voteEnd = uint32(deadline);
Govovner.sol:
because getVotes->getpastVotes
for an account and timestamp which it wont get its current votes and it will be off so it is possible after an amount of time that you still have votes becaues of using pastVotes
steps:
attacker sells of his votes (tokens).
but since its using getPastVotes
the attacker will still have votes when in the current block and timestmap they shouldnt,causing a break of logic
Govoner.sol:296
function getVotes(address _account, uint256 _timestamp) public view returns (uint256) { return settings.token.getPastVotes(_account, _timestamp); }
because of execute->execute
will revert because in treasury.sol its only called by owner otherwise it will revert.
because it only sends eth from the treasury not the goverance contract and there is no check in execute that there is enough votes to pass
if (state(proposalId) != ProposalState.Queued) revert PROPOSAL_NOT_QUEUED(proposalId); // Mark the proposal as executed proposals[proposalId].executed = true; // Execute the proposal settings.treasury.execute{ value: msg.value }(_targets, _values, _calldatas, _descriptionHash);
Governance.sol:352
unchecked { // Ensure the caller is the proposer or the proposer's voting weight has dropped below the proposal threshold if (msg.sender != proposal.proposer && getVotes(proposal.proposer, block.timestamp - 1) > proposal.proposalThreshold) revert INVALID_CANCEL(); }
because of unchecked when block.timestamp is overflowed you can cancel any proposal because if getVotes(proposer,uint32.max) if that the case and there is a possiblity that it can pass and if proposer sells of and dosnt hit the Threshold which can be anytime and a attacker can cancel any proposal. Governance.sol:377
function burnVetoer() external onlyOwner { emit VetoerUpdated(settings.vetoer, address(0)); delete settings.vetoer; }
Governance.sol:591,607,583
minBid
is to strict and can cause users fund lost// minBid = 1 ether + (1e18* 100) / 100=2e18 and minBid is wrong calculation its to strict of calculations it shouldn't be a whole 1 eth more and there is no docs of why not just make it 0.5 eth more
unchecked { // Compute the minimum bid minBid = highestBid + ((highestBid * settings.minBidIncrement) / 100); }
Governance.sol:116
_handleOutgoingTransfer(highestBidder, highestBid);
and they can make that call into the fallback and make it revert so they get the auction item for a very little funds funds can get lost and its brakes the whole auction idea
assembly { // Transfer ETH to the recipient // Limit the call to 50,000 gas success := call(50000, _to, _amount, 0, 0, 0, 0) }
and this can be an issue also there is no way onlyOwner to change it
auction.endTime = uint40(block.timestamp + settings.timeBuffer); }
if an attacker can make the minting revert then pause the functionality which is flawed and should instead not trust that an attacker just made it happen because he tried reentering and since of the lock it goes to the catch
statement
https://github.com/code-423n4/2022-09-nouns-builder/blob/debe9b792cc70510eadf9b3728cde5b0f2ec9a1f/src/token/metadata/MetadataRenderer.sol#L171
endTime
can be a hugh number and then the action.endTime
will overflow and be zeroand it can cause loss of funds.
// Cache the current timestamp uint256 startTime = block.timestamp; // Used to store the auction end time uint256 endTime; // Cannot realistically overflow unchecked { // Compute the auction end time // uint 40+uint32 at most = uint72 in uint256 endTime = startTime + settings.duration; } // Store the auction start and end time auction.startTime = uint40(startTime); auction.endTime = uint40(endTime);
if eoa call is true and it failed and then you try to get weth but since seccess=true
you wont get your funds.
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); } }
governance.sol:351
UUPS.sol
_self
and address(this)
and since this is true the onlyProxy modifer will always revert.modifier onlyProxy() { if (address(this) == __self) revert ONLY_DELEGATECALL(); if (_getImplementation() != __self) revert ONLY_PROXY(); _; }
You can initialize again even after its deployed and the attacker can steal funds with changing initilizion function
an attacker can call initialize even if your not owner and since its works like this ex:
lets say isTopLevelCall=true || true && true || false = false so the whole statement is false because of && and _initialized != 1
makes this vulnerable to issues
lock it like re-entrancy this whole funcioninlllity like this is wrong
steps:
deployer initializes in auction.sol:initialize
which makes isTopLevelCall=true
and _initializing=true
then when the attacker calls it
isTopLevelCall=false
and it will be true || true && true || false which will be true && false =false and it wont revert causing an attacker to inilitiaze again.
if ((!isTopLevelCall || _initialized != 0) && (Address.isContract(address(this)) || _initialized != 1)) revert ALREADY_INITIALIZED();
right now it not exploitable beacuse of manager check but if in the future you dont do this it wll be vunlernable
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.7/contracts/proxy/utils/Initializable.sol
they dont do _initilize!=1
*/
modifier initializer() { bool isTopLevelCall = !_initializing; if ((!isTopLevelCall || _initialized != 0) && (Address.isContract(address(this)) || _initialized != 1)) revert ALREADY_INITIALIZED(); _initialized = 1; if (isTopLevelCall) { _initializing = true; } _; if (isTopLevelCall) { _initializing = false; emit Initialized(1); } }
#0 - GalloDaSballo
2022-09-27T01:00:33Z
TODO
L
L
L
L
L
Really impressed by your progress Simon, I think you should get Grammarly or an assistant to format your reports though
5L
π 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
45.4217 USDC - $45.42
./src/auction/Auction.sol:244: function unpause() external onlyOwner { ./src/auction/Auction.sol:263: function pause() external onlyOwner { ./src/auction/Auction.sol:307: function setDuration(uint256 _duration) external onlyOwner { ./src/auction/Auction.sol:315: function setReservePrice(uint256 _reservePrice) external onlyOwner { ./src/auction/Auction.sol:323: function setTimeBuffer(uint256 _timeBuffer) external onlyOwner { ./src/auction/Auction.sol:331: function setMinimumBidIncrement(uint256 _percentage) external onlyOwner { ./src/auction/Auction.sol:374: function _authorizeUpgrade(address _newImpl) internal view override onlyOwner { ./src/governance/governor/Governor.sol:564: function updateVotingDelay(uint256 _newVotingDelay) external onlyOwner { ./src/governance/governor/Governor.sol:572: function updateVotingPeriod(uint256 _newVotingPeriod) external onlyOwner { ./src/governance/governor/Governor.sol:580: function updateProposalThresholdBps(uint256 _newProposalThresholdBps) external onlyOwner { ./src/governance/governor/Governor.sol:588: function updateQuorumThresholdBps(uint256 _newQuorumVotesBps) external onlyOwner { ./src/governance/governor/Governor.sol:596: function updateVetoer(address _newVetoer) external onlyOwner { ./src/governance/governor/Governor.sol:605: function burnVetoer() external onlyOwner { ./src/governance/governor/Governor.sol:618: function _authorizeUpgrade(address _newImpl) internal view override onlyOwner { ./src/governance/treasury/Treasury.sol:116: function queue(bytes32 _proposalId) external onlyOwner returns (uint256 eta) { ./src/governance/treasury/Treasury.sol:146: ) external payable onlyOwner { ./src/governance/treasury/Treasury.sol:180: function cancel(bytes32 _proposalId) external onlyOwner { ./src/lib/utils/Ownable.sol:28: modifier onlyOwner() { ./src/lib/utils/Ownable.sol:63: function transferOwnership(address _newOwner) public onlyOwner { ./src/lib/utils/Ownable.sol:71: function safeTransferOwnership(address _newOwner) public onlyOwner { ./src/lib/utils/Ownable.sol:87: function cancelOwnershipTransfer() public onlyOwner { ./src/manager/Manager.sol:187: function registerUpgrade(address _baseImpl, address _upgradeImpl) external onlyOwner { ./src/manager/Manager.sol:196: function removeUpgrade(address _baseImpl, address _upgradeImpl) external onlyOwner { ./src/manager/Manager.sol:209: function _authorizeUpgrade(address _newImpl) internal override onlyOwner {} ./src/token/metadata/MetadataRenderer.sol:95: ) external onlyOwner { ./src/token/metadata/MetadataRenderer.sol:347: function updateContractImage(string memory _newContractImage) external onlyOwner { ./src/token/metadata/MetadataRenderer.sol:355: function updateRendererBase(string memory _newRendererBase) external onlyOwner { ./src/token/metadata/MetadataRenderer.sol:363: function updateDescription(string memory _newDescription) external onlyOwner { ./src/token/metadata/MetadataRenderer.sol:376: function _authorizeUpgrade(address _impl) internal view override onlyOwner {
// Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled.
./src/auction/Auction.sol:136: bool extend; ./src/auction/Auction.sol:349: bool success;
./src/governance/governor/Governor.sol:132: uint256 numTargets = _targets.length; ./src/governance/governor/Governor.sol:138: if (numTargets != _values.length) revert PROPOSAL_LENGTH_MISMATCH(); ./src/governance/governor/Governor.sol:139: if (numTargets != _calldatas.length) revert PROPOSAL_LENGTH_MISMATCH(); ./src/governance/treasury/Treasury.sol:157: uint256 numTargets = _targets.length; ./src/lib/proxy/ERC1967Upgrade.sol:61: if (_data.length > 0 || _forceCall) { ./src/lib/utils/Address.sol:50: if (_returndata.length > 0) { ./src/token/metadata/MetadataRenderer.sol:78: return properties.length; ./src/token/metadata/MetadataRenderer.sol:84: return properties[_propertyId].items.length; ./src/token/metadata/MetadataRenderer.sol:97: uint256 dataLength = ipfsData.length; ./src/token/metadata/MetadataRenderer.sol:109: uint256 numStoredProperties = properties.length; ./src/token/metadata/MetadataRenderer.sol:112: uint256 numNewProperties = _names.length; ./src/token/metadata/MetadataRenderer.sol:115: uint256 numNewItems = _items.length; ./src/token/metadata/MetadataRenderer.sol:150: // Cannot underflow as the items array length is ensured to be at least 1 ./src/token/metadata/MetadataRenderer.sol:151: uint256 newItemIndex = items.length - 1; ./src/token/metadata/MetadataRenderer.sol:182: uint256 numProperties = properties.length; ./src/token/metadata/MetadataRenderer.sol:191: uint256 numItems = properties[i].items.length; ./src/token/Token.sol:73: uint256 numFounders = _founders.length;
Solidity version 0.8+ comes with implicit overflow and underflow checks on unsigned integers. When an overflow or an underflow isnβt possible (as an example, when a comparison is made before the arithmetic operation), some gas can be saved by using an unchecked block: Checked or Unchecked Arithmetic. I suggest wrapping with an unchecked block: Same thing with second unchecked because total can't overflow amount cant overflow
./src/governance/governor/Governor.sol:132: uint256 numTargets = _targets.length; ./src/governance/governor/Governor.sol:138: if (numTargets != _values.length) revert PROPOSAL_LENGTH_MISMATCH(); ./src/governance/governor/Governor.sol:139: if (numTargets != _calldatas.length) revert PROPOSAL_LENGTH_MISMATCH(); ./src/governance/treasury/Treasury.sol:157: uint256 numTargets = _targets.length; ./src/lib/proxy/ERC1967Upgrade.sol:61: if (_data.length > 0 || _forceCall) { ./src/lib/utils/Address.sol:50: if (_returndata.length > 0) { ./src/token/metadata/MetadataRenderer.sol:78: return properties.length; ./src/token/metadata/MetadataRenderer.sol:84: return properties[_propertyId].items.length; ./src/token/metadata/MetadataRenderer.sol:97: uint256 dataLength = ipfsData.length; ./src/token/metadata/MetadataRenderer.sol:109: uint256 numStoredProperties = properties.length; ./src/token/metadata/MetadataRenderer.sol:112: uint256 numNewProperties = _names.length; ./src/token/metadata/MetadataRenderer.sol:115: uint256 numNewItems = _items.length; ./src/token/metadata/MetadataRenderer.sol:150: // Cannot underflow as the items array length is ensured to be at least 1 ./src/token/metadata/MetadataRenderer.sol:151: uint256 newItemIndex = items.length - 1; ./src/token/metadata/MetadataRenderer.sol:182: uint256 numProperties = properties.length; ./src/token/metadata/MetadataRenderer.sol:191: uint256 numItems = properties[i].items.length; ./src/token/Token.sol:73: uint256 numFounders = _founders.length;
./src/auction/Auction.sol:104: if (highestBidder == address(0)) { ./src/auction/Auction.sol:184: if (_auction.highestBidder != address(0)) { ./src/auction/Auction.sol:228: auction.highestBidder = address(0); ./src/governance/governor/IGovernor.sol:264: /// @notice The address eligible to veto any proposal (address(0) if burned) ./src/governance/governor/Governor.sol:70: if (_treasury == address(0)) revert ADDRESS_ZERO(); ./src/governance/governor/Governor.sol:71: if (_token == address(0)) revert ADDRESS_ZERO(); ./src/governance/governor/Governor.sol:239: if (recoveredAddress == address(0) || recoveredAddress != _voter) revert INVALID_SIGNATURE(); ./src/governance/governor/Governor.sol:543: /// @notice The address eligible to veto any proposal (address(0) if burned) ./src/governance/governor/Governor.sol:597: if (_newVetoer == address(0)) revert ADDRESS_ZERO(); ./src/governance/governor/Governor.sol:606: emit VetoerUpdated(settings.vetoer, address(0)); ./src/governance/treasury/Treasury.sol:48: if (_governor == address(0)) revert ADDRESS_ZERO(); ./src/lib/token/ERC721Votes.sol:128: return current == address(0) ? _account : current; ./src/lib/token/ERC721Votes.sol:170: if (recoveredAddress == address(0) || recoveredAddress != _from) revert INVALID_SIGNATURE(); ./src/lib/token/ERC721Votes.sol:205: if (_from != address(0)) { ./src/lib/token/ERC721Votes.sol:220: if (_to != address(0)) { ./src/lib/token/ERC721.sol:84: if (_owner == address(0)) revert ADDRESS_ZERO(); ./src/lib/token/ERC721.sol:94: if (owner == address(0)) revert INVALID_OWNER(); ./src/lib/token/ERC721.sol:132: if (_to == address(0)) revert ADDRESS_ZERO(); ./src/lib/token/ERC721.sol:192: if (_to == address(0)) revert ADDRESS_ZERO(); ./src/lib/token/ERC721.sol:194: if (owners[_tokenId] != address(0)) revert ALREADY_MINTED(); ./src/lib/token/ERC721.sol:196: _beforeTokenTransfer(address(0), _to, _tokenId); ./src/lib/token/ERC721.sol:204: emit Transfer(address(0), _to, _tokenId); ./src/lib/token/ERC721.sol:206: _afterTokenTransfer(address(0), _to, _tokenId); ./src/lib/token/ERC721.sol:214: if (owner == address(0)) revert NOT_MINTED(); ./src/lib/token/ERC721.sol:216: _beforeTokenTransfer(owner, address(0), _tokenId); ./src/lib/token/ERC721.sol:226: emit Transfer(owner, address(0), _tokenId); ./src/lib/token/ERC721.sol:228: _afterTokenTransfer(owner, address(0), _tokenId); ./src/lib/interfaces/IERC721.sol:40: /// @dev Reverts if a transfer is attempted to address(0) ./src/lib/interfaces/IInitializable.sol:19: /// @dev Reverts if incorrectly initialized with address(0) ./src/lib/utils/Initializable.sol:36: if ((!isTopLevelCall || _initialized != 0) && (Address.isContract(address(this)) || _initialized != 1)) revert ALREADY_INITIALIZED(); ./src/lib/utils/Ownable.sol:48: emit OwnerUpdated(address(0), _initialOwner); ./src/manager/Manager.sol:82: if (_owner == address(0)) revert ADDRESS_ZERO(); ./src/manager/Manager.sol:117: if ((founder = _founderParams[0].wallet) == address(0)) revert FOUNDER_REQUIRED(); ./src/manager/Manager.sol:167: metadata = address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, metadataHash))))); ./src/manager/Manager.sol:168: auction = address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, auctionHash))))); ./src/manager/Manager.sol:169: treasury = address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, treasuryHash))))); ./src/manager/Manager.sol:170: governor = address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, governorHash))))); ./src/token/metadata/MetadataRenderer.sol:210: Strings.toHexString(uint256(uint160(address(this))), 20), ./src/token/Token.sol:132: while (tokenRecipient[_tokenId].wallet != address(0)) ++_tokenId; ./src/token/Token.sol:182: if (tokenRecipient[baseTokenId].wallet == address(0)) {
ex: iszero
./src/lib/utils/Ownable.sol:48: emit OwnerUpdated(address(0), _initialOwner); ./src/manager/Manager.sol:82: if (_owner == address(0)) revert ADDRESS_ZERO(); ./src/manager/Manager.sol:117: if ((founder = _founderParams[0].wallet) == address(0)) revert FOUNDER_REQUIRED(); ./src/manager/Manager.sol:167: metadata = address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, metadataHash))))); ./src/manager/Manager.sol:168: auction = address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, auctionHash))))); ./src/manager/Manager.sol:169: treasury = address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, treasuryHash))))); ./src/manager/Manager.sol:170: governor = address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, governorHash))))); ./src/token/metadata/MetadataRenderer.sol:210: Strings.toHexString(uint256(uint160(address(this))), 20), ./src/token/Token.sol:132: while (tokenRecipient[_tokenId].wallet != address(0)) ++_tokenId; ./src/token/Token.sol:182: if (tokenRecipient[baseTokenId].wallet == address(0)) {
timestamp is already a uint32 and is a waste.
proposal.timeCreated = uint32(block.timestamp);
#0 - GalloDaSballo
2022-09-26T20:28:43Z
Cache length -> 200 Rest is misguided