56658 sc insight transmuter s tokenuri does not revert for nonexistent tokenids

Submitted on Oct 19th 2025 at 02:53:26 UTC by @tygra for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #56658

  • Report Type: Smart Contract

  • Report severity: Insight

  • Target: https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/Transmuter.sol

  • Impacts:

    • Contract fails to deliver promised returns, but doesn't lose value

Description

Brief/Intro

NFTs the Transmuter mints to represent redemptions have a tokenURI method, but this differs from the EIP-721 spec as it does not revert for invalid tokenIds.

Vulnerability Details

The method in question, tokenURI calls NFTMetadataGenerator.generateTokenURI in order to return an SVG to describe NFT identified by the requested tokenID.

// Transmuter.sol
    function tokenURI(uint256 id) public view override returns (string memory) {
        return NFTMetadataGenerator.generateTokenURI(id, "Transmuter V3 Position");
    }

// NFTMetadataGenerator.sol
    function generateTokenURI(uint256 tokenId, string memory title) internal pure returns (string memory) {
        string memory svg = generateSVG(tokenId, title);
        string memory json = generateJSONString(tokenId, svg);
        return string(abi.encodePacked("data:application/json;base64,", json));
    }

Nowhere is the tokenURI validated, consequently the contract will return an SVG for nonexistent tokenIDs.

This differs from EIP-721's definition of contracts that implement a tokenURI method such that Transmuter.sol fails to deliver the expected behaviour for an EIP-721 compliant contract that includes token metadata.

Impact Details

There is no risk of any customer funds being affected. The risk of not reverting for invalid tokenIDs is that onchain or offchain programs attempting to fetch the metadata for a given tokenID can incorrectly believe a tokenID exists due to metadata always being returned.

References

https://eips.ethereum.org/EIPS/eip-721

Proof of Concept

Proof of Concept

Add this test to Transmuter.t.sol

It returns the following metadata even though token #1e16 does not exist

{"name": "AlchemistV3 Position #10000000000000000", "description": "Position token for Alchemist V3", "image": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMDAgNDg0IiB3aWR0aD0iMzAwIiBoZWlnaHQ9IjQ4NCI+PHJlY3Qgd2lkdGg9IjMwMCIgaGVpZ2h0PSI0ODQiIGZpbGw9IiNkNGMzYjciIC8+PGNpcmNsZSBjeD0iMTUwIiBjeT0iMTUwIiByPSIxMjAiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzBhM2E2MCIgc3Ryb2tlLXdpZHRoPSI0IiAvPjx0ZXh0IHg9IjE1MCIgeT0iMTIwIiBmb250LWZhbWlseT0iQXJpYWwiIGZvbnQtc2l6ZT0iMjQiIGZpbGw9IiMwYTNhNjAiIHRleHQtYW5jaG9yPSJtaWRkbGUiPlRyYW5zbXV0ZXIgVjMgUG9zaXRpb248L3RleHQ+PHRleHQgeD0iMTUwIiB5PSIxNTAiIGZvbnQtZmFtaWx5PSJBcmlhbCIgZm9udC1zaXplPSIyOCIgZmlsbD0iIzBhM2E2MCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9ImJvbGQiPlBvc2l0aW9uPC90ZXh0Pjx0ZXh0IHg9IjE1MCIgeT0iMTkwIiBmb250LWZhbWlseT0ibW9ub3NwYWNlIiBmb250LXNpemU9IjI0IiBmaWxsPSIjMGEzYTYwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj4jMTAwMDAwMDAwMDAwMDAwMDA8L3RleHQ+PC9zdmc+"}

Was this helpful?