Alert Source Discuss
⚠️ Review Standards Track: ERC

ERC-7634: 有限转移次数 NFT

一种 ERC-721 扩展,用于限制 NFT 之间的可转移性

Authors Qin Wang (@qinwang-git), Saber Yu (@OniReimu), Shiping Chen <shiping.chen@data61.csiro.au>
Created 2024-02-22
Requires EIP-165, EIP-721

摘要

本标准扩展了 ERC-721,引入了一种机制,允许铸造者通过一个名为 TransferCount 的参数来定制 NFT 的可转移性。TransferCount 设置了 NFT 可以被转移的次数限制。该标准规定了一个接口,其中包括用于设置和检索转移限制、跟踪转移计数以及定义转移前和转移后状态的函数。该标准能够更精细地控制 NFT 的所有权和转移权利,确保 NFT 可以被编程为具有特定的、可执行的转移限制。

动机

一旦 NFT 被售出,它们就会从铸造者(创建者)处分离,并且此后可以永久转移。然而,许多情况下需要对 NFT 的发行进行精确控制。我们从三个维度概述了它们的优势。

首先,通过限制 NFT 的销售或交易频率,可以保护稀有 NFT 的价值。例如,在拍卖中,限制一个令人垂涎的项目竞标轮数可以维持其溢价(尤其是在荷兰式拍卖中)。同样,在知识产权领域,专利可以被有限的转移次数所约束,之后才能被自由访问(进入 CC0)。在游戏领域,诸如武器、服装和车辆之类的物品可能具有有限的寿命,每次使用或交换都会导致磨损,最终在达到预定阈值时自动报废(销毁)。

其次,强制限制交易频率可以通过减轻与恶意 NFT 套利相关的风险(包括高频交易 (HFT))来增强网络安全和稳定性。虽然这提出了一个常见的漏洞,但缺乏易于部署和有效的方法来解决它,使得我们的方法特别有价值。

此外,限制转移的轮数可以减轻与(重新)质押 NFT 相关的经济风险,从而抑制潜在的泡沫。随着重新质押机制的快速发展,可以预见的是,用户可能很快就会参与多轮 NFT 质押(例如,NFT → stNFT → st^2NFT),类似于使用像 EigenLayer (Ethereum)、Babylon (Bitcoin) 和 Picasso (Solana) 这样的第三方平台质押流动性代币。值得注意的是,EigenLayer 当前的设置采用 NFT 作为参与者的重新质押位置(一种重新质押证明)。如果此 NFT 被重复重新质押到市场中,它可能会放大杠杆作用并加剧泡沫动态。通过限制质押迭代的次数,我们可以主动防止质押生态系统中出现类似庞氏骗局的动态。

主要收获

此标准提供了以下几个优点:

受控的价值保存:通过允许铸造者为 NFT 设置自定义的转移限制,此标准有助于保存数字资产的价值。正如实物收藏品通常会因稀缺性而获得或保持价值一样,限制 NFT 的转移次数可以帮助确保其价值随时间的推移而持续。

确保预期用途:设置转移限制可以确保 NFT 以符合其预期用途的方式使用。例如,如果 NFT 代表限量版数字艺术品,则限制转移可以防止其被过度交易并可能贬值。

扩展用例:这些增强功能通过为创建者和所有者提供更多的控制和灵活性,从而拓宽了 NFT 的潜在应用。例如,NFT 可以用于表示具有有限可转移性的会员资格或许可证,从而为数字所有权模型开辟新的可能性。

易于集成:为了确保广泛采用和易于集成,此标准扩展了现有的 ERC-721 接口。通过定义一个单独的接口(IERC7634),其中包括新函数,该标准允许现有的 ERC-721 合同以最小的更改来采用新功能。这种方法促进了向后兼容性,并鼓励将转移限制无缝地整合到当前的 NFT 项目中。

规范

本文档中的关键词“必须”,“不得”,“必需”,“应该”,“不应该”,“推荐”,“可以”和“可选”应解释为 RFC 2119 中所述。

  • setTransferLimit:一个为 tokenId 建立转移限制的函数。
  • transferLimitOf:一个检索 tokenId 的转移限制的函数。
  • transferCountOf:一个返回 tokenId 的当前转移计数的函数。

该标准的实现者必须具有以下所有函数:


pragma solidity ^0.8.4;

/// @title IERC7634 有限可转移 NFT 接口
/// @dev ERC7634 有限可转移 NFT 扩展的接口,适用于 ERC721
/// @author Saber Yu

interface IERC7634 {

    /**
     * @dev 当转移计数被设置或更新时发出
     */
    event TransferCount(uint256 indexed tokenId, address owner, uint256 counts);

    /**
     * @dev 返回 tokenId 的当前转移计数
     */
    function transferCountOf(uint256 tokenId) external view returns (uint256);

    /**
     * @dev 设置 tokenId 的转移限制。只能由 token 所有者或批准的地址调用。
     * @param tokenId 要设置限制的 token 的 ID
     * @param limit 允许 token 转移的最大次数
     */
    function setTransferLimit(uint256 tokenId, uint256 limit) external;

    /**
     * @dev 返回 tokenId 的转移限制
     */
    function transferLimitOf(uint256 tokenId) external view returns (uint256);
}
    

理由

跟踪内部转移计数重要吗?

是,也不是。它是可选的,并且完全取决于实际需求。如果您选择跟踪,下面给出的参考实现是一个推荐的实现。_incrementTransferCount 函数和相关的检索函数(transferLimitOftransferCountOf)旨在跟踪 NFT 已经经历的转移次数。这种内部跟踪机制对于强制执行铸造者的转移限制至关重要,确保一旦达到限制,就不会发生进一步的转移。

如果选择跟踪,这就是我们可能想要跟踪的全部内容吗?

建议还跟踪之前和之后的状态。可选的 _beforeTokenTransfer_afterTokenTransfer 函数被重写以定义 NFT 在转移之前和之后的状态。这些函数确保根据转移限制和计数执行任何必要的检查或更新。通过将这些检查集成到转移过程中,该标准确保始终执行转移限制。

向后兼容性

通过添加扩展函数集,此标准可以完全与 ERC-721 兼容。

扩展

除了现有的 NFT 协议之外,还可以使用其他高级功能来增强此标准。例如:

  • 结合销毁函数(例如,ERC-5679)将使 NFT 在达到其转移限制后自动过期,类似于 Snapchat 消息在多次查看后消失的短暂特性。

  • 结合 SBT 标准中定义的非转移函数,将使 NFT 在预定数量的交易后与单个所有者结算并绑定绑定。此功能类似于投标人最终在参与多轮投标后获得资金的情况。

参考实现

推荐的实现如下所示:

  • _incrementTransferCount:一个内部函数,用于增加转移计数。
  • _beforeTokenTransfer:一个重写的函数,用于定义转移之前的状态。
  • _afterTokenTransfe:一个重写的函数,用于概述转移之后的状态。

pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "./IERC7634.sol";

/// @title ERC721 的有限可转移 NFT 扩展
/// @dev 有限可转移 NFT 扩展的实现,适用于 ERC721
/// @author Saber Yu

contract ERC7634 is ERC721, IERC7634 {

    // 从 tokenId 到转移计数的映射
    mapping(uint256 => uint256) private _transferCounts;

    // 从 tokenId 到其最大转移限制的映射
    mapping(uint256 => uint256) private _transferLimits;

    /**
     * @dev 参见 {IERC7634-transferCountOf}。
     */
    function transferCountOf(uint256 tokenId) public view override returns (uint256) {
        require(_exists(tokenId), "ERC7634: Nonexistent token");
        return _transferCounts[tokenId];
    }

    /**
     * @dev 参见 {IERC7634-setTransferLimit}。
     */
    function setTransferLimit(uint256 tokenId, uint256 limit) public override {
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC7634: caller is not owner nor approved");
        _transferLimits[tokenId] = limit;
    }

    /**
     * @dev 参见 {IERC7634-transferLimitOf}。
     */
    function transferLimitOf(uint256 tokenId) public view override returns (uint256) {
        require(_exists(tokenId), "ERC7634: Nonexistent token");
        return _transferLimits[tokenId];
    }

    /**
     * @dev 用于增加转移计数的内部函数。
     */
    function _incrementTransferCount(uint256 tokenId) internal {
        _transferCounts[tokenId] += 1;
        emit TransferCount(tokenId, ownerOf(tokenId), _transferCounts[tokenId]);
    }

    /**
     * @dev 重写 {_beforeTokenTransfer} 以强制执行转移限制。
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal override {
        require(_transferCounts[tokenId] < _transferLimits[tokenId], "ERC7634: Transfer limit reached");
        super._beforeTokenTransfer(from, to, tokenId);
    }

    /**
     * @dev 重写 {_afterTokenTransfer} 以处理转移后逻辑。
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 tokenId,
        uint256 quantity
    ) internal virtual override {
        _incrementTransferCount(tokenId);

        if (_transferCounts[tokenId] == _transferLimits[tokenId]) {
            // 达到限制后可选的转移后操作
            // 根据所需的行为取消注释以下内容,例如 `burn` 操作
            // ---------------------------------------
            // _burn(tokenId); // 销毁 token
            // ---------------------------------------
        }

        super._afterTokenTransfer(from, to, tokenId, quantity);
    }


    /**
     * @dev 重写 {supportsInterface} 以声明对 IERC7634 的支持。
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) {
        return interfaceId == type(IERC7634).interfaceId || super.supportsInterface(interfaceId);
    }
}

安全考虑

  • 确保每个 NFT 铸造者都可以调用此函数来设置转移限制。
  • 考虑使转移限制在设置后不可变,以防止篡改或未经授权的修改。
  • 避免在与高级功能集成时执行资源密集型操作,这可能会超过执行期间的 gas 限制。

版权

版权和相关权利通过 CC0 放弃。

Citation

Please cite this document as:

Qin Wang (@qinwang-git), Saber Yu (@OniReimu), Shiping Chen <shiping.chen@data61.csiro.au>, "ERC-7634: 有限转移次数 NFT [DRAFT]," Ethereum Improvement Proposals, no. 7634, February 2024. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7634.