Skip to content

Commit

Permalink
Merge branch 'master' into feature/SignedMath
Browse files Browse the repository at this point in the history
  • Loading branch information
Amxx authored Jan 10, 2022
2 parents 4299249 + 8dd744f commit 9241143
Show file tree
Hide file tree
Showing 12 changed files with 432 additions and 18 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased

* `ERC2891`: add implementation of the royalty standard, and the respective extensions for `ERC721` and `ERC1155`. ([#3012](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3012))
* `GovernorTimelockControl`: improve the `state()` function to have it reflect cases where a proposal has been canceled directly on the timelock. ([#2977](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2977))
* `Math`: add a `abs(int256)` method that returns the unsigned absolute value of a signed value. ([#2984](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2984))
* Preset contracts are now deprecated in favor of [Contracts Wizard](https://wizard.openzeppelin.com). ([#2986](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2986))
Expand All @@ -11,8 +12,8 @@
* `Votes`: Added a base contract for vote tracking with delegation. ([#2944](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2944))
* `ERC721Votes`: Added an extension of ERC721 enabled with vote tracking and delegation. ([#2944](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2944))
* `ERC2771Context`: use immutable storage to store the forwarder address, no longer an issue since Solidity >=0.8.8 allows reading immutable variables in the constructor. ([#2917](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2917))
* `Base64`: add a library to parse bytes into base64 strings using `encode(bytes memory)` function, and provide examples to show how to use to build URL-safe `tokenURIs` ([#2884](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/#2884))
* `ERC20`: reduce allowance before triggering transfer.
* `Base64`: add a library to parse bytes into base64 strings using `encode(bytes memory)` function, and provide examples to show how to use to build URL-safe `tokenURIs`. ([#2884](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/#2884))
* `ERC20`: reduce allowance before triggering transfer. ([#3056](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/#3056))
* `SignedMath`: a new signed version of the Math library with `max`, `min`, `average`and `ceilDiv`. ([#2686](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2686))

## 4.4.1 (2021-12-14)
Expand Down
14 changes: 8 additions & 6 deletions contracts/interfaces/IERC2981.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ pragma solidity ^0.8.0;
import "./IERC165.sol";

/**
* @dev Interface for the NFT Royalty Standard
* @dev Interface for the NFT Royalty Standard.
*
* A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
* support for royalty payments across all NFT marketplaces and ecosystem participants.
*
* _Available since v4.5._
*/
interface IERC2981 is IERC165 {
/**
* @dev Called with the sale price to determine how much royalty is owed and to whom.
* @param tokenId - the NFT asset queried for royalty information
* @param salePrice - the sale price of the NFT asset specified by `tokenId`
* @return receiver - address of who should be sent the royalty payment
* @return royaltyAmount - the royalty payment amount for `salePrice`
* @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
* exchange. The royalty amount is denominated and should be payed in that same unit of exchange.
*/
function royaltyInfo(uint256 tokenId, uint256 salePrice)
external
Expand Down
33 changes: 33 additions & 0 deletions contracts/mocks/ERC721RoyaltyMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../token/ERC721/extensions/ERC721Royalty.sol";

contract ERC721RoyaltyMock is ERC721Royalty {
constructor(string memory name, string memory symbol) ERC721(name, symbol) {}

function setTokenRoyalty(
uint256 tokenId,
address recipient,
uint96 fraction
) public {
_setTokenRoyalty(tokenId, recipient, fraction);
}

function setDefaultRoyalty(address recipient, uint96 fraction) public {
_setDefaultRoyalty(recipient, fraction);
}

function mint(address to, uint256 tokenId) public {
_mint(to, tokenId);
}

function burn(uint256 tokenId) public {
_burn(tokenId);
}

function deleteDefaultRoyalty() public {
_deleteDefaultRoyalty();
}
}
26 changes: 19 additions & 7 deletions contracts/token/ERC721/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,28 @@ This set of interfaces, contracts, and utilities are all related to the https://

TIP: For a walk through on how to create an ERC721 token read our xref:ROOT:erc721.adoc[ERC721 guide].

The EIP consists of three interfaces, found here as {IERC721}, {IERC721Metadata}, and {IERC721Enumerable}. Only the first one is required in a contract to be ERC721 compliant. The core interface and the metadata extension are both implemented in {ERC721}. The enumerable extension is provided separately in {ERC721Enumerable}.
The EIP specifies four interfaces:

Additionally, {IERC721Receiver} can be used to prevent tokens from becoming forever locked in contracts. Imagine sending an in-game item to an exchange address that can't send it back!. When using <<IERC721-safeTransferFrom,`safeTransferFrom`>>, the token contract checks to see that the receiver is an {IERC721Receiver}, which implies that it knows how to handle {ERC721} tokens. If you're writing a contract that needs to receive {ERC721} tokens, you'll want to include this interface.
* {IERC721}: Core functionality required in all compliant implementation.
* {IERC721Metadata}: Optional extension that adds name, symbol, and token URI, almost always included.
* {IERC721Enumerable}: Optional extension that allows enumerating the tokens on chain, often not included since it requires large gas overhead.
* {IERC721Receiver}: An interface that must be implemented by contracts if they want to accept tokens through `safeTransferFrom`.
Additionally there are multiple custom extensions, including:
OpenZeppelin Contracts provides implementations of all four interfaces:

* designation of addresses that can pause token transfers for all users ({ERC721Pausable}).
* destruction of own tokens ({ERC721Burnable}).
* support for voting and vote delegation ({ERC721Votes})
* {ERC721}: The core and metadata extensions, with a base URI mechanism.
* {ERC721Enumerable}: The enumerable extension.
* {ERC721Holder}: A bare bones implementation of the receiver interface.
NOTE: This core set of contracts is designed to be unopinionated, allowing developers to access the internal functions in ERC721 (such as <<ERC721-_mint-address-uint256-,`_mint`>>) and expose them as external functions in the way they prefer. On the other hand, xref:ROOT:erc721.adoc#Presets[ERC721 Presets] (such as {ERC721PresetMinterPauserAutoId}) are designed using opinionated patterns to provide developers with ready to use, deployable contracts.
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.
NOTE: This core set of contracts is designed to be unopinionated, allowing developers to access the internal functions in ERC721 (such as <<ERC721-_mint-address-uint256-,`_mint`>>) and expose them as external functions in the way they prefer. On the other hand, xref:ROOT:erc721.adoc#Presets[ERC721 Presets] (such as {ERC721PresetMinterPauserAutoId}) are designed using opinionated patterns to provide developers with ready to use, deployable contracts.

== Core

Expand All @@ -44,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.
Expand Down
38 changes: 38 additions & 0 deletions contracts/token/ERC721/extensions/ERC721Royalty.sol
Original file line number Diff line number Diff line change
@@ -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);
}
}
2 changes: 1 addition & 1 deletion contracts/token/ERC721/extensions/IERC721Enumerable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ interface IERC721Enumerable is IERC721 {
* @dev Returns a token ID owned by `owner` at a given `index` of its token list.
* Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId);
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);

/**
* @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
Expand Down
112 changes: 112 additions & 0 deletions contracts/token/common/ERC2981.sol
Original file line number Diff line number Diff line change
@@ -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 changes: 10 additions & 0 deletions contracts/token/common/README.adoc
Original file line number Diff line number Diff line change
@@ -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}}
7 changes: 5 additions & 2 deletions scripts/gen-nav.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ const files = proc.execFileSync(
console.log('.API');

function getPageTitle (directory) {
if (directory === 'metatx') {
switch (directory) {
case 'metatx':
return 'Meta Transactions';
} else {
case 'common':
return 'Common (Tokens)';
default:
return startCase(directory);
}
}
Expand Down
40 changes: 40 additions & 0 deletions test/token/ERC721/extensions/ERC721Royalty.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
const { BN, constants } = require('@openzeppelin/test-helpers');
const ERC721RoyaltyMock = artifacts.require('ERC721RoyaltyMock');
const { ZERO_ADDRESS } = constants;

const { shouldBehaveLikeERC2981 } = require('../../common/ERC2981.behavior');

contract('ERC721Royalty', function (accounts) {
const [ account1, account2 ] = accounts;
const tokenId1 = new BN('1');
const tokenId2 = new BN('2');
const royalty = new BN('200');
const salePrice = new BN('1000');

beforeEach(async function () {
this.token = await ERC721RoyaltyMock.new('My Token', 'TKN');

await this.token.mint(account1, tokenId1);
await this.token.mint(account1, tokenId2);
this.account1 = account1;
this.account2 = account2;
this.tokenId1 = tokenId1;
this.tokenId2 = tokenId2;
this.salePrice = salePrice;
});

describe('token specific functions', function () {
beforeEach(async function () {
await this.token.setTokenRoyalty(tokenId1, account1, royalty);
});

it('removes royalty information after burn', async function () {
await this.token.burn(tokenId1);
const tokenInfo = await this.token.royaltyInfo(tokenId1, salePrice);

expect(tokenInfo[0]).to.be.equal(ZERO_ADDRESS);
expect(tokenInfo[1]).to.be.bignumber.equal(new BN('0'));
});
});
shouldBehaveLikeERC2981();
});
Loading

0 comments on commit 9241143

Please sign in to comment.