Puppy Raffle

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

tokenURI() builds invalid JSON because the rarity value is not quoted, so marketplaces may fail to parse the metadata

Root + Impact

Description

PuppyRaffle.tokenURI() in src/PuppyRaffle.sol builds the NFT metadata as a JSON string, but it inserts the rarity name without quotation marks, so the resulting JSON is invalid and any tool that tries to parse it will fail.

The vulnerable code is:

return string(
abi.encodePacked(
baseURI,
Base64.encode(
bytes(
abi.encodePacked(
'{"name":"',
name(),
'", "description":"An adorable puppy!", ',
'"attributes": [{"trait_type": "rarity", "value": ',
rareName,
'}]',
'}'
)
)
)
)
);

The name and the description are correctly quoted in the string, but the rarity value is not. When rareName is, for example, common, the attributes section comes out as "attributes": [{"trait_type": "rarity", "value": common}], with the word common bare, instead of "value": "common". In JSON, a string value must be wrapped in double quotes; an unquoted word is not a valid value, so the whole document is not valid JSON. The metadata is then Base64-encoded and returned as a data URI, which hides the problem from a quick glance at the on-chain return value, but any marketplace, wallet, or indexer that decodes the Base64 and parses the JSON will hit the same error. The image URI is also built in the function but is not included in the final JSON, so the metadata is missing the image field as well, although the invalid JSON is the main problem.

Risk

Likelihood: High - the invalid JSON is produced for every single token. There is no special condition and no user action that avoids it; every call to tokenURI() for any minted puppy returns the same broken metadata.
Impact: Low - no funds are lost and the raffle itself still works, but the NFT that players paid to win is presented incorrectly. Marketplaces and wallets that parse metadata strictly will fail to show the puppy's rarity trait, or may fail to display the token at all, so the prize is worth less and looks broken to its owner. For a project whose whole point is winning a cute dog NFT, metadata that cannot be read is a real, if not financial, defect.

Proof of Concept

Manual trace - no tests were run; this is a step-by-step reading of the code, with a worked example.

Worked example: suppose a puppy is minted with rarity rare, so rareName is the string rare.

  1. A marketplace calls tokenURI(tokenId). The function reads the rarity for that token, sets rareName to rare, and builds the JSON string by concatenation.

  2. The name part comes out correctly quoted: {"name": "Puppy Raffle", ...}. The description part is also correctly quoted.

  3. The attributes part comes out as "attributes": [{"trait_type": "rarity", "value": rare}], because rareName is inserted directly after "value": with no quotation marks around it.

  4. The full decoded JSON is therefore {"name": "Puppy Raffle", "description": "An adorable puppy!", "attributes": [{"trait_type": "rarity", "value": rare}]}, which is not valid JSON: rare is not a string, a number, a boolean, or null, so a JSON parser rejects it.

  5. The marketplace's parser fails, so it cannot read the rarity trait, and depending on how strictly it handles the error, it may show the token with no traits, or fail to load the token's metadata entirely. The same result follows for a common puppy, with the word common unquoted, and for a legendary puppy, with the word legendary unquoted.

Recommended Mitigation

Quote the rarity value in the JSON, just as the name and description are quoted. In Solidity the double quotes inside the string need to be escaped.

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

With that change the decoded metadata reads "value": "rare", which is valid JSON. Also consider adding the image field to the JSON so marketplaces can display the puppy image, and consider testing the decoded output with a JSON parser before deploying, since Base64 encoding makes this kind of error easy to miss.

Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge 30 minutes 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!