DCIPs/EIPS/eip-5635.md

266 lines
10 KiB
Markdown
Raw Normal View History

---
eip: 5635
title: NFT Licensing Agreements
description: An oracle for retrieving NFT licensing agreements
author: Timi (@0xTimi), 0xTriple7 (@ysqi)
discussions-to: https://ethereum-magicians.org/t/eip-5635-discussion-nft-licensing-agreement-standard/10779
status: Draft
type: Standards Track
category: ERC
created: 2022-08-10
requires: 165, 721, 1155, 2981
---
## Abstract
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.
```solidity
pragma solidity ^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.
interface IERC5635DNFT is IERC165 {
/// 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
function numberOfCredentials(
uint256 _tokenId
) external view returns (
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
function authorizedBy(
uint256 _tokenId,
uint256 _credentialId
) external view returns (
address _oNFT,
uint256 _tokenId,
address _registry
);
}
interface IERC165 {
/// @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
function supportsInterface(bytes4 interfaceID) external view returns (bool);
}
```
Every **Registry** contract must implement the `IERC5635Registry` and `IERC165` inferfaces.
```solidity
pragma solidity ^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
interface IERC5635Registry is IERC165 {
/// 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?
enum LicensingAgreementType {
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
function isLicensed(
address _dNFT,
uint256 _dNFT_Id,
address _oNFT,
uint256 _oNFT_Id
) external view returns (
bool _licensed
);
/// @return _licenseIdentifier - the identifier, e.g. `MIT` or `Apache`, similar to `SPDX-License-Identifier: MIT` in SPDX.
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 // The decimals is 9, means to divide the rate by 1,000,000,000
);
}
```
The **Registry** contract MAY implement the `IERC5635Licensing` and `IERC165` inferfaces.
```solidity
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: mortgages or not?
function approve(address indexed _oNFT, address _approved, uint256 _tokenId) external payable; //TODO: why 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);
}
```
## 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](./eip-2981.md), 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:
1. `approve` will lead to approval for one NFT related to an id.
2. `setApprovalForAll` will lead to approval of all NFTs owned by `msg.sender`.
## Backwards Compatibility
This standard is compatible with [EIP-721](./eip-721.md), [EIP-1155](./eip-1155.md), and [EIP-2981](./eip-2981.md).
## Reference Implementation
### Examples
#### Deploying an [EIP-721](./eip-721.md) NFT and signaling support for dNFT
```solidity
constructor (string memory name, string memory symbol, string memory baseURI) {
_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
```solidity
bytes4 private constant _INTERFACE_ID_IERC5635DNFT = 0xd584841c;
function checkDNFT(address _contract) internal returns (bool) {
(bool success) = IERC165(_contract).supportsInterface(_INTERFACE_ID_IERC5635DNFT);
return success;
}
```
#### Checking if an address is a Registry
```solidity
bytes4 private constant _INTERFACE_ID_IERC5635Registry = 0xb5065e9f;
function checkLARegistry(address _contract) internal returns (bool) {
(bool success) = IERC165(_contract).supportsInterface(_INTERFACE_ID_IERC5635Registry);
return success;
}
```
## Security Considerations
Needs discussion.
## Copyright
Copyright and related rights waived via [CC0](../LICENSE.md).