The tokenURI function in the SoulboundProfileNFT contract uses ownerOf(tokenId) == address(0) to check if a token exists. However, this check is incorrect because ownerOf(tokenId) reverts if the token does not exist, instead of returning address(0). As a result, calling tokenURI with an invalid tokenId will cause a transaction failure instead of handling the error gracefully.
In the tokenURI function, the following check is used to verify whether a token exists:
However, this approach is flawed because:
ownerOf(tokenId) Does Not Return address(0) for Invalid Tokens
The ownerOf function in OpenZeppelin’s ERC721 contract is designed to revert if the token does not exist.
It does not return address(0).
This means the if-condition will never execute; instead, the function will revert immediately when calling ownerOf with a non-existent tokenId.
How the Issue Manifests
If a user queries tokenURI(nonExistentTokenId), the transaction will fail with a revert error from ownerOf, rather than the intended ERC721Metadata__URI_QueryFor_NonExistentToken() error.
This makes error handling inconsistent and unexpected.
Add the test below in the testSoulboundProfileNFT.t.sol and run the command forge test --mt testTokenURI_ShouldRevertForNonExistentToken -vvvv
Your Output should look like the one below:
Looking at the Logs, you will see that the contract reverted with ERC721NonexistentToken(999), proving that ownerOf(tokenId) == address(0) does not correctly check token existence.
Unexpected Reverts: Calling tokenURI with an invalid token ID will revert due to ownerOf, rather than executing the intended error-handling logic.
Inconsistent Error Handling: The contract's ERC721Metadata__URI_QueryFor_NonExistentToken() error is never actually used, making debugging more difficult.
Manual Code Review
OpenZeppelin ERC721 Documentation
Use _exists(tokenId) Instead of ownerOf(tokenId) == address(0)
OpenZeppelin provides an _exists(tokenId) function that safely checks if a token exists without reverting. Update tokenURI as follows:
Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelyhood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.