This EIP standardizes an NFT licensing oracle to store (register) and retrieve (discover) granted licensing agreements for non-fungible token (NFT) derivative works, which are also NFTs but are created using properties of some other underlying NFTs.
In this standard, an NFT derivative work is referred to as a dNFT, while the original underlying NFT is referred to as an oNFT.
The NFT owner, known as the licensor, may authorize another creator, known as the licensee, to create a derivative works (dNFTs), in exchange for an agreed payment, known as a Royalty. A licensing agreement outlines terms and conditions related to the deal between the licensor and licensee.
Specification
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.
In general, there are three important roles in this standard:
oNFT: An original underlying NFT. The holder of an oNFT is a licensor. An oNFT can be any NFT.
dNFT: A derivative work based on one or more oNFTs. The holder of a dNFT is a licensee.
Registry: A trusted smart contract able to verify whether a credential is signed or released by the holder of oNFT.
Every dNFT contract must implement the IERC5635NFT and IERC165 inferfaces.
pragmasolidity^0.6.0;import"./IERC165.sol";///
/// @notice Interface of NFT derivatives (dNFT) for the NFT Licensing Standard
/// @dev The ERC-165 identifier for this interface is 0xd584841c.
interfaceIERC5635DNFTisIERC165{/// ERC165 bytes to add to interface array - set in parent contract
/// implementing this standard
///
/// bytes4(keccak256("IERC5635DNFT{}")) == 0xd584841c
/// bytes4 private constant _INTERFACE_ID_IERC5635DNFT = 0xd584841c;
/// _registerInterface(_INTERFACE_ID_IERC5635XDNFT);
/// @notice Get the number of credentials.
/// @param _tokenId - ID of the dNFT asset queried
/// @return _number - the number of credentials
functionnumberOfCredentials(uint256_tokenId)externalviewreturns(uint256_number);/// @notice Called with the sale price to determine how much royalty is owed and to whom.
/// @param _tokenId - ID of the dNFT asset queried
/// @param _credentialId - ID of the licensing agreement credential, the max id is numberOfCredentials(_tokenId)-1
/// @return _oNFT - the oNFT address where the licensing from
/// @return _tokenID - the oNFT ID where the licensing from
/// @return _registry - the address of registry which can verify this credential
functionauthorizedBy(uint256_tokenId,uint256_credentialId)externalviewreturns(address_oNFT,uint256_tokenId,address_registry);}interfaceIERC165{/// @notice Query if a contract implements an interface
/// @param interfaceID The interface identifier, as specified in ERC-165
/// @dev Interface identification is specified in ERC-165. This function
/// uses less than 30,000 gas.
/// @return `true` if the contract implements `interfaceID` and
/// `interfaceID` is not 0xffffffff, `false` otherwise
functionsupportsInterface(bytes4interfaceID)externalviewreturns(bool);}
Every Registry contract must implement the IERC5635Registry and IERC165 inferfaces.
pragmasolidity^0.6.0;import"./IERC165.sol";///
/// @dev Interface of NFT derivatives (dNFT) for the NFT Licensing Standard
/// Note: the ERC-165 identifier for this interface is 0xb5065e9f
interfaceIERC5635RegistryisIERC165{/// ERC165 bytes to add to interface array - set in parent contract
/// implementing this standard
///
/// bytes4(keccak256("IERC5635Registry{}")) == 0xb5065e9f
/// bytes4 private constant _INTERFACE_ID_IERC5635Registry = 0xb5065e9f;
/// _registerInterface(_INTERFACE_ID_IERC5635Registry);
// TODO: Is the syntax correct?
enumLicensingAgreementType{NonExclusive,Exclusive,Sole}/// @notice
/// @param _dNFT -
/// @param _dNFT_Id -
/// @param _oNFT -
/// @param _oNFT_Id -
/// @return _licensed -
/// @return _tokenID - the oNFT ID where the licensing from
/// @return _registry - the address of registry which can verify this credential
functionisLicensed(address_dNFT,uint256_dNFT_Id,address_oNFT,uint256_oNFT_Id)externalviewreturns(bool_licensed);/// @return _licenseIdentifier - the identifier, e.g. `MIT` or `Apache`, similar to `SPDX-License-Identifier: MIT` in SPDX.
functionlicensingInfo(address_dNFT,uint256_dNFT_Id,address_oNFT,uint256_oNFT_Id)externalviewreturns(bool_licensed,address_licensor,uint64_timeOfSignature,uint64_expiryTime,LicensingAgreementType_type,string_licenseName,string_licenseUri//
);functionroyaltyRate(address_dNFT,uint256_dNFT_Id,address_oNFT,uint256_oNFT_Id)externalviewreturns(addressbeneficiary,uint256rate// The decimals is 9, means to divide the rate by 1,000,000,000
);}
The Registry contract MAY implement the IERC5635Licensing and IERC165 inferfaces.
pragmasolidity^0.6.0;import"./IERC165.sol";///
///
interfaceIERC5635LicensingisIERC165,IERC5635Registry{eventLicence(addressindexed_oNFT,uint256indexed_oNFT_Id,addressindexed_dNFT,uint256indexed_dNFT_Id,uint64_expiryTime,LicensingAgreementType_type,string_licenseName,string_licenseUri);eventApproval(addressindexed_oNFT,addressindexed_owner,addressindexed_approved,uint256indexed_tokenId);eventApprovalForAll(addressindexed_oNFT,addressindexed_owner,addressindexed_operator,bool_approved);functionlicence(addressindexed_oNFT,uint256indexed_oNFT_Id,addressindexed_dNFT,uint256indexed_dNFT_Id,uint64_expiryTime,LicensingAgreementType_type,string_licenseName,string_licenseUri)externalpayable;//TODO: mortgages or not?
functionapprove(addressindexed_oNFT,address_approved,uint256_tokenId)externalpayable;//TODO: why payable?
functionsetApprovalForAll(addressindexed_oNFT,address_operator,bool_approved)external;functiongetApproved(addressindexed_oNFT,uint256_tokenId)externalviewreturns(address);functionisApprovedForAll(addressindexed_oNFT,address_owner,address_operator)externalviewreturns(bool);}
Rationale
Licensing credentials from a dNFT’s contract can be retrieved with authorizedBy, which specifies the details of a licensing agreement, which may include the oNFT. Those credentials may be verified with a registry service.
Anyone can retrieve licensing royalty information with licensingRoyalty via the registry. While it is not possible to enforce the rules set out in this EIP on-chain, just like EIP-2981, we encourages NFT marketplaces to follow this EIP.
Two stages: Licensing and Discovery
Taking the moment when the dNFT is minted as the cut-off point, the stage before is called the Licensing stage, and the subsequent stage is called the Discovery stage. The interface IERC5635Licensing is for the Licensing stage, and the interfaces IERC5635DNFT and IERC5635Registry are for the Discovery stage.
Design decision: beneficiary of licensing agreement
As soon as someone sells their NFT, the full licensed rights are passed along to the new owner without any encumbrances, so that the beneficiary should be the new owner.
Difference between CantBeEvil Licenses and Licensing Agreements.
CantBeEvil licenses are creator-holder licenses which indicate what rights the NFTs’ holder are granted from the creator. Meanwhile, licensing agreements is a contract between a licensor and licensee. So, CantBeEvil licenses cannot be used as a licensing agreement.
Design decision: Relationship between different approval levels
The approved address can license() the licensing agreement to dNFT on behalf of the holder of an oNFT. We define two levels of approval like that:
approve will lead to approval for one NFT related to an id.
setApprovalForAll will lead to approval of all NFTs owned by msg.sender.
Deploying an EIP-721 NFT and signaling support for dNFT
constructor(stringmemoryname,stringmemorysymbol,stringmemorybaseURI){_name=name;_symbol=symbol;_setBaseURI(baseURI);// register the supported interfaces to conform to ERC721 via ERC165
_registerInterface(_INTERFACE_ID_ERC721);_registerInterface(_INTERFACE_ID_ERC721_METADATA);_registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE);// dNFT interface
_registerInterface(_INTERFACE_ID_IERC5635DNFT);}
Checking if the NFT being sold on your marketplace is a dNFT