Unverified Commit ecf0725d by GuiCz Committed by GitHub

Documentation/erc721 contracts (#2218)

* Adds / Updates documentation of ERC721 contract

* Improve ERC721Burnable documentation

* Fix typo

* Revert changes on ERC721 private functions

* Add documentation to the ERC721 contract's constructor

* Add IERC721Receiver & ERC721Holder documentations

* Add references to IERC721 functions

* Add references to IERC721Metadata/Receiver

* PR fixes

* Fixes to ERC721 documentation

* Add missing fixes

Co-authored-by: Nicolás Venturo <nicolas.venturo@gmail.com>
parent d3ef93a9
...@@ -87,6 +87,9 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable ...@@ -87,6 +87,9 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable
*/ */
bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63; bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63;
/**
* @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
*/
constructor (string memory name, string memory symbol) public { constructor (string memory name, string memory symbol) public {
_name = name; _name = name;
_symbol = symbol; _symbol = symbol;
...@@ -98,9 +101,7 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable ...@@ -98,9 +101,7 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable
} }
/** /**
* @dev Gets the balance of the specified address. * @dev See {IERC721-balanceOf}.
* @param owner address to query the balance of
* @return uint256 representing the amount owned by the passed address
*/ */
function balanceOf(address owner) public view override returns (uint256) { function balanceOf(address owner) public view override returns (uint256) {
require(owner != address(0), "ERC721: balance query for the zero address"); require(owner != address(0), "ERC721: balance query for the zero address");
...@@ -109,60 +110,28 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable ...@@ -109,60 +110,28 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable
} }
/** /**
* @dev Gets the owner of the specified token ID. * @dev See {IERC721-ownerOf}.
* @param tokenId uint256 ID of the token to query the owner of
* @return address currently marked as the owner of the given token ID
*/ */
function ownerOf(uint256 tokenId) public view override returns (address) { function ownerOf(uint256 tokenId) public view override returns (address) {
return _tokenOwners.get(tokenId, "ERC721: owner query for nonexistent token"); return _tokenOwners.get(tokenId, "ERC721: owner query for nonexistent token");
} }
/** /**
* @dev Gets the token name. * @dev See {IERC721Metadata-name}.
* @return string representing the token name
*/ */
function name() public view override returns (string memory) { function name() public view override returns (string memory) {
return _name; return _name;
} }
/** /**
* @dev Gets the token symbol. * @dev See {IERC721Metadata-symbol}.
* @return string representing the token symbol
*/ */
function symbol() public view override returns (string memory) { function symbol() public view override returns (string memory) {
return _symbol; return _symbol;
} }
/** /**
* @dev Returns the URI for a given token ID. May return an empty string. * @dev See {IERC721Metadata-tokenURI}.
*
* If a base URI is set (via {_setBaseURI}), it is added as a prefix to the
* token's own URI (via {_setTokenURI}).
*
* If there is a base URI but no token URI, the token's ID will be used as
* its URI when appending it to the base URI. This pattern for autogenerated
* token URIs can lead to large gas savings.
*
* .Examples
* |===
* |`_setBaseURI()` |`_setTokenURI()` |`tokenURI()`
* | ""
* | ""
* | ""
* | ""
* | "token.uri/123"
* | "token.uri/123"
* | "token.uri/"
* | "123"
* | "token.uri/123"
* | "token.uri/"
* | ""
* | "token.uri/<tokenId>"
* |===
*
* Requirements:
*
* - `tokenId` must exist.
*/ */
function tokenURI(uint256 tokenId) public view override returns (string memory) { function tokenURI(uint256 tokenId) public view override returns (string memory) {
require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token"); require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
...@@ -191,18 +160,14 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable ...@@ -191,18 +160,14 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable
} }
/** /**
* @dev Gets the token ID at a given index of the tokens list of the requested owner. * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
* @param owner address owning the tokens list to be accessed
* @param index uint256 representing the index to be accessed of the requested tokens list
* @return uint256 token ID at the given index of the tokens list owned by the requested address
*/ */
function tokenOfOwnerByIndex(address owner, uint256 index) public view override returns (uint256) { function tokenOfOwnerByIndex(address owner, uint256 index) public view override returns (uint256) {
return _holderTokens[owner].at(index); return _holderTokens[owner].at(index);
} }
/** /**
* @dev Gets the total amount of tokens stored by the contract. * @dev See {IERC721Enumerable-totalSupply}.
* @return uint256 representing the total amount of tokens
*/ */
function totalSupply() public view override returns (uint256) { function totalSupply() public view override returns (uint256) {
// _tokenOwners are indexed by tokenIds, so .length() returns the number of tokenIds // _tokenOwners are indexed by tokenIds, so .length() returns the number of tokenIds
...@@ -210,10 +175,7 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable ...@@ -210,10 +175,7 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable
} }
/** /**
* @dev Gets the token ID at a given index of all the tokens in this contract * @dev See {IERC721Enumerable-tokenByIndex}.
* Reverts if the index is greater or equal to the total number of tokens.
* @param index uint256 representing the index to be accessed of the tokens list
* @return uint256 token ID at the given index of the tokens list
*/ */
function tokenByIndex(uint256 index) public view override returns (uint256) { function tokenByIndex(uint256 index) public view override returns (uint256) {
(uint256 tokenId, ) = _tokenOwners.at(index); (uint256 tokenId, ) = _tokenOwners.at(index);
...@@ -221,12 +183,7 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable ...@@ -221,12 +183,7 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable
} }
/** /**
* @dev Approves another address to transfer the given token ID * @dev See {IERC721-approve}.
* The zero address indicates there is no approved address.
* There can only be one approved address per token at a given time.
* Can only be called by the token owner or an approved operator.
* @param to address to be approved for the given token ID
* @param tokenId uint256 ID of the token to be approved
*/ */
function approve(address to, uint256 tokenId) public virtual override { function approve(address to, uint256 tokenId) public virtual override {
address owner = ownerOf(tokenId); address owner = ownerOf(tokenId);
...@@ -240,10 +197,7 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable ...@@ -240,10 +197,7 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable
} }
/** /**
* @dev Gets the approved address for a token ID, or zero if no address set * @dev See {IERC721-getApproved}.
* Reverts if the token ID does not exist.
* @param tokenId uint256 ID of the token to query the approval of
* @return address currently approved for the given token ID
*/ */
function getApproved(uint256 tokenId) public view override returns (address) { function getApproved(uint256 tokenId) public view override returns (address) {
require(_exists(tokenId), "ERC721: approved query for nonexistent token"); require(_exists(tokenId), "ERC721: approved query for nonexistent token");
...@@ -252,10 +206,7 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable ...@@ -252,10 +206,7 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable
} }
/** /**
* @dev Sets or unsets the approval of a given operator * @dev See {IERC721-setApprovalForAll}.
* An operator is allowed to transfer all tokens of the sender on their behalf.
* @param operator operator address to set the approval
* @param approved representing the status of the approval to be set
*/ */
function setApprovalForAll(address operator, bool approved) public virtual override { function setApprovalForAll(address operator, bool approved) public virtual override {
require(operator != _msgSender(), "ERC721: approve to caller"); require(operator != _msgSender(), "ERC721: approve to caller");
...@@ -265,22 +216,14 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable ...@@ -265,22 +216,14 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable
} }
/** /**
* @dev Tells whether an operator is approved by a given owner. * @dev See {IERC721-isApprovedForAll}.
* @param owner owner address which you want to query the approval of
* @param operator operator address which you want to query the approval of
* @return bool whether the given operator is approved by the given owner
*/ */
function isApprovedForAll(address owner, address operator) public view override returns (bool) { function isApprovedForAll(address owner, address operator) public view override returns (bool) {
return _operatorApprovals[owner][operator]; return _operatorApprovals[owner][operator];
} }
/** /**
* @dev Transfers the ownership of a given token ID to another address. * @dev See {IERC721-transferFrom}.
* Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
* Requires the msg.sender to be the owner, approved, or operator.
* @param from current owner of the token
* @param to address to receive the ownership of the given token ID
* @param tokenId uint256 ID of the token to be transferred
*/ */
function transferFrom(address from, address to, uint256 tokenId) public virtual override { function transferFrom(address from, address to, uint256 tokenId) public virtual override {
//solhint-disable-next-line max-line-length //solhint-disable-next-line max-line-length
...@@ -290,31 +233,14 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable ...@@ -290,31 +233,14 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable
} }
/** /**
* @dev Safely transfers the ownership of a given token ID to another address * @dev See {IERC721-safeTransferFrom}.
* If the target address is a contract, it must implement {IERC721Receiver-onERC721Received},
* which is called upon a safe transfer, and return the magic value
* `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
* the transfer is reverted.
* Requires the msg.sender to be the owner, approved, or operator
* @param from current owner of the token
* @param to address to receive the ownership of the given token ID
* @param tokenId uint256 ID of the token to be transferred
*/ */
function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override { function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
safeTransferFrom(from, to, tokenId, ""); safeTransferFrom(from, to, tokenId, "");
} }
/** /**
* @dev Safely transfers the ownership of a given token ID to another address * @dev See {IERC721-safeTransferFrom}.
* If the target address is a contract, it must implement {IERC721Receiver-onERC721Received},
* which is called upon a safe transfer, and return the magic value
* `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
* the transfer is reverted.
* Requires the _msgSender() to be the owner, approved, or operator
* @param from current owner of the token
* @param to address to receive the ownership of the given token ID
* @param tokenId uint256 ID of the token to be transferred
* @param _data bytes data to send along with a safe transfer check
*/ */
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public virtual override { function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public virtual override {
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
...@@ -322,16 +248,22 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable ...@@ -322,16 +248,22 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable
} }
/** /**
* @dev Safely transfers the ownership of a given token ID to another address * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* If the target address is a contract, it must implement `onERC721Received`, * are aware of the ERC721 protocol to prevent tokens from being forever locked.
* which is called upon a safe transfer, and return the magic value *
* `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise, * `_data` is additional data, it has no specified format and it is sent in call to `to`.
* the transfer is reverted. *
* Requires the msg.sender to be the owner, approved, or operator * This internal function is equivalent to {safeTransfertFrom}, and can be used to e.g.
* @param from current owner of the token * implement alternative mecanisms to perform token transfer, such as signature-based.
* @param to address to receive the ownership of the given token ID *
* @param tokenId uint256 ID of the token to be transferred * Requirements:
* @param _data bytes data to send along with a safe transfer check *
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/ */
function _safeTransfer(address from, address to, uint256 tokenId, bytes memory _data) internal virtual { function _safeTransfer(address from, address to, uint256 tokenId, bytes memory _data) internal virtual {
_transfer(from, to, tokenId); _transfer(from, to, tokenId);
...@@ -339,20 +271,23 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable ...@@ -339,20 +271,23 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable
} }
/** /**
* @dev Returns whether the specified token exists. * @dev Returns whether `tokenId` exists.
* @param tokenId uint256 ID of the token to query the existence of *
* @return bool whether the token exists * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
*
* Tokens start existing when they are minted (`_mint`),
* and stop existing when they are burned (`_burn`).
*/ */
function _exists(uint256 tokenId) internal view returns (bool) { function _exists(uint256 tokenId) internal view returns (bool) {
return _tokenOwners.contains(tokenId); return _tokenOwners.contains(tokenId);
} }
/** /**
* @dev Returns whether the given spender can transfer a given token ID. * @dev Returns whether `spender` is allowed to manage `tokenId`.
* @param spender address of the spender to query *
* @param tokenId uint256 ID of the token to be transferred * Requirements:
* @return bool whether the msg.sender is approved for the given token ID, *
* is an operator of the owner, or is the owner of the token * - `tokenId` must exist.
*/ */
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) { function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
require(_exists(tokenId), "ERC721: operator query for nonexistent token"); require(_exists(tokenId), "ERC721: operator query for nonexistent token");
...@@ -361,29 +296,21 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable ...@@ -361,29 +296,21 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable
} }
/** /**
* @dev Internal function to safely mint a new token. * @dev Safely mints `tokenId` and transfers it to `to`.
* Reverts if the given token ID already exists. *
* If the target address is a contract, it must implement `onERC721Received`, * Requirements:
* which is called upon a safe transfer, and return the magic value d*
* `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise, * - `tokenId` must not exist.
* the transfer is reverted. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
* @param to The address that will own the minted token *
* @param tokenId uint256 ID of the token to be minted * Emits a {Transfer} event.
*/ */
function _safeMint(address to, uint256 tokenId) internal virtual { function _safeMint(address to, uint256 tokenId) internal virtual {
_safeMint(to, tokenId, ""); _safeMint(to, tokenId, "");
} }
/** /**
* @dev Internal function to safely mint a new token. * @dev Same as {_safeMint-address-uint256-}, with an additional `data` parameter which is forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
* Reverts if the given token ID already exists.
* If the target address is a contract, it must implement `onERC721Received`,
* which is called upon a safe transfer, and return the magic value
* `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
* the transfer is reverted.
* @param to The address that will own the minted token
* @param tokenId uint256 ID of the token to be minted
* @param _data bytes data to send along with a safe transfer check
*/ */
function _safeMint(address to, uint256 tokenId, bytes memory _data) internal virtual { function _safeMint(address to, uint256 tokenId, bytes memory _data) internal virtual {
_mint(to, tokenId); _mint(to, tokenId);
...@@ -391,10 +318,16 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable ...@@ -391,10 +318,16 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable
} }
/** /**
* @dev Internal function to mint a new token. * @dev Mints `tokenId` and transfers it to `to`.
* Reverts if the given token ID already exists. *
* @param to The address that will own the minted token * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
* @param tokenId uint256 ID of the token to be minted *
* Requirements:
*
* - `tokenId` must not exist.
* - `to` cannot be the zero address.
*
* Emits a {Transfer} event.
*/ */
function _mint(address to, uint256 tokenId) internal virtual { function _mint(address to, uint256 tokenId) internal virtual {
require(to != address(0), "ERC721: mint to the zero address"); require(to != address(0), "ERC721: mint to the zero address");
...@@ -410,9 +343,14 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable ...@@ -410,9 +343,14 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable
} }
/** /**
* @dev Internal function to burn a specific token. * @dev Destroys `tokenId`.
* Reverts if the token does not exist. * The approval is cleared when the token is burned.
* @param tokenId uint256 ID of the token being burned *
* Requirements:
*
* - `tokenId` must exist.
*
* Emits a {Transfer} event.
*/ */
function _burn(uint256 tokenId) internal virtual { function _burn(uint256 tokenId) internal virtual {
address owner = ownerOf(tokenId); address owner = ownerOf(tokenId);
...@@ -435,11 +373,15 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable ...@@ -435,11 +373,15 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable
} }
/** /**
* @dev Internal function to transfer ownership of a given token ID to another address. * @dev Transfers `tokenId` from `from` to `to`.
* As opposed to {transferFrom}, this imposes no restrictions on msg.sender. * As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
* @param from current owner of the token *
* @param to address to receive the ownership of the given token ID * Requirements:
* @param tokenId uint256 ID of the token to be transferred *
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
*
* Emits a {Transfer} event.
*/ */
function _transfer(address from, address to, uint256 tokenId) internal virtual { function _transfer(address from, address to, uint256 tokenId) internal virtual {
require(ownerOf(tokenId) == from, "ERC721: transfer of token that is not own"); require(ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
...@@ -459,13 +401,11 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable ...@@ -459,13 +401,11 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable
} }
/** /**
* @dev Internal function to set the token URI for a given token. * @dev Sets `_tokenURI` as the tokenURI of `tokenId`.
* *
* Reverts if the token ID does not exist. * Requirements:
* *
* TIP: If all token IDs share a prefix (for example, if your URIs look like * - `tokenId` must exist.
* `https://api.myproject.com/token/<id>`), use {_setBaseURI} to store
* it and save gas.
*/ */
function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual { function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token"); require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
...@@ -532,11 +472,12 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable ...@@ -532,11 +472,12 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable
* *
* Calling conditions: * Calling conditions:
* *
* - when `from` and `to` are both non-zero, ``from``'s `tokenId` will be * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
* transferred to `to`. * transferred to `to`.
* - when `from` is zero, `tokenId` will be minted for `to`. * - When `from` is zero, `tokenId` will be minted for `to`.
* - when `to` is zero, ``from``'s `tokenId` will be burned. * - When `to` is zero, ``from``'s `tokenId` will be burned.
* - `from` and `to` are never both zero. * - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* *
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/ */
......
...@@ -11,8 +11,11 @@ import "./ERC721.sol"; ...@@ -11,8 +11,11 @@ import "./ERC721.sol";
*/ */
abstract contract ERC721Burnable is Context, ERC721 { abstract contract ERC721Burnable is Context, ERC721 {
/** /**
* @dev Burns a specific ERC721 token. * @dev Burns `tokenId`. See {ERC721-_burn}.
* @param tokenId uint256 id of the ERC721 token to be burned. *
* Requirements:
*
* - The caller must own `tokenId` or be an approved operator.
*/ */
function burn(uint256 tokenId) public virtual { function burn(uint256 tokenId) public virtual {
//solhint-disable-next-line max-line-length //solhint-disable-next-line max-line-length
......
...@@ -4,7 +4,19 @@ pragma solidity ^0.6.0; ...@@ -4,7 +4,19 @@ pragma solidity ^0.6.0;
import "./IERC721Receiver.sol"; import "./IERC721Receiver.sol";
/**
* @dev Implementation of the {IERC721Receiver} interface.
*
* Accepts all token transfers.
* Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}.
*/
contract ERC721Holder is IERC721Receiver { contract ERC721Holder is IERC721Receiver {
/**
* @dev See {IERC721Receiver-onERC721Received}.
*
* Always returns `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(address, address, uint256, bytes memory) public virtual override returns (bytes4) { function onERC721Received(address, address, uint256, bytes memory) public virtual override returns (bytes4) {
return this.onERC721Received.selector; return this.onERC721Received.selector;
} }
......
...@@ -43,7 +43,8 @@ interface IERC721 is IERC165 { ...@@ -43,7 +43,8 @@ interface IERC721 is IERC165 {
* *
* Requirements: * Requirements:
* *
* - `from`, `to` cannot be zero. * - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`. * - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}. * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
...@@ -59,7 +60,8 @@ interface IERC721 is IERC165 { ...@@ -59,7 +60,8 @@ interface IERC721 is IERC165 {
* *
* Requirements: * Requirements:
* *
* - `from`, `to` cannot be zero. * - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`. * - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* *
...@@ -115,7 +117,8 @@ interface IERC721 is IERC165 { ...@@ -115,7 +117,8 @@ interface IERC721 is IERC165 {
* *
* Requirements: * Requirements:
* *
* - `from`, `to` cannot be zero. * - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`. * - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
......
...@@ -9,18 +9,13 @@ pragma solidity ^0.6.0; ...@@ -9,18 +9,13 @@ pragma solidity ^0.6.0;
*/ */
interface IERC721Receiver { interface IERC721Receiver {
/** /**
* @notice Handle the receipt of an NFT * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* @dev The ERC721 smart contract calls this function on the recipient * by `operator` from `from`, this function is called.
* after a {IERC721-safeTransferFrom}. This function MUST return the function selector, *
* otherwise the caller will revert the transaction. The selector to be * It must return its Solidity selector to confirm the token transfer.
* returned can be obtained as `this.onERC721Received.selector`. This * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
* function MAY throw to revert and reject the transfer. *
* Note: the ERC721 contract address is always the message sender. * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
* @param operator The address which called `safeTransferFrom` function
* @param from The address which previously owned the token
* @param tokenId The NFT identifier which is being transferred
* @param data Additional data with no specified format
* @return bytes4 `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
*/ */
function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data)
external returns (bytes4); external returns (bytes4);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment