tokenURI should return a URI that resolves to the token's metadata JSON. For JSON built on-chain, that is a data URI: data:application/json;base64, followed by the Base64 of the JSON.
tokenURI (L87-L107) returns abi.encodePacked(_baseURI(), Base64.encode(json)). SoulboundProfileNFT does not override _baseURI(), and in the pinned OpenZeppelin 5.0.2 it returns "" (ERC721.sol L100-L102). The returned value is therefore the bare Base64 of the JSON, for example eyJuYW1lIjoi.... It has no data: prefix and no : anywhere, so it carries no URI scheme of any kind. Standard ERC721 metadata consumers, such as wallets and explorers, get a schemeless relative reference that does not resolve to the metadata.
Likelihood:
Every minted profile is affected. The PoC shows the same schemeless output for four different profiles.
The harm appears when a standard ERC721 metadata consumer is the display path for a profile. The token is soulbound, so marketplace display matters little, and no in-scope contract reads tokenURI.
Impact:
Standard consumers receive a relative reference instead of a data URI, so no profile's name, age or image is rendered through the ERC721 metadata interface.
The data can still be recovered by base64-decoding the string by hand or by reading the ProfileMinted event. No funds are at risk. The IERC721Metadata interface is implemented; only the value it returns is malformed.
The PoC mints a profile ("Alice", 25, "ipfs://QmAliceImage") and rebuilds the contract's exact JSON in the test. It then asserts that tokenURI(1):
equals Base64.encode(json) byte for byte;
differs from "data:application/json;base64," + Base64.encode(json);
does not start with data:;
contains no : at all, so it carries no URI scheme of any kind;
starts with eyJ, the Base64 of {".
A second test shows the same for three more profiles.
Run from the repository root, with the file saved as test/PoC_DD-05.t.sol:
Result: Suite result: ok. 2 passed; 0 failed; 0 skipped. The logs print tokenURI(1) = eyJuYW1lIjoiQWxpY2Ui... next to the expected data:application/json;base64,eyJuYW1lIjoiQWxpY2Ui....
Prefix the data-URI header, or override _baseURI() to return it.
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.