Puppy Raffle

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

`PuppyRaffle::tokenURI` emits invalid JSON: the rarity value is interpolated without quotes

Root + Impact

Root cause: PuppyRaffle::tokenURI interpolates the rarity name into the JSON without wrapping it in quotes.

Impact: every token's metadata is syntactically invalid JSON, so any strict consumer — marketplaces, wallets, indexers — fails to parse it and the NFT displays without name, description or image.

Description

  • tokenURI must return a base64-encoded JSON document conforming to the ERC-721 metadata schema, in which value is a string and must therefore be quoted.

  • The opening quote before the value is missing and so is the closing one. The produced document ends up as "value": common}, where common is a bare token, which no JSON parser accepts.

abi.encodePacked(
'{"name":"', name(),
'", "description":"An adorable puppy!", ',
@> '"attributes": [{"trait_type": "rarity", "value": ',
@> rareName, // inserted unquoted
'}], "image":"', imageURI, '"}'
)

The resulting payload, once decoded:

{"name":"Puppy Raffle", "description":"An adorable puppy!", "attributes": [{"trait_type": "rarity", "value": common}], "image":"ipfs://..."}

Risk

Likelihood:

  • Deterministic and universal: every token ever minted carries the malformed document, regardless of rarity or owner.

Impact:

  • Marketplaces and wallets that validate metadata reject the document outright, so the puppy renders as an unnamed, imageless token. The NFT is the entire prize of the protocol, and it is undisplayable.

  • Indexers that parse leniently may read the attribute as null, silently losing the rarity trait that gives the collection its value.

Proof of Concept

Decode the returned URI and attempt to parse it:

function test_tokenUriIsMalformedJson() public {
address[] memory p = new address[](4);
for (uint256 i = 0; i < 4; i++) p[i] = address(uint160(1000 + i));
puppyRaffle.enterRaffle{value: 4 ether}(p);
vm.warp(block.timestamp + 2 days);
puppyRaffle.selectWinner();
string memory uri = puppyRaffle.tokenURI(0);
// base64 payload decodes to: ... "value": common}] ...
// `common` is a bare token: JSON.parse throws on it
console.log(uri);
}

Running the decoded payload through any JSON parser fails:

$ node -e 'JSON.parse(`{"attributes": [{"trait_type": "rarity", "value": common}]}`)'
SyntaxError: Unexpected token c in JSON at position 49

Control — the same document with the value quoted parses without error, confirming the quotes are the only defect:

$ node -e 'JSON.parse(`{"attributes": [{"trait_type": "rarity", "value": "common"}]}`); console.log("ok")'
ok

Recommended Mitigation

Quote the value:

- '"attributes": [{"trait_type": "rarity", "value": ',
+ '"attributes": [{"trait_type": "rarity", "value": "',
rareName,
- '}], "image":"',
+ '"}], "image":"',

A test that base64-decodes tokenURI and asserts the result parses as JSON would prevent this class of regression.

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!