DynamicSvgNft合约问题:getHighSVG与getLowSVG返回值颠倒
DynamicSvgNft合约SVG URI返回值混淆问题修复
问题现象
合约中getHighSVG()返回s_lowImageURI的值,getLowSVG()返回s_highImageURI的值,导致测试结果不符合预期——高SVG的获取函数返回低SVG的URI,反之亦然。
相关代码
合约代码(DynamicSvgNft.sol)
// 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; } }
测试代码(DynamicSvgNft.test.js)
// 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)
// 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"]
问题根源
合约构造函数定义的参数顺序为:(address priceFeedAddress, string memory lowSvg, string memory highSvg),但部署脚本中传递的参数顺序是[ethUsdPriceFeedAddress, highSVG, lowSVG]——将highSVG传给了lowSvg参数,lowSVG传给了highSvg参数。
这导致构造函数中:
s_lowImageURI = svgToImageUri(lowSvg)实际存储的是highSVG转换后的URIs_highImageURI = svgToImageUri(highSvg)实际存储的是lowSVG转换后的URI
最终调用getHighSVG()返回的是lowSVG的URI,getLowSVG()返回的是highSVG的URI,完全颠倒。
修复方案
修改部署脚本中的参数顺序,使其与合约构造函数的参数顺序匹配:
将部署脚本中的:
args = [ethUsdPriceFeedAddress, highSVG, lowSVG]
改为:
args = [ethUsdPriceFeedAddress, lowSVG, highSVG]
验证步骤
- 清理之前的部署记录(Hardhat环境下运行
npx hardhat clean) - 重新部署合约:
npx hardhat deploy --tags dynamicsvg - 运行测试:
npx hardhat test - 检查测试输出,确认
getHighSVG()返回highSVGImageUri,getLowSVG()返回lowSVGImageUri
内容的提问来源于stack exchange,提问作者Web3Phoenix
相关产品推荐
相关产品推荐

