Platform: Code4rena
Start Date: 31/01/2023
Pot Size: $36,500 CANTO
Total HM: 5
Participants: 38
Period: 3 days
Judge: berndartmueller
Total Solo HM: 2
Id: 212
League: ETH
Rank: 29/38
Findings: 1
Award: $44.97
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: HardlyDifficult
Also found by: 0xAgro, 0xSmartContract, Aymen0909, DevABDee, JC, Matin, Rolezn, SleepingBugs, adriro, brevis, btk, chaduke, d3e4, enckrish, hihen, joestakey, libratus, merlin, nicobevi, rotcivegaf, shark, sorrynotsorry
149.2473 CANTO - $44.97
Pragma statements can be allowed to float when a contract is intended for consumption by other developers, as in the case with contracts in a library or EthPM package. Otherwise, the developer would need to manually update the pragma in order to compile locally. https://swcregistry.io/docs/SWC-103
Ethereum Smart Contract Best Practices - Lock pragmas to specific compiler version. solidity-specific/locking-pragmas
SubprotocolData
IS DEFINED TWICE WITH DIFFERENT PROPERTIESThere're two different struct objects called SubprotocolData
with different properties. This is confusing and could result on unexpected behaviors if one is used instead of the other one by mistake.
It's not a good practice to have structures with the same name that means different things. The recommendation is to rename one of them with a different name.
From the readability perspective, it's really difficult to follow the code on CidNFT.sol
since the code is compacted. It's recommended to add more empty lines on the code to make it easier to read.
Add spacing between lines of code
EnumerableSet
FOR ACTIVE TYPE PROTOCOLSEnumerableSet has the functionality that it's required for the data manipulation on the case of Active AssociationType
. It's always recommended to use well tested libraries instead of create a custom implementation when possible.
_safeMint()
INSTEAD OF mint()
Even though there is a comment that says that the use of _mint()
instead of _safeMint()
is intentional I still believe that _safeMint()
should be used instead.
For reference, OpenZeppelin's documentation for _mint
states: "Usage of this method is discouraged, use _safeMint whenever possible".
Use _safeMint()
instead of _mint()
It's recommended to reduce the code duplication. Since the nft ownership validation is being done twice, it should be better to use an auxiliary function for such validation.
Create an auxiliary function
contract CidNFT is ERC721, ERC721TokenReceiver { ... function add( uint256 _cidNFTID, string calldata _subprotocolName, uint256 _key, uint256 _nftIDToAdd, AssociationType _type ) external { ... + _validateNFTOwnership(_cidNFTID); - address cidNFTOwner = ownerOf[_cidNFTID]; - if ( - cidNFTOwner != msg.sender && - getApproved[_cidNFTID] != msg.sender && - !isApprovedForAll[cidNFTOwner][msg.sender] - ) revert NotAuthorizedForCIDNFT(msg.sender, _cidNFTID, cidNFTOwner); ... } ... function remove( uint256 _cidNFTID, string calldata _subprotocolName, uint256 _key, uint256 _nftIDToRemove, AssociationType _type ) public { ... + _validateNFTOwnership(_cidNFTID); - address cidNFTOwner = ownerOf[_cidNFTID]; - if ( - cidNFTOwner != msg.sender && - getApproved[_cidNFTID] != msg.sender && - !isApprovedForAll[cidNFTOwner][msg.sender] - ) revert NotAuthorizedForCIDNFT(msg.sender, _cidNFTID, cidNFTOwner); ... } ... + function _validateNFTOwnership(uint256 _cidNFTID) internal { + address cidNFTOwner = ownerOf[_cidNFTID]; + if ( + cidNFTOwner != msg.sender && + getApproved[_cidNFTID] != msg.sender && + !isApprovedForAll[cidNFTOwner][msg.sender] + ) revert NotAuthorizedForCIDNFT(msg.sender, _cidNFTID, cidNFTOwner); + } ... }
_cidNFTID
used on the add()
should be the new minted id.The _cidNFTID
used on the add()
call should be the new one instead of be open to user input.
function mint(bytes[] calldata _addList) external { + uint256 newTokenId = numMinted; + unchecked { + ++numMinted; + } - _mint(msg.sender, ++numMinted); // We do not use _safeMint here on purpose. If a contract calls this method, he expects to get an NFT back + _mint(msg.sender, newTokenId); // We do not use _safeMint here on purpose. If a contract calls this method, he expects to get an NFT back bytes4 addSelector = this.add.selector; for (uint256 i = 0; i < _addList.length; ++i) { ( bool success, /*bytes memory result*/ - ) = address(this).delegatecall(abi.encodePacked(addSelector, _addList[i])); + ) = address(this).delegatecall(abi.encodePacked(addSelector, newTokenId, _addList[i])); if (!success) revert AddCallAfterMintingFailed(i); } }
#0 - c4-judge
2023-02-18T13:11:11Z
berndartmueller marked the issue as grade-b