Foundation Drop contest - CodingNameKiki's results

Foundation is a web3 destination.

General Information

Platform: Code4rena

Start Date: 11/08/2022

Pot Size: $40,000 USDC

Total HM: 8

Participants: 108

Period: 4 days

Judge: hickuphh3

Total Solo HM: 2

Id: 152

League: ETH

Foundation

Findings Distribution

Researcher Performance

Rank: 85/108

Findings: 2

Award: $33.77

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

Labels

bug
duplicate
2 (Med Risk)
edited-by-warden

Awards

13.1705 USDC - $13.17

External Links

  1. Missing checks for address(0x0) when assigning values to address state variables

contracts/NFTCollectionFactory.sol 193: versionNFTCollection = _versionNFTCollection;

  1. _safeMint() should be used rather than _mint() wherever possible

_mint() is discouraged in favor of _safeMint() which ensures that the recipient is either an EOA or implements IERC721Receiver. Both OpenZeppelin and solmate have versions of this function

contracts/NFTDropCollection.sol 182: _mint(to, i);

#0 - HardlyDifficult

2022-08-18T21:30:03Z

Missing checks for address(0x0) when assigning values to address state variables

Invalid. That's a number not an address, and 0 is an acceptable value there.

Use safeMint

Agree will fix - for context see our response here.

#1 - HickupHH3

2022-08-31T11:05:44Z

dup of #183

  1. Require statement in modifier function can be replaced with revert to save gas.

contracts/NFTCollectionFactory.sol 173: require(rolesContract.isAdmin(msg.sender), "NFTCollectionFactory: Caller does not have the Admin role");

contracts/NFTDropCollection.sol 88: require(bytes(_baseURI).length > 0, "NFTDropCollection: _baseURI must be set"); 93: require(postRevealBaseURIHash != bytes32(0), "NFTDropCollection: Already revealed");

  1. Usage of uints smaller than 32 bytes (256 bits) incurs overhead

contracts/NFTCollectionFactory.sol 80: uint32 public versionNFTCollection; 95: uint32 public versionNFTDropCollection; 192: function initialize(uint32 _versionNFTCollection) external initializer { 291: uint32 maxTokenId, 329: uint32 maxTokenId, 368: uint32 maxTokenId, 391: uint32 maxTokenId,

contracts/NFTDropCollection.sol 126: uint32 _maxTokenId, 220: function updateMaxTokenId(uint32 _maxTokenId) external onlyAdmin {

  1. Require strings longer than 32 bytes cost extra gas

contracts/NFTCollectionFactory.sol 173: require(rolesContract.isAdmin(msg.sender), "NFTCollectionFactory: Caller does not have the Admin role"); 182: require(_rolesContract.isContract(), "NFTCollectionFactory: RolesContract is not a contract"); 203: require(_implementation.isContract(), "NFTCollectionFactory: Implementation is not a contract"); 227: require(_implementation.isContract(), "NFTCollectionFactory: Implementation is not a contract"); 262: require(bytes(symbol).length != 0, "NFTCollectionFactory: Symbol is required");

contracts/NFTDropCollection.sol 88: require(bytes(_baseURI).length > 0, "NFTDropCollection: _baseURI must be set"); 93: require(postRevealBaseURIHash != bytes32(0), "NFTDropCollection: Already revealed"); 130: require(bytes(_symbol).length > 0, "NFTDropCollection: _symbol must be set"); 131: require(_maxTokenId > 0, "NFTDropCollection: _maxTokenId must be set"); 172: require(count != 0, "NFTDropCollection: count must be greater than 0"); 179: require(latestTokenId <= maxTokenId, "NFTDropCollection: Exceeds max tokenId"); 238: require(_postRevealBaseURIHash != bytes32(0), "NFTDropCollection: use reveal instead");

  1. Errors: Reduce the length of error messages (long revert strings)

1.Shortening revert strings to fit in 32 bytes will decrease deployment time gas and will decrease runtime gas when the revert condition is met. 2.Revert strings that are longer than 32 bytes require at least one additional mstore, along with additional overhead for computing memory offset, etc. 3.In these instances, consider shortening the revert strings to fit within 32 bytes, or using custom errors:

contracts/NFTCollectionFactory.sol 173: require(rolesContract.isAdmin(msg.sender), "NFTCollectionFactory: Caller does not have the Admin role"); 182: require(_rolesContract.isContract(), "NFTCollectionFactory: RolesContract is not a contract"); 203: require(_implementation.isContract(), "NFTCollectionFactory: Implementation is not a contract"); 227: require(_implementation.isContract(), "NFTCollectionFactory: Implementation is not a contract"); 262: require(bytes(symbol).length != 0, "NFTCollectionFactory: Symbol is required");

contracts/NFTDropCollection.sol 88: require(bytes(_baseURI).length > 0, "NFTDropCollection: _baseURI must be set"); 93: require(postRevealBaseURIHash != bytes32(0), "NFTDropCollection: Already revealed"); 130: require(bytes(_symbol).length > 0, "NFTDropCollection: _symbol must be set"); 131: require(_maxTokenId > 0, "NFTDropCollection: _maxTokenId must be set"); 172: require(count != 0, "NFTDropCollection: count must be greater than 0"); 179: require(latestTokenId <= maxTokenId, "NFTDropCollection: Exceeds max tokenId"); 238: require(_postRevealBaseURIHash != bytes32(0), "NFTDropCollection: use reveal instead");

  1. Unchecking arithmetic operations that cannot underflow/overflow

From Solidity v0.8 onwards, all arithmetic operations come with implicit overflow and underflow checks. In some instances, an overflow/underflow is impossible, gas can be saved by using an unchecked block to remove the implicit checks:

contracts/NFTCollectionFactory.sol (Before) 207: versionNFTCollection++; (After) 207: unchecked { versionNFTCollection++ };

(Before) 231: versionNFTDropCollection++; (After) 231: unchecked { versionNFTDropCollection++ }:

  1. Bytes is more efficient than string and uses less gas.

If data can fit into 32 bytes, then you should use bytes32 datatype rather than bytes or strings as it is cheaper in solidity. Basically, any fixed size variable in solidity is cheaper than variable size. That will save gas on the contract. instances of string can be replaced by bytes(1..32):

contracts/NFTCollectionFactory.sol 240: string.concat("NFTDropV", versionNFTDropCollection.toString()), 214: string.concat("NFTv", versionNFTCollection.toString())

contracts/NFTDropCollection.sol 303: return string.concat(baseURI, tokenId.toString(), ".json");

  1. Functions guaranteed to revert when called by normal users can be marked payable

contracts/NFTCollectionFactory.sol 172: modifier onlyAdmin() { 202: function adminUpdateNFTCollectionImplementation(address _implementation) external onlyAdmin { 226: function adminUpdateNFTDropCollectionImplementation(address _implementation) external onlyAdmin {

#0 - HardlyDifficult

2022-08-17T09:17:17Z

  1. Custom errors

Agree but won't fix at this time. We use these in the market but not in collections. Unfortunately custom errors are still not as good of an experience for users (e.g. on etherscan). We used them in the market originally because we were nearing the max contract size limit and this was a good way to reduce the bytecode. We'll consider this in the future as tooling continues to improve.

  1. Usage of uints smaller than 32 bytes (256 bits) incurs overhead

contracts/NFTCollectionFactory.sol 80: uint32 public versionNFTCollection; 95: uint32 public versionNFTDropCollection; 192: function initialize(uint32 _versionNFTCollection) external initializer {

Invalid. The benefit from packing storage outweighs the casting overhead. Compressing these allows for a warm sload instead of 2 cold sloads while deploying new collections.

291: uint32 maxTokenId, 329: uint32 maxTokenId, 368: uint32 maxTokenId, 391: uint32 maxTokenId, contracts/NFTDropCollection.sol 126: uint32 _maxTokenId, 220: function updateMaxTokenId(uint32 _maxTokenId) external onlyAdmin {

Potentially valid but won't fix. This is uint32 to allow packing storage for a significant gas savings. Accepting the input as uint256 would incur additional overhead in checking for overflow before saving these values.

3 & 4. Use short error messages

Agree but won't fix. We use up to 64 bytes, aiming to respect the incremental cost but 32 bytes is a bit too short to provide descriptive error messages for our users.

  1. Unchecking arithmetic operations that cannot underflow/overflow

Invalid. The examples provided are already unchecked.

  1. Use bytes32 instead of string

Invalid. The baseURI cannot be stored in bytes32 because it may be longer than 32 bytes. It's possible this technique could work for name or symbol, however in order to stay flexible in case a creator would like to use a long value here -- we will keep these as strings.

  1. Functions guaranteed to revert when called by normal users can be marked payable

Disagree. This could save gas but 1) this seems like a poor practice to encourage and could be error prone. and 2) we don't care about optimizing admin-only actions.

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