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

ERC721市场中ERC2981 NFT版税实现咨询及合约纠错

ERC2981版税问题解答及合约修正

一、_setDefaultRoyalty中feeNumerator的含义

feeNumerator既不是NFT价格,也不是直接的百分比数值——它是版税比例的分子,ERC2981默认用10000作为分母(即100% = 10000)。比如你要设置5%的版税,feeNumerator需要传500;10%就传1000,以此类推。

二、你的PodShip合约版税实现问题

代码里有几个核心错误:

  • 全局版税覆盖问题:_setDefaultRoyalty是设置合约内所有NFT的默认版税,每次调用都会覆盖之前的设置。你在listNFT里调用它,会导致所有NFT最终都使用最后一次上架的那个NFT的版税参数,完全不符合每个NFT独立设置版税的需求。
  • 版税数值错误:你传入的nftRoyaltyPercent是1-50的数值,比如传5的话,实际计算出的版税是5/10000=0.05%,和你预期的5%差了100倍。
  • 缺少接口声明:ERC721和ERC2981组合时,必须重写supportsInterface函数,否则NFT市场无法识别你的合约支持ERC2981标准。
  • 设置时机错误:版税是和NFT本身绑定的,应该在 mint 时设置,而不是上架时。

三、修正后的关键代码示例

// SPDX-License-Identifier: MIT
pragma solidity 0.8.8;

import "hardhat/console.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/token/common/ERC2981.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";

contract PodShip is ERC721URIStorage, ERC2981, Ownable {
    using Counters for Counters.Counter;
    Counters.Counter private _podcastId;
    Counters.Counter private _nftId;

    struct Podcast {
        address nftCreator;
        address nftOwner;
        string nftName;
        string nftDescription;
        uint256 nftPrice;
        uint256 nftRoyaltyBasisPoints; // 存储基点值,1%=100基点
        uint256 nftId;
        bool listed;
    }

    mapping(uint256 => Podcast) podcastId;

    constructor() ERC721("PodShip NFTs", "PODSHIP") {}

    // 必须重写该函数,让合约支持ERC2981接口
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721URIStorage, ERC2981) returns (bool) {
        return super.supportsInterface(interfaceId);
    }

    function mintNft(
        string memory ipfsURI,
        string memory _nftName, 
        string memory _nftDescription, 
        uint256 _nftPrice, 
        uint256 _nftRoyaltyPercent) public returns(uint256) {
            require(_nftPrice > 0, "NFT Price not set correctly");
            require(_nftRoyaltyPercent > 0 && _nftRoyaltyPercent <= 50, "NFT Royalty should be in-between 1 & 50");
            
            // 把百分比转换成基点(1% = 100),匹配ERC2981的分母10000
            uint256 royaltyBasisPoints = _nftRoyaltyPercent * 100;

            _podcastId.increment();
            _nftId.increment();
            uint256 nft_Id = _nftId.current();

            _safeMint(msg.sender, nft_Id);
            _setTokenURI(nft_Id, ipfsURI);

            // 为当前NFT单独设置专属版税,不影响其他NFT
            _setTokenRoyalty(nft_Id, payable(msg.sender), uint96(royaltyBasisPoints));

            podcastId[_podcastId.current()] = Podcast(
            payable(msg.sender), 
            payable(msg.sender), 
            _nftName, 
            _nftDescription, 
            _nftPrice, 
            royaltyBasisPoints,
            nft_Id,
            false
        );
        console.log(nft_Id);
        console.log(_podcastId.current());

        return nft_Id;
    }

    function listNFT(uint256 _podcastID) public {
        require(msg.sender == podcastId[_podcastID].nftOwner && msg.sender == podcastId[_podcastID].nftCreator, "Only Owner can list his NFT");
        podcastId[_podcastID].listed = true;
        approve(address(this), podcastId[_podcastID].nftId);
        setApprovalForAll(address(this), true);
        // 移除原有的_setDefaultRoyalty调用
    }
}

四、核心修正说明

  1. 用_setTokenRoyalty替代_setDefaultRoyalty:这个函数是为单个NFT设置专属版税,不会干扰其他NFT的版税配置。
  2. 转换版税为基点:把1-50的百分比转换成100-5000的基点,确保实际版税比例和预期一致。
  3. 添加supportsInterface重写:让合约正确暴露ERC2981接口,兼容主流NFT市场的版税读取逻辑。
  4. 调整版税设置时机:在 mint NFT 时就完成版税绑定,逻辑更合理。

内容的提问来源于stack exchange,提问作者DevABDee

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 12:15:43