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
Rank: 40/108
Findings: 2
Award: $74.71
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Saw-mon_and_Natalie
Also found by: 0x1f8b, 0x52, 0xDjango, 0xNazgul, 0xSmartContract, 0xSolus, 0xackermann, 0xmatt, 0xsolstars, Aymen0909, Bnke0x0, Chom, Deivitto, DevABDee, Dravee, ElKu, IllIllI, JC, Kumpa, Lambda, LeoS, MiloTruck, PwnedNoMore, ReyAdmirado, Rohan16, Rolezn, Ruhum, Sm4rty, TomJ, Treasure-Seeker, Vexjon, Waze, Yiko, __141345__, apostle0x01, auditor0517, berndartmueller, bin2chen, bobirichman, brgltd, bulej93, c3phas, carlitox477, cccz, cryptphi, csanuragjain, d3e4, danb, delfin454000, durianSausage, erictee, fatherOfBlocks, gogo, iamwhitelights, joestakey, jonatascm, ladboy233, mics, oyc_109, rbserver, ret2basic, robee, rokinot, rvierdiiev, shenwilly, sikorico, simon135, thank_you, wagmi, yash90, zeesaw, zkhorse
54.1073 USDC - $54.11
This method check that the symbol isn't null, however it does not check nothing about the symbol. This last check may be needed to be added.
In the three cases, collection is never set. Their signature should make the next change to their signatures: returns (address collection)
to just returns (address)
NFTCollection inherit from ERC721BurnableUpgradeable already inherit ERC721Upgradeable and ERC165Upgradeable, meaning that ERC721Upgradeable and ERC165Upgradeable should be removed.
However, SequentialMintCollection already inherit ERC721BurnableUpgradeable, meaning that ERC721BurnableUpgradeable should be removed.
SequentialMintCollection already inherit Initializable and ITokenCreator , meaning that both should be removed.
CollectionRoyalties already inherit IGetRoyalties, IGetFees, IRoyaltyInfo, meaning that all of them should be removed.
In conclusion, NFTCollection should be:
Should change it name to _cidToMinted
Should change it name to _tokenIdToCreatorPaymentAddress
NFTDropCollection inherit from ERC721BurnableUpgradeable already inherit ERC721Upgradeable and ERC165Upgradeable, meaning that ERC721Upgradeable and ERC165Upgradeable should be removed.
However, SequentialMintCollection already inherit ERC721BurnableUpgradeable, meaning that ERC721BurnableUpgradeable should be removed.
SequentialMintCollection already inherit Initializable and ITokenCreator , meaning that both should be removed.
CollectionRoyalties already inherit IGetRoyalties, IGetFees, IRoyaltyInfo, meaning that all of them should be removed.
AdminRole already inherit AccessControlUpgradeable. meaning it can be removed
In conclusion, NFTDropCollection should be:
By defining _baseURI as a parameter in multiple function, this shadows a storage variable with the same name, inherited from ERC721Upgradable contract, making the code more difficult to understand.
Suggestion: change _baseURI to __baseURI in functions: validBaseURI; initialize; reveal; updatePreRevealContent
#0 - HardlyDifficult
2022-08-18T18:47:22Z
[LOW] createNFTCollection
Unclear what the suggestion here is.
[Non-critical] createNFTDropCollection; createNFTDropCollectionWithPaymentAddress; createNFTDropCollectionWithPaymentFactory unnecessary return variable collection
Agree, we will change to use the named returns more consistenly.
[Non-critical] Redundant inherit
This was intentional, for top-level contracts I like to expand all inheritance used to make it clear what all the dependencies are and the linearization order they will be included in.
private naming convention
Fair feedback but that is not a convention we follow.
[Non-critical] _baseURI shadowing in multiple functions
Agree, will fix
🌟 Selected for report: Dravee
Also found by: 0x040, 0x1f8b, 0xDjango, 0xHarry, 0xNazgul, 0xSmartContract, 0xbepresent, 0xkatana, Amithuddar, Aymen0909, Bnke0x0, Chom, CodingNameKiki, Deivitto, DevABDee, Diraco, ElKu, Fitraldys, Funen, IllIllI, JC, LeoS, Metatron, MiloTruck, Noah3o6, ReyAdmirado, Rohan16, Rolezn, Saw-mon_and_Natalie, Sm4rty, SpaceCake, TomJ, Tomio, Trabajo_de_mates, Waze, Yiko, __141345__, ajtra, apostle0x01, bobirichman, brgltd, bulej93, c3phas, cRat1st0s, carlitox477, d3e4, durianSausage, erictee, fatherOfBlocks, gerdusx, gogo, hakerbaya, ignacio, jag, joestakey, ladboy233, medikko, mics, newfork01, oyc_109, pfapostol, robee, rvierdiiev, sach1r0, saian, samruna, sikorico, simon135, wagmi, zeesaw, zkhorse, zuhaibmohd
20.6049 USDC - $20.60
Storage variable versionNFTCollection is used multiple times. It is better to use a a memory variable to save gas in multiple storage calls (4), and only at the end write the storage variable. New function to save gas
function adminUpdateNFTCollectionImplementation(address _implementation) external onlyAdmin { require(_implementation.isContract(), "NFTCollectionFactory: Implementation is not a contract"); implementationNFTCollection = _implementation; uint32 _versionNFTCollection; unchecked { // Version cannot overflow 256 bits. _versionNFTCollection=versionNFTCollection+1; } // The implementation is initialized when assigned so that others may not claim it as their own. INFTCollectionInitializer(_implementation).initialize( payable(address(rolesContract)), string.concat("NFT Collection Implementation v", _versionNFTCollection.toString()), string.concat("NFTv", _versionNFTCollection.toString()) ); emit ImplementationNFTCollectionUpdated(_implementation, _versionNFTCollection); versionNFTCollection=_versionNFTCollection; }
Giving that implementationNFTCollection = _implementation;
is executed before trying to initialize the contract in _implementation
, it would be better to move this line to the end of the function.
Giving that implementationNFTDropCollection = _implementation;
is executed before trying to initialize the contract in _implementation
, it would be better to move this line to the end of the function.
Storage variable versionNFTCollection is used multiple times. It is better to use a a memory variable to save gas in multiple storage calls (4), and only at the end write the storage variable. New function to save gas
function adminUpdateNFTDropCollectionImplementation(address _implementation) external onlyAdmin { require(_implementation.isContract(), "NFTCollectionFactory: Implementation is not a contract"); implementationNFTDropCollection = _implementation; uint32 _versionNFTDropCollection; unchecked { // Version cannot overflow 256 bits. _versionNFTDropCollection=versionNFTDropCollection+1; } emit ImplementationNFTDropCollectionUpdated(_implementation, _versionNFTDropCollection); // The implementation is initialized when assigned so that others may not claim it as their own. INFTDropCollectionInitializer(_implementation).initialize( payable(address(this)), string.concat("NFT Drop Collection Implementation v", _versionNFTDropCollection.toString()), string.concat("NFTDropV", _versionNFTDropCollection.toString()), "ipfs://bafybeibvxnuaqtvaxu26gdgly2rm4g2piu7b2tqlx2dsz6wwhqbey2gddy/", 0x1337000000000000000000000000000000000000000000000000000000001337, 1, address(0), payable(0) ); versionNFTDropCollection=_versionNFTDropCollection; }
latestTokenId
is accessed 5 times + loop access, it can be reduced to 2
function mintCountTo(uint16 count, address to) external onlyMinterOrAdmin returns (uint256 firstTokenId) { require(count != 0, "NFTDropCollection: `count` must be greater than 0"); uint32 _latestTokenId=latestTokenId; unchecked { // If +1 overflows then +count would also overflow, unless count==0 in which case the loop would exceed gas limits firstTokenId = _latestTokenId + 1; } _latestTokenId = _latestTokenId + count; require(_latestTokenId <= maxTokenId, "NFTDropCollection: Exceeds max tokenId"); for (uint256 i = firstTokenId; i <= _latestTokenId; ) { _mint(to, i); unchecked { ++i; } } latestTokenId=_latestTokenId }
Minted event is emitted in function _mint, which includes tokenCID twice, once as indexedTokenCID and once as tokenCID. Meaning that tokenCID will always be indexedTokenCID. It would be better to eliminate one parameter, leaving Mint event as:
event Minted(address indexed creator, uint256 indexed tokenId, string indexed tokenCID);
#0 - HardlyDifficult
2022-08-19T00:28:31Z
multiple storage access can be avoided
Agree but since this is an admin only function I prefer the current form for simplicity.
possible wrong initialization can spend more gas than require
That's fair, but we wrote it this way to logically group actions. As an admin-only function optimizing for gas, particularly for reverts is not a goal.
mintCountTo: multiple storage access
Agree, will fix
Minted event has redundant information.
It was done this way so our frontend could use the CID as a topic to listen for and our backend could capture the complete CID from the event.