Issue with DynamicSvgNft Contract: getHighSVG() and getLowSVG() Functions Returning Incorrect Values

32 views Asked by At

I am currently working on a project that involves an NFT contract called DynamicSvgNft. The contract is designed to change the SVG image URI of the NFT based on a price feed from Chainlink. However, I am facing an issue where the getHighSVG() and getLowSVG() functions are returning mixed-up values.

Here is the relevant code for the contract:

// DynamicSvgNft.sol

     contract DynamicSvgNft is ERC721, Ownable {
 uint256 private s_tokenCounter;
 string private s_lowImageURI;
 string private s_highImageURI;
 AggregatorV3Interface internal immutable i_priceFeed;

 mapping (uint256 => int256) private s_tokenToHighValue;

 event CreatedNft(uint256 indexed tokenId, int256 highValue);

 constructor(address priceFeedAddress, string memory lowSvg, string memory highSvg) ERC721("Dynamic SVG NFT", "DSN") {
     s_tokenCounter = 0;
     i_priceFeed = AggregatorV3Interface(priceFeedAddress);
     s_lowImageURI = svgToImageUri(lowSvg);
     s_highImageURI = svgToImageUri(highSvg);

 }

 // functionalities
 
 function mintNft(int256 highValue) public {
     _safeMint(msg.sender, s_tokenCounter);
     s_tokenToHighValue[s_tokenCounter] = highValue;
      s_tokenCounter = s_tokenCounter + 1;
     emit CreatedNft(s_tokenCounter, highValue);
 }

 function svgToImageUri(string memory svg) public pure returns (string memory) {
     // convert the svgs to image uris and store them in variables for later use
     string memory baseURL = "data:image/svg+xml;base64,";
     string memory svgBase64Encoded = Base64.encode(bytes(string(abi.encodePacked(svg))));
     return string(abi.encodePacked(baseURL, svgBase64Encoded));
 }


 function _baseURI() internal pure override returns (string memory) {
     return "data:application/json;base64,";
 }

 function tokenURI(uint256 tokenId) public view override returns (string memory) {
     if(!_exists(tokenId)) {
         revert ERC721Metadata__URI_QueryFor_NonExistentToken();
     } 

     (, int256 price, , , ) = i_priceFeed.latestRoundData();

     string memory imageURI;
     if (price >= s_tokenToHighValue[tokenId]) {
           imageURI = s_highImageURI;
     } else {
          imageURI = s_lowImageURI;
     }
     return
         string(
         abi.encodePacked(
             _baseURI(),
             Base64.encode(
                 bytes(
                     abi.encodePacked(
                         '{"name":"',
                         name(),
                         '", "description":"An NFT that changes based on the chainlink feed",',
                         '"attributes": [{"trait_type": "coolness", "value": 100}], "image":"',
                         imageURI,
                         '"}'
                     )
                 )
             )
         )
         );
 }
 function getLowSVG()  public view returns (string memory) {
     return s_lowImageURI;
 }
   function getHighSVG()  public view returns (string memory) {
     return s_highImageURI;
 }
 function getTokenCounter() public view returns (uint256 ) {
     return s_tokenCounter;
 }
 function getPriceFeed() public view returns (AggregatorV3Interface) {
     return i_priceFeed;
 }
}

And here is the test code where the issue is being observed:

// DynamicSvgNft.test.js


const highSVGImageUri = "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjAwIDIwMCIgd2lkdGg9IjQwMCIgIGhlaWdodD0iNDAwIiB4bWxucz0";
    const lowSVGImageUri = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/...";

describe("Constructor", () => {
    it("sets the initial values correctly", async () => {
        const highSVG = await dynamicSvgNft.getHighSVG(); // Returns lowSVGImageUri instead of the highSVGImageUri
        const lowSVG = await dynamicSvgNft.getLowSVG();   // Returns the highSVGImageUri instead of the lowSVGImageUri
          // ... (assertions)
           console.log(`highSVG is => ${highSVG}`)
            assert.equal(highSVG, highSVGImageUri)
            console.log(`lowSVG is => ${lowSVG}`)
            assert.equal(lowSVG, lowSVGImageUri)
            assert.equal(tokenCounter.toString(), "0")
            assert.equal(priceFeed, MockV3Aggregator.address)
    });
});

// ... (other test code)

DynamicSvgNft.deploy.js

    const { network } = require("hardhat");
    const { developmentChains, networkConfig } = require("../helper-hardhat-config");
    const { verify } = require("../utils/verify");
    const fs = require("fs")
    
    module.exports = async ({getNamedAccounts, deployments}) => {
        const {deploy, log} = deployments
        const {deployer} = await  getNamedAccounts()
    
        const chainId = network.config.chainId
        let ethUsdPriceFeedAddress
    
        if(developmentChains.includes(network.name)) {
            const EthUsdAggregator = await deployments.get("MockV3Aggregator")
            ethUsdPriceFeedAddress  = EthUsdAggregator.address
        } else {
            ethUsdPriceFeedAddress = networkConfig[chainId].ethUsdPriceFeed
        }
    
        log("#####################################");
    
        // read the nft images
        const lowSVG =  fs.readFileSync("images/dynamicNft/frown.svg", {encoding: "utf8"})
        const highSVG =  fs.readFileSync("images/dynamicNft/happy.svg", {encoding: "utf8"})
        args = [ethUsdPriceFeedAddress, highSVG, lowSVG]
    
        const dynamicSvgNft = await deploy("DynamicSvgNft", {
            from: deployer,
            args:args,
            log: true,
            waitConfirmations: network.config.blockConfirmations || 1
        })
    
        if(!developmentChains.includes(network.name) && process.env.ETHERSCAN_API_KEY) {
            log("verifying.....")
            await verify(dynamicSvgNft.address, args)
            log("verified!")
    
            log("#########################################")
        }
    
    }
    module.exports.tags = ["all", "dynamicsvg", "main"]

The issue is that the getHighSVG() function is returning the value of s_lowImageURI, and vice versa, resulting in incorrect values being returned during the test

I have thoroughly reviewed the contract and test code, but I couldn't identify the root cause of the problem. I suspect that there might be an issue with the contract deployment or the svgToImageUri function used in the contract.

Any insights or suggestions on how to debug and fix this issue would be highly appreciated! If needed, I can also provide the full code for the contract and test files.

1

There are 1 answers

0
Web3Phoenix On

I encountered an issue with the DynamicSvgNft contract, where the getHighSVG() and getLowSVG() functions were returning incorrect image URIs. The contract is designed to set different SVG images based on the price feed value from a Chainlink oracle.

Upon investigation, I found that the problem was in the way I was passing the arguments to the contract constructor during deployment. The constructor expects the price feed address, followed by the low SVG and high SVG, in that order. However, I mistakenly passed the arguments in the wrong order, resulting in incorrect image URIs being stored in the contract.

    const { network } = require("hardhat");
    const { developmentChains, networkConfig } = require("../helper-hardhat-config");
    const { verify } = require("../utils/verify");
    const fs = require("fs")
    
    module.exports = async ({getNamedAccounts, deployments}) => {
        const {deploy, log} = deployments
        const {deployer} = await  getNamedAccounts()
    
        const chainId = network.config.chainId
        let ethUsdPriceFeedAddress
    
        if(chainId == 31337) {
            const EthUsdAggregator = await deployments.get("MockV3Aggregator")
            ethUsdPriceFeedAddress  = EthUsdAggregator.address
        } else {
            ethUsdPriceFeedAddress = networkConfig[chainId].ethUsdPriceFeed
        }
    
        log("#####################################");
    
        // read the nft images
        const lowSVG =  fs.readFileSync("images/dynamicNft/frown.svg", {encoding: "utf8"})
        const highSVG =  fs.readFileSync("images/dynamicNft/happy.svg", {encoding: "utf8"})
        args = [ethUsdPriceFeedAddress, highSVG, lowSVG]
    
        const dynamicSvgNft = await deploy("DynamicSvgNft", {
            from: deployer,
            args:args,
            log: true,
            waitConfirmations: network.config.blockConfirmations || 1
        })
    
        if(!developmentChains.includes(network.name) && process.env.ETHERSCAN_API_KEY) {
            log("verifying.....")
            await verify(dynamicSvgNft.address, args)
            log("verified!")
    
            log("#########################################")
        }
    
    }
    
    module.exports.tags = ["all", "dynamicsvg", "main"]

To resolve the issue, I corrected the argument order from:

args = [ethUsdPriceFeedAddress, highSVG, lowSVG]

to:

args = [ethUsdPriceFeedAddress, lowSVG, highSVG]

By passing the arguments in the correct order, the contract now sets the image URIs correctly based on the provided SVGs. The getHighSVG() and getLowSVG() functions now return the expected image URIs, and the tests are passing as expected.

Remember to always verify the order of arguments when deploying contracts with multiple parameters to avoid such problems.