Implement Non Fungible Token Royalty (EIP2981) (#3012)
Co-authored-by: Francisco Giordano <frangio.1@gmail.com> Co-authored-by: Hadrien Croubois <hadrien.croubois@gmail.com>
This commit is contained in:
@ -24,6 +24,7 @@ Additionally there are a few of other extensions:
|
||||
|
||||
* {ERC721URIStorage}: A more flexible but more expensive way of storing metadata.
|
||||
* {ERC721Votes}: Support for voting and vote delegation.
|
||||
* {ERC721Royalty}: A way to signal royalty information following ERC2981.
|
||||
* {ERC721Pausable}: A primitive to pause contract operation.
|
||||
* {ERC721Burnable}: A way for token holders to burn their own tokens.
|
||||
|
||||
@ -53,6 +54,8 @@ NOTE: This core set of contracts is designed to be unopinionated, allowing devel
|
||||
|
||||
{{ERC721Votes}}
|
||||
|
||||
{{ERC721Royalty}}
|
||||
|
||||
== Presets
|
||||
|
||||
These contracts are preconfigured combinations of the above features. They can be used through inheritance or as models to copy and paste their source code.
|
||||
|
||||
38
contracts/token/ERC721/extensions/ERC721Royalty.sol
Normal file
38
contracts/token/ERC721/extensions/ERC721Royalty.sol
Normal file
@ -0,0 +1,38 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts v4.4.0 (token/ERC721/extensions/ERC721Royalty.sol)
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../ERC721.sol";
|
||||
import "../../common/ERC2981.sol";
|
||||
import "../../../utils/introspection/ERC165.sol";
|
||||
|
||||
/**
|
||||
* @dev Extension of ERC721 with the ERC2981 NFT Royalty Standard, a standardized way to retrieve royalty payment
|
||||
* information.
|
||||
*
|
||||
* Royalty information can be specified globally for all token ids via {_setDefaultRoyalty}, and/or individually for
|
||||
* specific token ids via {_setTokenRoyalty}. The latter takes precedence over the first.
|
||||
*
|
||||
* IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See
|
||||
* https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the EIP. Marketplaces are expected to
|
||||
* voluntarily pay royalties together with sales, but note that this standard is not yet widely supported.
|
||||
*
|
||||
* _Available since v4.5._
|
||||
*/
|
||||
abstract contract ERC721Royalty is ERC2981, ERC721 {
|
||||
/**
|
||||
* @dev See {IERC165-supportsInterface}.
|
||||
*/
|
||||
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, ERC2981) returns (bool) {
|
||||
return super.supportsInterface(interfaceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev See {ERC721-_burn}. This override additionally clears the royalty information for the token.
|
||||
*/
|
||||
function _burn(uint256 tokenId) internal virtual override {
|
||||
super._burn(tokenId);
|
||||
_resetTokenRoyalty(tokenId);
|
||||
}
|
||||
}
|
||||
112
contracts/token/common/ERC2981.sol
Normal file
112
contracts/token/common/ERC2981.sol
Normal file
@ -0,0 +1,112 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts v4.4.0 (token/common/ERC2981.sol)
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../../interfaces/IERC2981.sol";
|
||||
import "../../utils/introspection/ERC165.sol";
|
||||
|
||||
/**
|
||||
* @dev Implementation of the NFT Royalty Standard, a standardized way to retrieve royalty payment information.
|
||||
*
|
||||
* Royalty information can be specified globally for all token ids via {_setDefaultRoyalty}, and/or individually for
|
||||
* specific token ids via {_setTokenRoyalty}. The latter takes precedence over the first.
|
||||
*
|
||||
* Royalty is specified as a fraction of sale price. {_feeDenominator} is overridable but defaults to 10000, meaning the
|
||||
* fee is specified in basis points by default.
|
||||
*
|
||||
* IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See
|
||||
* https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the EIP. Marketplaces are expected to
|
||||
* voluntarily pay royalties together with sales, but note that this standard is not yet widely supported.
|
||||
*
|
||||
* _Available since v4.5._
|
||||
*/
|
||||
abstract contract ERC2981 is IERC2981, ERC165 {
|
||||
struct RoyaltyInfo {
|
||||
address receiver;
|
||||
uint96 royaltyFraction;
|
||||
}
|
||||
|
||||
RoyaltyInfo private _defaultRoyaltyInfo;
|
||||
mapping(uint256 => RoyaltyInfo) private _tokenRoyaltyInfo;
|
||||
|
||||
/**
|
||||
* @dev See {IERC165-supportsInterface}.
|
||||
*/
|
||||
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) {
|
||||
return interfaceId == type(IERC2981).interfaceId || super.supportsInterface(interfaceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc IERC2981
|
||||
*/
|
||||
function royaltyInfo(uint256 _tokenId, uint256 _salePrice) external view override returns (address, uint256) {
|
||||
RoyaltyInfo memory royalty = _tokenRoyaltyInfo[_tokenId];
|
||||
|
||||
if (royalty.receiver == address(0)) {
|
||||
royalty = _defaultRoyaltyInfo;
|
||||
}
|
||||
|
||||
uint256 royaltyAmount = (_salePrice * royalty.royaltyFraction) / _feeDenominator();
|
||||
|
||||
return (royalty.receiver, royaltyAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev The denominator with which to interpret the fee set in {_setTokenRoyalty} and {_setDefaultRoyalty} as a
|
||||
* fraction of the sale price. Defaults to 10000 so fees are expressed in basis points, but may be customized by an
|
||||
* override.
|
||||
*/
|
||||
function _feeDenominator() internal pure virtual returns (uint96) {
|
||||
return 10000;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Sets the royalty information that all ids in this contract will default to.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - `receiver` cannot be the zero address.
|
||||
* - `feeNumerator` cannot be greater than the fee denominator.
|
||||
*/
|
||||
function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual {
|
||||
require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
|
||||
require(receiver != address(0), "ERC2981: invalid receiver");
|
||||
|
||||
_defaultRoyaltyInfo = RoyaltyInfo(receiver, feeNumerator);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Removes default royalty information.
|
||||
*/
|
||||
function _deleteDefaultRoyalty() internal virtual {
|
||||
delete _defaultRoyaltyInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Sets the royalty information for a specific token id, overriding the global default.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - `tokenId` must be already minted.
|
||||
* - `receiver` cannot be the zero address.
|
||||
* - `feeNumerator` cannot be greater than the fee denominator.
|
||||
*/
|
||||
function _setTokenRoyalty(
|
||||
uint256 tokenId,
|
||||
address receiver,
|
||||
uint96 feeNumerator
|
||||
) internal virtual {
|
||||
require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
|
||||
require(receiver != address(0), "ERC2981: Invalid parameters");
|
||||
|
||||
_tokenRoyaltyInfo[tokenId] = RoyaltyInfo(receiver, feeNumerator);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Resets royalty information for the token id back to the global default.
|
||||
*/
|
||||
function _resetTokenRoyalty(uint256 tokenId) internal virtual {
|
||||
delete _tokenRoyaltyInfo[tokenId];
|
||||
}
|
||||
}
|
||||
10
contracts/token/common/README.adoc
Normal file
10
contracts/token/common/README.adoc
Normal file
@ -0,0 +1,10 @@
|
||||
= Common (Tokens)
|
||||
|
||||
Functionality that is common to multiple token standards.
|
||||
|
||||
* {ERC2981}: NFT Royalties compatible with both ERC721 and ERC1155.
|
||||
** For ERC721 consider {ERC721Royalty} which clears the royalty information from storage on burn.
|
||||
|
||||
== Contracts
|
||||
|
||||
{{ERC2981}}
|
||||
Reference in New Issue
Block a user