如何用Solidity编写收取0.1ETH的铸币函数并在HardHat中验证
实现收取0.1ETH的Solidity铸币函数及HardHat验证方案
问题分析
当前代码存在几处关键问题:Solidity函数语法错误、token_id未自增,以及HardHat测试中变量不匹配、余额未重新查询,导致无法正确验证铸币收费逻辑。
Solidity铸币函数修正
核心问题修复
- 移除函数开头多余的分号
- 新增
token_id自增逻辑,避免重复铸币同一ID - 简化金额判断(
0.1 ether与100000000000000000 wei等价,无需重复判断) - 补充合约基础结构(继承ERC721和Ownable)
完整Solidity代码
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract PayableNFT is ERC721, Ownable { uint256 public token_id; // 构造函数:初始化NFT名称、符号,设置初始owner constructor(address initialOwner) ERC721("PayableNFT", "PNFT") Ownable(initialOwner) {} // 收取0.1ETH的铸币函数 function mint() payable public returns (uint256) { // 验证支付金额是否为0.1ETH require(msg.value == 0.1 ether, "必须支付0.1 ETH"); uint256 currentTokenId = token_id; // 安全铸币给调用者 _safeMint(msg.sender, currentTokenId); // 自增token_id,用于下一次铸币 token_id++; return currentTokenId; } // Owner提取合约中ETH的函数 function withdraw() public onlyOwner { uint256 contractBalance = address(this).balance; require(contractBalance > 0, "合约无余额可提取"); payable(owner()).transfer(contractBalance); } }
HardHat验证代码修正
核心问题修复
- 修正合约实例变量名不匹配问题(原代码部署
nft却调用hoodie.mint) - 调用铸币后重新查询余额(本地变量不会自动同步链上状态)
- 新增
deployed()等待合约部署完成 - 用signer直接查询余额,简化逻辑
完整HardHat测试代码
const { ethers } = require("hardhat"); async function main() { // 获取签名者 const [owner] = await ethers.getSigners(); // 加载合约工厂 const PayableNFT = await ethers.getContractFactory("PayableNFT"); // 部署合约 const payableNFT = await PayableNFT.deploy(owner.address); // 等待合约部署完成 await payableNFT.deployed(); console.log("合约部署地址:", payableNFT.address); // 查询铸币前owner余额 const balanceBefore = await owner.getBalance(); console.log("铸币前余额:", ethers.utils.formatEther(balanceBefore), "ETH"); // 调用铸币函数,支付0.1ETH const mintTx = await payableNFT.mint({ value: ethers.utils.parseEther("0.1") }); await mintTx.wait(); // 查询铸币后owner余额 const balanceAfter = await owner.getBalance(); console.log("铸币后余额:", ethers.utils.formatEther(balanceAfter), "ETH"); // 查询合约余额,验证是否收到0.1ETH const contractBalance = await ethers.provider.getBalance(payableNFT.address); console.log("合约当前余额:", ethers.utils.formatEther(contractBalance), "ETH"); // 验证owner持有铸出的NFT const tokenOwner = await payableNFT.ownerOf(0); console.log("Token ID 0的持有者:", tokenOwner); } main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); });
进阶测试用例(HardHat测试文件)
可以编写自动化测试用例验证逻辑正确性:
const { expect } = require("chai"); const { ethers } = require("hardhat"); describe("PayableNFT", function () { let PayableNFT; let payableNFT; let owner; const MINT_PRICE = ethers.utils.parseEther("0.1"); beforeEach(async function () { [owner] = await ethers.getSigners(); PayableNFT = await ethers.getContractFactory("PayableNFT"); payableNFT = await PayableNFT.deploy(owner.address); await payableNFT.deployed(); }); it("铸币时收取0.1ETH并正确 mint NFT", async function () { const balanceBefore = await owner.getBalance(); // 执行铸币交易 const tx = await payableNFT.mint({ value: MINT_PRICE }); const receipt = await tx.wait(); // 计算实际消耗的gas费用 const gasCost = receipt.gasUsed.mul(receipt.effectiveGasPrice); // 验证owner余额减少(0.1ETH + gas费) const balanceAfter = await owner.getBalance(); expect(balanceBefore.sub(balanceAfter)).to.be.closeTo(MINT_PRICE.add(gasCost), 10n ** 14n); // 验证合约收到0.1ETH expect(await ethers.provider.getBalance(payableNFT.address)).to.equal(MINT_PRICE); // 验证owner持有Token ID 0 expect(await payableNFT.ownerOf(0)).to.equal(owner.address); }); it("支付金额不足时铸币失败", async function () { await expect( payableNFT.mint({ value: ethers.utils.parseEther("0.05") }) ).to.be.revertedWith("必须支付0.1 ETH"); }); it("Owner可提取合约中的ETH", async function () { // 先铸币存入0.1ETH await payableNFT.mint({ value: MINT_PRICE }); const ownerBalanceBefore = await owner.getBalance(); // 提取合约余额 const withdrawTx = await payableNFT.withdraw(); const withdrawReceipt = await withdrawTx.wait(); const withdrawGasCost = withdrawReceipt.gasUsed.mul(withdrawReceipt.effectiveGasPrice); // 验证owner余额增加(0.1ETH - gas费) const ownerBalanceAfter = await owner.getBalance(); expect(ownerBalanceAfter.sub(ownerBalanceBefore)).to.be.closeTo(MINT_PRICE.sub(withdrawGasCost), 10n ** 14n); // 验证合约余额为0 expect(await ethers.provider.getBalance(payableNFT.address)).to.equal(0); }); });
内容的提问来源于stack exchange,提问作者Pexate
相关产品推荐
相关产品推荐

