Squashed commit of the following: commit 06243c3e8e86074ff8e9e3f22b7829a2c315d707 Merge: 991882ec 99373558 Author: Francisco Giordano <frangio.1@gmail.com> Date: Thu May 23 18:15:37 2019 -0300 Merge branch 'api-docs' into api-docs-merge commit 991882eca5bb8a3391995154bfb9d53d8a69cb4f Author: Francisco Giordano <frangio.1@gmail.com> Date: Thu May 23 18:08:02 2019 -0300 manually apply docs changes and renamings commit fa1f6e97dd67a76c3cd828d0a5e19b4ac6c37acb Author: Francisco Giordano <frangio.1@gmail.com> Date: Thu May 23 17:36:03 2019 -0300 move functions to new order commit 99373558e3af4905d29bc6f3f542ba93d28a24ab Author: Francisco Giordano <frangio.1@gmail.com> Date: Thu May 23 16:23:40 2019 -0300 add missing docs links commit d180d6c36a6f5460e85473ee5a18992d1449a6ff Author: Francisco Giordano <frangio.1@gmail.com> Date: Thu May 23 16:14:12 2019 -0300 update solidity-docgen dependency fixes uri encoded links commit faab0e50da91cd2f0a409e3ad32e2db127ad319a Author: Francisco Giordano <frangio.1@gmail.com> Date: Thu May 23 16:05:03 2019 -0300 update openzeppelin-docsite and solidity-docgen dependencies add visibility specifiers commit ef305268bb2735e488e35d16819a4b432b3a35e3 Author: Nicolás Venturo <nicolas.venturo@gmail.com> Date: Thu May 23 15:36:45 2019 -0300 Fix guide links. commit 339b20dbfa2d5f6ea02e63c2f3fdcba0fe879c3c Author: Nicolás Venturo <nicolas.venturo@gmail.com> Date: Thu May 23 13:37:51 2019 -0300 Fix typos. commit 6c7b97460578b9eea90b53c280454e361f8f0052 Author: Francisco Giordano <frangio.1@gmail.com> Date: Thu May 23 15:26:29 2019 -0300 fix utilities guide links commit 0e7692a8fd8516a11becc4121d77d792439600b1 Author: Francisco Giordano <frangio.1@gmail.com> Date: Thu May 23 15:23:19 2019 -0300 update solidity-docgen dependency commit ebb8a8651516ece21736c6c3b2577eb1b3487651 Author: Francisco Giordano <frangio.1@gmail.com> Date: Thu May 23 15:15:01 2019 -0300 fix utilities guide links commit 5ec47d62785e1d6e5f8e91edca58f2dc7f87d7a3 Author: Francisco Giordano <frangio.1@gmail.com> Date: Thu May 23 15:14:49 2019 -0300 fix escrow docs ordering commit cdcdc909b16f219a9a3272036b6a8f21e34b48ef Author: Francisco Giordano <frangio.1@gmail.com> Date: Thu May 23 13:35:07 2019 -0300 add wip notice commit 987e2951ae93211c8c70c8288e30573555c57830 Author: Francisco Giordano <frangio.1@gmail.com> Date: Thu May 23 13:09:35 2019 -0300 update openzeppelin-docsite dependency fixes links to old versions commit b00d22c0affac2e2108df8b773dfa1706afcb44e Author: Francisco Giordano <frangio.1@gmail.com> Date: Thu May 23 13:09:28 2019 -0300 fix guide links commit f112a9400c5e5ad495c8e0fdb972e26987b34244 Author: Francisco Giordano <frangio.1@gmail.com> Date: Wed May 22 20:42:37 2019 -0300 update docsite commit 68aacdd56a29e35a84f6732f9293612bbcaf7520 Author: Nicolás Venturo <nicolas.venturo@gmail.com> Date: Wed May 22 20:00:39 2019 -0300 ERC20Capped commit 4edce78bab2c6d140f3ea3f33db71e92ca4d8aaf Author: Nicolás Venturo <nicolas.venturo@gmail.com> Date: Wed May 22 19:52:30 2019 -0300 Unnecessary polish on token docs. commit 2a4c91cf49c2736dc09c1c03cf383911def1a1b2 Author: Francisco Giordano <frangio.1@gmail.com> Date: Wed May 22 19:20:05 2019 -0300 rename guides commit 61dd818ea76d4c170c4ab175c6be0d6067d21a29 Author: Nicolás Venturo <nicolas.venturo@gmail.com> Date: Wed May 22 17:04:09 2019 -0300 ERC1820 docs. commit 77b5f0353123b76358dc6d86bdc646c86c9b0bea Author: Nicolás Venturo <nicolas.venturo@gmail.com> Date: Wed May 22 16:17:34 2019 -0300 Introspection and ERC165. commit 76641a253b3b70279802c5134dd107532eea4b2c Author: Francisco Giordano <frangio.1@gmail.com> Date: Wed May 22 17:59:53 2019 -0300 update docgen commit 7be98bc3fbd3566231f943f01b9acb58567d755b Author: Francisco Giordano <frangio.1@gmail.com> Date: Wed May 22 17:23:50 2019 -0300 update solidity-docgen commit f7268e6e010f8ef9ac83df241a803f91efc08c0c Author: Francisco Giordano <frangio.1@gmail.com> Date: Wed May 22 16:58:31 2019 -0300 update docgen commit 2a8c7a378e8962a5baeb334b2492815f05075f98 Author: Nicolás Venturo <nicolas.venturo@gmail.com> Date: Wed May 22 14:36:35 2019 -0300 Util docs. commit 327ae8ff45a1a523c7591bf4996c4a9b52d7ec7a Author: Francisco Giordano <frangio.1@gmail.com> Date: Wed May 22 13:08:50 2019 -0300 add missing drafts commit 5e7f71335ac8423c0e363ae8c7ad9b2977f202f8 Author: Francisco Giordano <frangio.1@gmail.com> Date: Wed May 22 12:47:41 2019 -0300 tweak ierc20 docs commit cd0e86a0b712f74ffd406e072d4b1fbf4dd2c176 Author: Francisco Giordano <frangio.1@gmail.com> Date: Wed May 22 12:46:45 2019 -0300 add some erc721 docs commit e081184159417f71da14bc0fc110b7b11e29d75d Author: Francisco Giordano <frangio.1@gmail.com> Date: Wed May 22 12:41:46 2019 -0300 update docsite commit 0beb75784022419d47123c2a9fe7a5f1eb87f9b2 Author: Francisco Giordano <frangio.1@gmail.com> Date: Wed May 22 12:22:27 2019 -0300 correct drafts structure commit 2e94b287c7cead7a6c0603205670566461c31abb Author: Francisco Giordano <frangio.1@gmail.com> Date: Wed May 22 11:56:25 2019 -0300 fix docsite-start script commit 0fa4160484309d0851584fe57c0d81a3600977db Author: Francisco Giordano <frangio.1@gmail.com> Date: Wed May 22 11:47:44 2019 -0300 improve docsite start script (automatically watch docgen) commit 9d571897cc03bee92035964cf7a2cfeda1e2f690 Author: Francisco Giordano <frangio.1@gmail.com> Date: Wed May 22 11:30:37 2019 -0300 update solidity-docgen commit 82980f5aefbdfb8a9815a3b7b0e88e970b65dd5d Author: Francisco Giordano <frangio.1@gmail.com> Date: Tue May 21 19:15:13 2019 -0300 edit docs for Secondary commit 00d7a005b0530bee730b77a1b69a95f1b4ffe315 Author: Francisco Giordano <frangio.1@gmail.com> Date: Tue May 21 19:15:13 2019 -0300 edit docs for ownable commit b0c4c2bdf83eca5d4a71792daac603236733d46e Author: Francisco Giordano <frangio.1@gmail.com> Date: Tue May 21 18:27:13 2019 -0300 change title of Math section commit deb788583f191780e55b7f673520eaf13a5c7e23 Author: Francisco Giordano <frangio.1@gmail.com> Date: Tue May 21 18:26:59 2019 -0300 capitalization commit f2bcf85d343ea4a0739fe22a77b1e22c2296ddea Author: Francisco Giordano <frangio.1@gmail.com> Date: Tue May 21 18:26:06 2019 -0300 edit docs for Pausable commit 73ba0cf43dbb44c39c1bf2ee63ef9fe558faa919 Author: Nicolás Venturo <nicolas.venturo@gmail.com> Date: Sat May 18 19:08:06 2019 -0300 Crypto docs. commit 9d6fc6223f51cf2321b2e3217c512579654c3917 Merge: 7e21f8f7 9f1cec12 Author: Nicolás Venturo <nicolas.venturo@gmail.com> Date: Fri May 17 17:23:15 2019 -0300 Merge branch 'api-docs-777' into api-docs commit 9f1cec12e3351fb1b5fc0b59f5ded39928064a56 Author: Nicolás Venturo <nicolas.venturo@gmail.com> Date: Fri May 17 17:22:54 2019 -0300 ERC777 done. commit 7e21f8f7b6982d2f92df302cdf6ec62522d8ffff Author: Francisco Giordano <frangio.1@gmail.com> Date: Fri May 17 16:39:47 2019 -0300 add math docs commit f18d1f17023b6e5b42ae04fc38aa1170e6863864 Author: Nicolás Venturo <nicolas.venturo@gmail.com> Date: Thu May 16 20:01:46 2019 -0300 First draft of ERC777 docs. commit985c5d3053Author: Nicolás Venturo <nicolas.venturo@gmail.com> Date: Thu May 16 19:14:32 2019 -0300 Final draft for IERC777. commit bf53f133d987b67f938a329e6d659ba3483aab0b Author: Francisco Giordano <frangio.1@gmail.com> Date: Thu May 16 19:13:37 2019 -0300 more work on ERC20 api docs commitb7c250b7cbAuthor: Nicolás Venturo <nicolas.venturo@gmail.com> Date: Thu May 16 17:08:47 2019 -0300 Fix typo. commit197bbfbfc6Author: Nicolás Venturo <nicolas.venturo@gmail.com> Date: Thu May 16 17:05:14 2019 -0300 Initial draft of IERC777. commit 7dc3b55161c860437a8f13a2ce5808b1c3dd70a2 Author: Francisco Giordano <frangio.1@gmail.com> Date: Thu May 16 11:58:32 2019 -0300 add payment docs structure commitda16fc4480Author: Nicolás Venturo <nicolas.venturo@gmail.com> Date: Thu May 16 16:05:33 2019 -0300 Initial ERC777 docstrings. commit9f6a7e35bdAuthor: Francisco Giordano <frangio.1@gmail.com> Date: Wed May 15 22:13:17 2019 -0300 partial pass through ERC20 docs (cherry picked from commit2f9ae975c8)
300 lines
12 KiB
Solidity
300 lines
12 KiB
Solidity
pragma solidity ^0.5.0;
|
|
|
|
import "./IERC721.sol";
|
|
import "./IERC721Receiver.sol";
|
|
import "../../math/SafeMath.sol";
|
|
import "../../utils/Address.sol";
|
|
import "../../drafts/Counters.sol";
|
|
import "../../introspection/ERC165.sol";
|
|
|
|
/**
|
|
* @title ERC721 Non-Fungible Token Standard basic implementation
|
|
* @dev see https://eips.ethereum.org/EIPS/eip-721
|
|
*/
|
|
contract ERC721 is ERC165, IERC721 {
|
|
using SafeMath for uint256;
|
|
using Address for address;
|
|
using Counters for Counters.Counter;
|
|
|
|
// Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
|
|
// which can be also obtained as `IERC721Receiver(0).onERC721Received.selector`
|
|
bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
|
|
|
|
// Mapping from token ID to owner
|
|
mapping (uint256 => address) private _tokenOwner;
|
|
|
|
// Mapping from token ID to approved address
|
|
mapping (uint256 => address) private _tokenApprovals;
|
|
|
|
// Mapping from owner to number of owned token
|
|
mapping (address => Counters.Counter) private _ownedTokensCount;
|
|
|
|
// Mapping from owner to operator approvals
|
|
mapping (address => mapping (address => bool)) private _operatorApprovals;
|
|
|
|
/*
|
|
* bytes4(keccak256('balanceOf(address)')) == 0x70a08231
|
|
* bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e
|
|
* bytes4(keccak256('approve(address,uint256)')) == 0x095ea7b3
|
|
* bytes4(keccak256('getApproved(uint256)')) == 0x081812fc
|
|
* bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465
|
|
* bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c
|
|
* bytes4(keccak256('transferFrom(address,address,uint256)')) == 0x23b872dd
|
|
* bytes4(keccak256('safeTransferFrom(address,address,uint256)')) == 0x42842e0e
|
|
* bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) == 0xb88d4fde
|
|
*
|
|
* => 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^
|
|
* 0xa22cb465 ^ 0xe985e9c ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd
|
|
*/
|
|
bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
|
|
|
|
constructor () public {
|
|
// register the supported interfaces to conform to ERC721 via ERC165
|
|
_registerInterface(_INTERFACE_ID_ERC721);
|
|
}
|
|
|
|
/**
|
|
* @dev Gets the balance of the specified address.
|
|
* @param owner address to query the balance of
|
|
* @return uint256 representing the amount owned by the passed address
|
|
*/
|
|
function balanceOf(address owner) public view returns (uint256) {
|
|
require(owner != address(0), "ERC721: balance query for the zero address");
|
|
|
|
return _ownedTokensCount[owner].current();
|
|
}
|
|
|
|
/**
|
|
* @dev Gets the owner of the specified token ID.
|
|
* @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 returns (address) {
|
|
address owner = _tokenOwner[tokenId];
|
|
require(owner != address(0), "ERC721: owner query for nonexistent token");
|
|
|
|
return owner;
|
|
}
|
|
|
|
/**
|
|
* @dev Approves another address to transfer the given token ID
|
|
* 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 {
|
|
address owner = ownerOf(tokenId);
|
|
require(to != owner, "ERC721: approval to current owner");
|
|
|
|
require(msg.sender == owner || isApprovedForAll(owner, msg.sender),
|
|
"ERC721: approve caller is not owner nor approved for all"
|
|
);
|
|
|
|
_tokenApprovals[tokenId] = to;
|
|
emit Approval(owner, to, tokenId);
|
|
}
|
|
|
|
/**
|
|
* @dev Gets the approved address for a token ID, or zero if no address set
|
|
* 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 returns (address) {
|
|
require(_exists(tokenId), "ERC721: approved query for nonexistent token");
|
|
|
|
return _tokenApprovals[tokenId];
|
|
}
|
|
|
|
/**
|
|
* @dev Sets or unsets the approval of a given operator
|
|
* An operator is allowed to transfer all tokens of the sender on their behalf.
|
|
* @param to operator address to set the approval
|
|
* @param approved representing the status of the approval to be set
|
|
*/
|
|
function setApprovalForAll(address to, bool approved) public {
|
|
require(to != msg.sender, "ERC721: approve to caller");
|
|
|
|
_operatorApprovals[msg.sender][to] = approved;
|
|
emit ApprovalForAll(msg.sender, to, approved);
|
|
}
|
|
|
|
/**
|
|
* @dev Tells whether an operator is approved by a given owner.
|
|
* @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 returns (bool) {
|
|
return _operatorApprovals[owner][operator];
|
|
}
|
|
|
|
/**
|
|
* @dev Transfers the ownership of a given token ID to another address.
|
|
* 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 {
|
|
//solhint-disable-next-line max-line-length
|
|
require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721: transfer caller is not owner nor approved");
|
|
|
|
_transferFrom(from, to, tokenId);
|
|
}
|
|
|
|
/**
|
|
* @dev Safely transfers the ownership of a given token ID to another address
|
|
* 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.
|
|
* 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 {
|
|
safeTransferFrom(from, to, tokenId, "");
|
|
}
|
|
|
|
/**
|
|
* @dev Safely transfers the ownership of a given token ID to another address
|
|
* 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.
|
|
* 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
|
|
* @param _data bytes data to send along with a safe transfer check
|
|
*/
|
|
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public {
|
|
transferFrom(from, to, tokenId);
|
|
require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
|
|
}
|
|
|
|
/**
|
|
* @dev Returns whether the specified token exists.
|
|
* @param tokenId uint256 ID of the token to query the existence of
|
|
* @return bool whether the token exists
|
|
*/
|
|
function _exists(uint256 tokenId) internal view returns (bool) {
|
|
address owner = _tokenOwner[tokenId];
|
|
return owner != address(0);
|
|
}
|
|
|
|
/**
|
|
* @dev Returns whether the given spender can transfer a given token ID.
|
|
* @param spender address of the spender to query
|
|
* @param tokenId uint256 ID of the token to be transferred
|
|
* @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
|
|
*/
|
|
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
|
|
require(_exists(tokenId), "ERC721: operator query for nonexistent token");
|
|
address owner = ownerOf(tokenId);
|
|
return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
|
|
}
|
|
|
|
/**
|
|
* @dev Internal function to mint a new token.
|
|
* Reverts if the given token ID already exists.
|
|
* @param to The address that will own the minted token
|
|
* @param tokenId uint256 ID of the token to be minted
|
|
*/
|
|
function _mint(address to, uint256 tokenId) internal {
|
|
require(to != address(0), "ERC721: mint to the zero address");
|
|
require(!_exists(tokenId), "ERC721: token already minted");
|
|
|
|
_tokenOwner[tokenId] = to;
|
|
_ownedTokensCount[to].increment();
|
|
|
|
emit Transfer(address(0), to, tokenId);
|
|
}
|
|
|
|
/**
|
|
* @dev Internal function to burn a specific token.
|
|
* Reverts if the token does not exist.
|
|
* Deprecated, use _burn(uint256) instead.
|
|
* @param owner owner of the token to burn
|
|
* @param tokenId uint256 ID of the token being burned
|
|
*/
|
|
function _burn(address owner, uint256 tokenId) internal {
|
|
require(ownerOf(tokenId) == owner, "ERC721: burn of token that is not own");
|
|
|
|
_clearApproval(tokenId);
|
|
|
|
_ownedTokensCount[owner].decrement();
|
|
_tokenOwner[tokenId] = address(0);
|
|
|
|
emit Transfer(owner, address(0), tokenId);
|
|
}
|
|
|
|
/**
|
|
* @dev Internal function to burn a specific token.
|
|
* Reverts if the token does not exist.
|
|
* @param tokenId uint256 ID of the token being burned
|
|
*/
|
|
function _burn(uint256 tokenId) internal {
|
|
_burn(ownerOf(tokenId), tokenId);
|
|
}
|
|
|
|
/**
|
|
* @dev Internal function to transfer ownership of a given token ID to another address.
|
|
* 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
|
|
* @param tokenId uint256 ID of the token to be transferred
|
|
*/
|
|
function _transferFrom(address from, address to, uint256 tokenId) internal {
|
|
require(ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
|
|
require(to != address(0), "ERC721: transfer to the zero address");
|
|
|
|
_clearApproval(tokenId);
|
|
|
|
_ownedTokensCount[from].decrement();
|
|
_ownedTokensCount[to].increment();
|
|
|
|
_tokenOwner[tokenId] = to;
|
|
|
|
emit Transfer(from, to, tokenId);
|
|
}
|
|
|
|
/**
|
|
* @dev Internal function to invoke `onERC721Received` on a target address.
|
|
* The call is not executed if the target address is not a contract.
|
|
*
|
|
* This function is deprecated.
|
|
* @param from address representing the previous owner of the given token ID
|
|
* @param to target address that will receive the tokens
|
|
* @param tokenId uint256 ID of the token to be transferred
|
|
* @param _data bytes optional data to send along with the call
|
|
* @return bool whether the call correctly returned the expected magic value
|
|
*/
|
|
function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)
|
|
internal returns (bool)
|
|
{
|
|
if (!to.isContract()) {
|
|
return true;
|
|
}
|
|
|
|
bytes4 retval = IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, _data);
|
|
return (retval == _ERC721_RECEIVED);
|
|
}
|
|
|
|
/**
|
|
* @dev Private function to clear current approval of a given token ID.
|
|
* @param tokenId uint256 ID of the token to be transferred
|
|
*/
|
|
function _clearApproval(uint256 tokenId) private {
|
|
if (_tokenApprovals[tokenId] != address(0)) {
|
|
_tokenApprovals[tokenId] = address(0);
|
|
}
|
|
}
|
|
}
|