ERC-5635: NFT 许可协议
用于检索 NFT 许可协议的预言机
Authors | Timi (@0xTimi), 0xTriple7 (@ysqi) |
---|---|
Created | 2022-08-10 |
Discussion Link | https://ethereum-magicians.org/t/eip-5635-discussion-nft-licensing-agreement-standard/10779 |
Requires | EIP-165, EIP-721, EIP-1155, EIP-2981 |
Table of Contents
摘要
本 EIP 标准化了一个 NFT 许可预言机,用于存储(注册)和检索(发现)为非同质化代币(NFT)衍生作品授予的许可协议,这些衍生作品也是 NFT,但使用其它底层 NFT 的属性创建。
在本标准中,NFT 衍生作品被称为 dNFT,而原始底层 NFT 被称为 oNFT。
NFT 所有者,被称为 licensor
(许可方),可以授权另一个创建者,被称为 licensee
(被许可方),以创建衍生作品(dNFT),以换取约定的付款,被称为 Royalty
(版权费)。许可协议概述了许可方和被许可方之间交易相关的条款和条件。
规范
本文档中的关键词“必须”,“禁止”,“需要”,“应该”,“不应该”,“推荐”,“可以”和“可选”应按照 RFC 2119 中的描述进行解释。
一般来说,此标准中有三个重要的角色:
- oNFT:原始底层 NFT。oNFT 的持有者是许可方。oNFT 可以是任何 NFT。
- dNFT:基于一个或多个 oNFT 的衍生作品。dNFT 的持有者是被许可方。
- Registry(注册机构):一个可信的智能合约,能够验证凭证是否由 oNFT 的持有者签名或发布。
每个 dNFT 合约必须实现 IERC5635NFT
和 IERC165
接口。
pragma solidity ^0.6.0;
import "./IERC165.sol";
///
/// @notice NFT Licensing Standard 中 NFT 衍生品 (dNFT) 的接口
/// @dev 此接口的 ERC-165 标识符是 0xd584841c。
interface IERC5635DNFT is IERC165 {
/// 要添加到接口数组的 ERC165 字节 - 设置在实现此标准的父合约中
///
/// bytes4(keccak256("IERC5635DNFT{}")) == 0xd584841c
/// bytes4 private constant _INTERFACE_ID_IERC5635DNFT = 0xd584841c;
/// _registerInterface(_INTERFACE_ID_IERC5635XDNFT);
/// @notice 获取凭证的数量。
/// @param _tokenId - 被查询的 dNFT 资产的 ID
/// @return _number - 凭证的数量
function numberOfCredentials(
uint256 _tokenId
) external view returns (
uint256 _number
);
/// @notice 使用销售价格调用以确定欠多少版权费以及欠给谁。
/// @param _tokenId - 被查询的 dNFT 资产的 ID
/// @param _credentialId - 许可协议凭证的 ID,最大 ID 是 numberOfCredentials(_tokenId)-1
/// @return _oNFT - 许可来自的 oNFT 地址
/// @return _tokenID - 许可来自的 oNFT ID
/// @return _registry - 可以验证此凭证的注册机构的地址
function authorizedBy(
uint256 _tokenId,
uint256 _credentialId
) external view returns (
address _oNFT,
uint256 _tokenId,
address _registry
);
}
interface IERC165 {
/// @notice 查询合约是否实现了接口
/// @param interfaceID 接口标识符,如 ERC-165 中指定
/// @dev 接口标识在 ERC-165 中指定。这个函数
/// 使用不到 30,000 gas。
/// @return 如果合约实现了 `interfaceID` 并且
/// `interfaceID` 不是 0xffffffff,则返回 `true`,否则返回 `false`
function supportsInterface(bytes4 interfaceID) external view returns (bool);
}
每个 Registry 合约必须实现 IERC5635Registry
和 IERC165
接口。
pragma solidity ^0.6.0;
import "./IERC165.sol";
///
/// @dev NFT Licensing Standard 中 NFT 衍生品 (dNFT) 的接口
/// 注意:此接口的 ERC-165 标识符是 0xb5065e9f
interface IERC5635Registry is IERC165 {
/// 要添加到接口数组的 ERC165 字节 - 设置在实现此标准的父合约中
///
/// bytes4(keccak256("IERC5635Registry{}")) == 0xb5065e9f
/// bytes4 private constant _INTERFACE_ID_IERC5635Registry = 0xb5065e9f;
/// _registerInterface(_INTERFACE_ID_IERC5635Registry);
// TODO: 语法是否正确?
enum LicensingAgreementType {
NonExclusive,
Exclusive,
Sole
}
/// @notice
/// @param _dNFT -
/// @param _dNFT_Id -
/// @param _oNFT -
/// @param _oNFT_Id -
/// @return _licensed -
/// @return _tokenID - 许可来自的 oNFT ID
/// @return _registry - 可以验证此凭证的注册机构的地址
function isLicensed(
address _dNFT,
uint256 _dNFT_Id,
address _oNFT,
uint256 _oNFT_Id
) external view returns (
bool _licensed
);
/// @return _licenseIdentifier - 标识符,例如 `MIT` 或 `Apache`,类似于 SPDX 中的 `SPDX-License-Identifier: MIT`。
function licensingInfo(
address _dNFT,
uint256 _dNFT_Id,
address _oNFT,
uint256 _oNFT_Id
) external view returns (
bool _licensed,
address _licensor,
uint64 _timeOfSignature,
uint64 _expiryTime,
LicensingAgreementType _type,
string _licenseName,
string _licenseUri //
);
function royaltyRate(
address _dNFT,
uint256 _dNFT_Id,
address _oNFT,
uint256 _oNFT_Id
) external view returns (
address beneficiary,
uint256 rate // 小数位是 9,表示将费率除以 1,000,000,000
);
}
Registry 合约可以实现 IERC5635Licensing
和 IERC165
接口。
pragma solidity ^0.6.0;
import "./IERC165.sol";
///
///
interface IERC5635Licensing is IERC165, IERC5635Registry {
event Licence(address indexed _oNFT, uint256 indexed _oNFT_Id, address indexed _dNFT, uint256 indexed _dNFT_Id, uint64 _expiryTime, LicensingAgreementType _type, string _licenseName, string _licenseUri);
event Approval(address indexed _oNFT, address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
event ApprovalForAll(address indexed _oNFT, address indexed _owner, address indexed _operator, bool _approved);
function licence(address indexed _oNFT, uint256 indexed _oNFT_Id, address indexed _dNFT, uint256 indexed _dNFT_Id, uint64 _expiryTime, LicensingAgreementType _type, string _licenseName, string _licenseUri) external payable; //TODO: 抵押贷款与否?
function approve(address indexed _oNFT, address _approved, uint256 _tokenId) external payable; //TODO: 为什么是 payable?
function setApprovalForAll(address indexed _oNFT, address _operator, bool _approved) external;
function getApproved(address indexed _oNFT, uint256 _tokenId) external view returns (address);
function isApprovedForAll(address indexed _oNFT, address _owner, address _operator) external view returns (bool);
}
理由
可以使用 authorizedBy
从 dNFT 的合约中检索许可凭证,该凭证指定了许可协议的详细信息,其中可能包括 oNFT。这些凭证可以通过 registry
服务进行验证。
任何人都可以通过注册机构使用 licensingRoyalty
检索许可版税信息。虽然无法在链上强制执行此 EIP 中规定的规则,就像 EIP-2981 一样,但我们鼓励 NFT 市场遵守此 EIP。
两个阶段:许可和发现
以铸造 dNFT 的时刻作为分界点,之前的阶段称为许可阶段,之后的阶段称为发现阶段。接口 IERC5635Licensing
用于许可阶段,接口 IERC5635DNFT
和 IERC5635Registry
用于发现阶段。
设计决策:许可协议的受益人
一旦有人出售他们的 NFT,完整的许可权利将没有任何负担地传递给新的所有者,因此受益人应该是新的所有者。
CantBeEvil 许可证和许可协议之间的区别。
CantBeEvil 许可证是创建者-持有者许可证,它表明 NFT 持有者从创建者那里获得的权利。与此同时,许可协议是许可方和被许可方之间的合同。因此,CantBeEvil 许可证不能用作许可协议。
设计决策:不同批准级别之间的关系
被批准的地址可以代表 oNFT 的持有者将许可协议 license()
给 dNFT。我们定义了两个级别的批准:
approve
将导致批准与 ID 相关的某个 NFT。setApprovalForAll
将导致批准msg.sender
拥有的所有 NFT。
向后兼容性
此标准与 EIP-721、EIP-1155 和 EIP-2981 兼容。
参考实现
例子
部署一个 EIP-721 NFT 并发出支持 dNFT 的信号
constructor (string memory name, string memory symbol, string memory baseURI) {
_name = name;
_symbol = symbol;
_setBaseURI(baseURI);
// 注册受支持的接口以通过 ERC165 符合 ERC721
_registerInterface(_INTERFACE_ID_ERC721);
_registerInterface(_INTERFACE_ID_ERC721_METADATA);
_registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE);
// dNFT 接口
_registerInterface(_INTERFACE_ID_IERC5635DNFT);
}
检查在您的市场上出售的 NFT 是否是 dNFT
bytes4 private constant _INTERFACE_ID_IERC5635DNFT = 0xd584841c;
function checkDNFT(address _contract) internal returns (bool) {
(bool success) = IERC165(_contract).supportsInterface(_INTERFACE_ID_IERC5635DNFT);
return success;
}
检查地址是否是注册机构
bytes4 private constant _INTERFACE_ID_IERC5635Registry = 0xb5065e9f;
function checkLARegistry(address _contract) internal returns (bool) {
(bool success) = IERC165(_contract).supportsInterface(_INTERFACE_ID_IERC5635Registry);
return success;
}
安全考虑
需要讨论。
版权
在 CC0 下放弃版权和相关权利。
Citation
Please cite this document as:
Timi (@0xTimi), 0xTriple7 (@ysqi), "ERC-5635: NFT 许可协议 [DRAFT]," Ethereum Improvement Proposals, no. 5635, August 2022. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-5635.