DatingDapp

AI First Flight #6
Beginner FriendlyFoundrySolidityNFT
EXP
View results
Submission Details
Impact: low
Likelihood: high
Invalid

SoulboundProfileNFT::tokenURI unreachable ownerOf(tokenId) == address(0) check — burned tokens revert from ERC721 before reaching the custom error

Root + Impact

Description

  • The tokenURI function contains a check if (ownerOf(tokenId) == address(0)) intended to revert with a custom error for non-existent tokens. However, OpenZeppelin's ERC721 implementation of ownerOf already reverts with ERC721NonexistentToken(tokenId) when called on a burned or non-minted token — it never returns address(0).

    This means the custom ERC721Metadata__URI_QueryFor_NonExistentToken() error is unreachable dead code. The actual revert message users see will be OpenZeppelin's ERC721NonexistentToken, not the contract's custom error.

// Root cause in SoulboundProfileNFT.sol lines 80-83
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
if (ownerOf(tokenId) == address(0)) {
// @> This line is NEVER reached — ownerOf() reverts before returning address(0)
revert ERC721Metadata__URI_QueryFor_NonExistentToken();
}
// ...
}

Risk

Likelihood:

  • Every call to tokenURI for a non-existent token will hit this behavior.

Impact:

  • Low severity — the function still correctly reverts for non-existent tokens (via OZ's revert), but the custom error message is misleading dead code. Front-ends relying on the custom error selector to distinguish error types will not catch this error.

Proof of Concept

This test calls tokenURI on a non-existent token and expects OpenZeppelin's ERC721NonexistentToken revert — not the contract's custom error. The custom error is dead code because ownerOf() reverts internally before ever returning address(0).

function testM04_UnreachableCustomError() public {
// Try tokenURI on non-existent token — reverts with OZ error, not custom error
vm.expectRevert(abi.encodeWithSelector(
IERC721Errors.ERC721NonexistentToken.selector,
999
));
soulboundNFT.tokenURI(999);
// The custom ERC721Metadata__URI_QueryFor_NonExistentToken error is never emitted
}

Recommended Mitigation

Use _requireOwned(tokenId) (available in OZ v5) or _ownerOf(tokenId) (the internal non-reverting version) for the check. The internal _ownerOf returns address(0) for non-existent tokens, allowing the custom error to be reached.

function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
- if (ownerOf(tokenId) == address(0)) {
+ if (_ownerOf(tokenId) == address(0)) {
revert ERC721Metadata__URI_QueryFor_NonExistentToken();
}
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 3 hours ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!