You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Solidity编写收取0.1ETH的铸币函数并在HardHat中验证

实现收取0.1ETH的Solidity铸币函数及HardHat验证方案

问题分析

当前代码存在几处关键问题:Solidity函数语法错误、token_id未自增,以及HardHat测试中变量不匹配、余额未重新查询,导致无法正确验证铸币收费逻辑。

Solidity铸币函数修正

核心问题修复

  1. 移除函数开头多余的分号
  2. 新增token_id自增逻辑,避免重复铸币同一ID
  3. 简化金额判断(0.1 ether与100000000000000000 wei等价,无需重复判断)
  4. 补充合约基础结构(继承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验证代码修正

核心问题修复

  1. 修正合约实例变量名不匹配问题(原代码部署nft却调用hoodie.mint)
  2. 调用铸币后重新查询余额(本地变量不会自动同步链上状态)
  3. 新增deployed()等待合约部署完成
  4. 用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 09:54:21