DatingDapp

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

Token URI missing data uri prefix

Root + Impact

Description

The NFT stores metadata on-chain and returns Base64-encoded JSON from tokenURI(). NFT clients generally expect tokenURI() to return a URI such as ipfs://..., https://..., or an inline data URI.

The function returns only the raw Base64 string because _baseURI() is not overridden and therefore defaults to an empty string. Without the data:application/json;base64, prefix, wallets and marketplaces can treat the returned string as an invalid or unknown URI instead of decoding it as JSON metadata.

function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
if (ownerOf(tokenId) == address(0)) {
revert ERC721Metadata__URI_QueryFor_NonExistentToken();
}
string memory profileName = _profiles[tokenId].name;
uint256 profileAge = _profiles[tokenId].age;
string memory imageURI = _profiles[tokenId].profileImage;
return string(
abi.encodePacked(
// @> _baseURI() is empty by default
_baseURI(),
// @> Raw Base64 is returned without "data:application/json;base64,"
Base64.encode(
bytes(
abi.encodePacked(
'{"name":"',
profileName,
'", ',
'"description":"A soulbound dating profile NFT.", ',
'"attributes": [{"trait_type": "Age", "value": ',
Strings.toString(profileAge),
"}], ",
'"image":"',
imageURI,
'"}'
)
)
)
)
);
}

Risk

Likelihood:

  • This occurs for every minted profile because the contract does not override _baseURI().

  • This occurs whenever an NFT client expects a standards-compatible URI rather than a bare Base64 payload.

Impact:

  • Profile NFTs may fail to render metadata in wallets, marketplaces, and indexers.

  • The dating profile NFT can lose its intended utility as a visible verified profile.

Proof of Concept

function testTokenURIMissingDataPrefix() public {
SoulboundProfileNFT nft = new SoulboundProfileNFT();
address alice = address(0xA11CE);
vm.prank(alice);
nft.mintProfile("Alice", 25, "ipfs://alice");
string memory uri = nft.tokenURI(1);
bytes memory uriBytes = bytes(uri);
bytes memory expectedPrefix = bytes("data:application/json;base64,");
for (uint256 i = 0; i < expectedPrefix.length; i++) {
assertTrue(i >= uriBytes.length || uriBytes[i] != expectedPrefix[i]);
break;
}
}

Recommended Mitigation

Prepend the standard inline JSON metadata prefix before the Base64 payload.

return string(
abi.encodePacked(
- _baseURI(),
+ "data:application/json;base64,",
Base64.encode(
- bytes(
- abi.encodePacked(
- '{"name":"',
- profileName,
- '", ',
- '"description":"A soulbound dating profile NFT.", ',
- '"attributes": [{"trait_type": "Age", "value": ',
- Strings.toString(profileAge),
- "}], ",
- '"image":"',
- imageURI,
- '"}'
- )
+ abi.encodePacked(
+ '{"name":"',
+ profileName,
+ '", ',
+ '"description":"A soulbound dating profile NFT.", ',
+ '"attributes": [{"trait_type": "Age", "value": ',
+ Strings.toString(profileAge),
+ "}], ",
+ '"image":"',
+ imageURI,
+ '"}'
)
)
)
);
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 2 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!