Improved ERC721 granularity (#1304)
* Split enumerable and metadata implementations. * Renamed ERC721Basic to ERC721, and ERC721 to ERC721Full. * Fixed linter errors.
This commit is contained in:
committed by
Francisco Giordano
parent
bafdcf0701
commit
4b33eaefa2
@ -1,139 +1,279 @@
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
import "./IERC721.sol";
|
||||
import "./ERC721Basic.sol";
|
||||
import "./IERC721Receiver.sol";
|
||||
import "../../math/SafeMath.sol";
|
||||
import "../../utils/Address.sol";
|
||||
import "../../introspection/ERC165.sol";
|
||||
|
||||
|
||||
/**
|
||||
* @title Full ERC721 Token
|
||||
* This implementation includes all the required and some optional functionality of the ERC721 standard
|
||||
* Moreover, it includes approve all functionality using operator terminology
|
||||
* @title ERC721 Non-Fungible Token Standard basic implementation
|
||||
* @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
|
||||
*/
|
||||
contract ERC721 is ERC165, ERC721Basic, IERC721 {
|
||||
contract ERC721 is ERC165, IERC721 {
|
||||
|
||||
// Token name
|
||||
string internal _name;
|
||||
using SafeMath for uint256;
|
||||
using Address for address;
|
||||
|
||||
// Token symbol
|
||||
string internal _symbol;
|
||||
// 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 owner to list of owned token IDs
|
||||
mapping(address => uint256[]) private _ownedTokens;
|
||||
// Mapping from token ID to owner
|
||||
mapping (uint256 => address) private _tokenOwner;
|
||||
|
||||
// Mapping from token ID to index of the owner tokens list
|
||||
mapping(uint256 => uint256) private _ownedTokensIndex;
|
||||
// Mapping from token ID to approved address
|
||||
mapping (uint256 => address) private _tokenApprovals;
|
||||
|
||||
// Array with all token ids, used for enumeration
|
||||
uint256[] private _allTokens;
|
||||
// Mapping from owner to number of owned token
|
||||
mapping (address => uint256) private _ownedTokensCount;
|
||||
|
||||
// Mapping from token id to position in the allTokens array
|
||||
mapping(uint256 => uint256) private _allTokensIndex;
|
||||
// Mapping from owner to operator approvals
|
||||
mapping (address => mapping (address => bool)) private _operatorApprovals;
|
||||
|
||||
// Optional mapping for token URIs
|
||||
mapping(uint256 => string) private _tokenURIs;
|
||||
|
||||
bytes4 private constant _InterfaceId_ERC721Enumerable = 0x780e9d63;
|
||||
/**
|
||||
* 0x780e9d63 ===
|
||||
* bytes4(keccak256('totalSupply()')) ^
|
||||
* bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) ^
|
||||
* bytes4(keccak256('tokenByIndex(uint256)'))
|
||||
bytes4 private constant _InterfaceId_ERC721 = 0x80ac58cd;
|
||||
/*
|
||||
* 0x80ac58cd ===
|
||||
* bytes4(keccak256('balanceOf(address)')) ^
|
||||
* bytes4(keccak256('ownerOf(uint256)')) ^
|
||||
* bytes4(keccak256('approve(address,uint256)')) ^
|
||||
* bytes4(keccak256('getApproved(uint256)')) ^
|
||||
* bytes4(keccak256('setApprovalForAll(address,bool)')) ^
|
||||
* bytes4(keccak256('isApprovedForAll(address,address)')) ^
|
||||
* bytes4(keccak256('transferFrom(address,address,uint256)')) ^
|
||||
* bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^
|
||||
* bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)'))
|
||||
*/
|
||||
|
||||
bytes4 private constant _InterfaceId_ERC721Metadata = 0x5b5e139f;
|
||||
/**
|
||||
* 0x5b5e139f ===
|
||||
* bytes4(keccak256('name()')) ^
|
||||
* bytes4(keccak256('symbol()')) ^
|
||||
* bytes4(keccak256('tokenURI(uint256)'))
|
||||
*/
|
||||
|
||||
/**
|
||||
* @dev Constructor function
|
||||
*/
|
||||
constructor(string name, string symbol) public {
|
||||
_name = name;
|
||||
_symbol = symbol;
|
||||
|
||||
constructor()
|
||||
public
|
||||
{
|
||||
// register the supported interfaces to conform to ERC721 via ERC165
|
||||
_registerInterface(_InterfaceId_ERC721Enumerable);
|
||||
_registerInterface(_InterfaceId_ERC721Metadata);
|
||||
_registerInterface(_InterfaceId_ERC721);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Gets the token name
|
||||
* @return string representing the token name
|
||||
* @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 name() external view returns (string) {
|
||||
return _name;
|
||||
function balanceOf(address owner) public view returns (uint256) {
|
||||
require(owner != address(0));
|
||||
return _ownedTokensCount[owner];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Gets the token symbol
|
||||
* @return string representing the token symbol
|
||||
* @dev Gets the owner of the specified token ID
|
||||
* @param tokenId uint256 ID of the token to query the owner of
|
||||
* @return owner address currently marked as the owner of the given token ID
|
||||
*/
|
||||
function symbol() external view returns (string) {
|
||||
return _symbol;
|
||||
function ownerOf(uint256 tokenId) public view returns (address) {
|
||||
address owner = _tokenOwner[tokenId];
|
||||
require(owner != address(0));
|
||||
return owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns an URI for a given token ID
|
||||
* Throws if the token ID does not exist. May return an empty string.
|
||||
* @param tokenId uint256 ID of the token to query
|
||||
* @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 tokenURI(uint256 tokenId) public view returns (string) {
|
||||
function approve(address to, uint256 tokenId) public {
|
||||
address owner = ownerOf(tokenId);
|
||||
require(to != owner);
|
||||
require(msg.sender == owner || isApprovedForAll(owner, msg.sender));
|
||||
|
||||
_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));
|
||||
return _tokenURIs[tokenId];
|
||||
return _tokenApprovals[tokenId];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Gets the token ID at a given index of the tokens list of the requested owner
|
||||
* @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
|
||||
* @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 tokenOfOwnerByIndex(
|
||||
function setApprovalForAll(address to, bool approved) public {
|
||||
require(to != msg.sender);
|
||||
_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,
|
||||
uint256 index
|
||||
address operator
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256)
|
||||
returns (bool)
|
||||
{
|
||||
require(index < balanceOf(owner));
|
||||
return _ownedTokens[owner][index];
|
||||
return _operatorApprovals[owner][operator];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Gets the total amount of tokens stored by the contract
|
||||
* @return uint256 representing the total amount of tokens
|
||||
*/
|
||||
function totalSupply() public view returns (uint256) {
|
||||
return _allTokens.length;
|
||||
* @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
|
||||
{
|
||||
require(_isApprovedOrOwner(msg.sender, tokenId));
|
||||
require(to != address(0));
|
||||
|
||||
_clearApproval(from, tokenId);
|
||||
_removeTokenFrom(from, tokenId);
|
||||
_addTokenTo(to, tokenId);
|
||||
|
||||
emit Transfer(from, to, tokenId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Gets the token ID at a given index of all the tokens in this contract
|
||||
* 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 returns (uint256) {
|
||||
require(index < totalSupply());
|
||||
return _allTokens[index];
|
||||
* @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
|
||||
{
|
||||
// solium-disable-next-line arg-overflow
|
||||
safeTransferFrom(from, to, tokenId, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Internal function to set the token URI for a given token
|
||||
* Reverts if the token ID does not exist
|
||||
* @param tokenId uint256 ID of the token to set its URI
|
||||
* @param uri string URI to assign
|
||||
* @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 _setTokenURI(uint256 tokenId, string uri) internal {
|
||||
require(_exists(tokenId));
|
||||
_tokenURIs[tokenId] = uri;
|
||||
function safeTransferFrom(
|
||||
address from,
|
||||
address to,
|
||||
uint256 tokenId,
|
||||
bytes _data
|
||||
)
|
||||
public
|
||||
{
|
||||
transferFrom(from, to, tokenId);
|
||||
// solium-disable-next-line arg-overflow
|
||||
require(_checkAndCallSafeTransfer(from, to, tokenId, _data));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns whether the specified token exists
|
||||
* @param tokenId uint256 ID of the token to query the existence of
|
||||
* @return 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)
|
||||
{
|
||||
address owner = ownerOf(tokenId);
|
||||
// Disable solium check because of
|
||||
// https://github.com/duaraghav8/Solium/issues/175
|
||||
// solium-disable-next-line operator-whitespace
|
||||
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 by the msg.sender
|
||||
*/
|
||||
function _mint(address to, uint256 tokenId) internal {
|
||||
require(to != address(0));
|
||||
_addTokenTo(to, tokenId);
|
||||
emit Transfer(address(0), to, 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 by the msg.sender
|
||||
*/
|
||||
function _burn(address owner, uint256 tokenId) internal {
|
||||
_clearApproval(owner, tokenId);
|
||||
_removeTokenFrom(owner, tokenId);
|
||||
emit Transfer(owner, address(0), tokenId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Internal function to clear current approval of a given token ID
|
||||
* Reverts if the given address is not indeed the owner of the token
|
||||
* @param owner owner of the token
|
||||
* @param tokenId uint256 ID of the token to be transferred
|
||||
*/
|
||||
function _clearApproval(address owner, uint256 tokenId) internal {
|
||||
require(ownerOf(tokenId) == owner);
|
||||
if (_tokenApprovals[tokenId] != address(0)) {
|
||||
_tokenApprovals[tokenId] = address(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -142,10 +282,9 @@ contract ERC721 is ERC165, ERC721Basic, IERC721 {
|
||||
* @param tokenId uint256 ID of the token to be added to the tokens list of the given address
|
||||
*/
|
||||
function _addTokenTo(address to, uint256 tokenId) internal {
|
||||
super._addTokenTo(to, tokenId);
|
||||
uint256 length = _ownedTokens[to].length;
|
||||
_ownedTokens[to].push(tokenId);
|
||||
_ownedTokensIndex[tokenId] = length;
|
||||
require(_tokenOwner[tokenId] == address(0));
|
||||
_tokenOwner[tokenId] = to;
|
||||
_ownedTokensCount[to] = _ownedTokensCount[to].add(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -154,64 +293,34 @@ contract ERC721 is ERC165, ERC721Basic, IERC721 {
|
||||
* @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
|
||||
*/
|
||||
function _removeTokenFrom(address from, uint256 tokenId) internal {
|
||||
super._removeTokenFrom(from, tokenId);
|
||||
|
||||
// To prevent a gap in the array, we store the last token in the index of the token to delete, and
|
||||
// then delete the last slot.
|
||||
uint256 tokenIndex = _ownedTokensIndex[tokenId];
|
||||
uint256 lastTokenIndex = _ownedTokens[from].length.sub(1);
|
||||
uint256 lastToken = _ownedTokens[from][lastTokenIndex];
|
||||
|
||||
_ownedTokens[from][tokenIndex] = lastToken;
|
||||
// This also deletes the contents at the last position of the array
|
||||
_ownedTokens[from].length--;
|
||||
|
||||
// Note that this will handle single-element arrays. In that case, both tokenIndex and lastTokenIndex are going to
|
||||
// be zero. Then we can make sure that we will remove _tokenId from the ownedTokens list since we are first swapping
|
||||
// the lastToken to the first position, and then dropping the element placed in the last position of the list
|
||||
|
||||
_ownedTokensIndex[tokenId] = 0;
|
||||
_ownedTokensIndex[lastToken] = tokenIndex;
|
||||
require(ownerOf(tokenId) == from);
|
||||
_ownedTokensCount[from] = _ownedTokensCount[from].sub(1);
|
||||
_tokenOwner[tokenId] = address(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Internal function to mint a new token
|
||||
* Reverts if the given token ID already exists
|
||||
* @param to address the beneficiary that will own the minted token
|
||||
* @param tokenId uint256 ID of the token to be minted by the msg.sender
|
||||
* @dev Internal function to invoke `onERC721Received` on a target address
|
||||
* The call is not executed if the target address is not a contract
|
||||
* @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 whether the call correctly returned the expected magic value
|
||||
*/
|
||||
function _mint(address to, uint256 tokenId) internal {
|
||||
super._mint(to, tokenId);
|
||||
|
||||
_allTokensIndex[tokenId] = _allTokens.length;
|
||||
_allTokens.push(tokenId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Internal function to burn a specific token
|
||||
* Reverts if the token does not exist
|
||||
* @param owner owner of the token to burn
|
||||
* @param tokenId uint256 ID of the token being burned by the msg.sender
|
||||
*/
|
||||
function _burn(address owner, uint256 tokenId) internal {
|
||||
super._burn(owner, tokenId);
|
||||
|
||||
// Clear metadata (if any)
|
||||
if (bytes(_tokenURIs[tokenId]).length != 0) {
|
||||
delete _tokenURIs[tokenId];
|
||||
function _checkAndCallSafeTransfer(
|
||||
address from,
|
||||
address to,
|
||||
uint256 tokenId,
|
||||
bytes _data
|
||||
)
|
||||
internal
|
||||
returns (bool)
|
||||
{
|
||||
if (!to.isContract()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Reorg all tokens array
|
||||
uint256 tokenIndex = _allTokensIndex[tokenId];
|
||||
uint256 lastTokenIndex = _allTokens.length.sub(1);
|
||||
uint256 lastToken = _allTokens[lastTokenIndex];
|
||||
|
||||
_allTokens[tokenIndex] = lastToken;
|
||||
_allTokens[lastTokenIndex] = 0;
|
||||
|
||||
_allTokens.length--;
|
||||
_allTokensIndex[tokenId] = 0;
|
||||
_allTokensIndex[lastToken] = tokenIndex;
|
||||
bytes4 retval = IERC721Receiver(to).onERC721Received(
|
||||
msg.sender, from, tokenId, _data);
|
||||
return (retval == _ERC721_RECEIVED);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,325 +0,0 @@
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
import "./IERC721Basic.sol";
|
||||
import "./IERC721Receiver.sol";
|
||||
import "../../math/SafeMath.sol";
|
||||
import "../../utils/Address.sol";
|
||||
import "../../introspection/ERC165.sol";
|
||||
|
||||
|
||||
/**
|
||||
* @title ERC721 Non-Fungible Token Standard basic implementation
|
||||
* @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
|
||||
*/
|
||||
contract ERC721Basic is ERC165, IERC721Basic {
|
||||
|
||||
using SafeMath for uint256;
|
||||
using Address for address;
|
||||
|
||||
// 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 => uint256) private _ownedTokensCount;
|
||||
|
||||
// Mapping from owner to operator approvals
|
||||
mapping (address => mapping (address => bool)) private _operatorApprovals;
|
||||
|
||||
bytes4 private constant _InterfaceId_ERC721 = 0x80ac58cd;
|
||||
/*
|
||||
* 0x80ac58cd ===
|
||||
* bytes4(keccak256('balanceOf(address)')) ^
|
||||
* bytes4(keccak256('ownerOf(uint256)')) ^
|
||||
* bytes4(keccak256('approve(address,uint256)')) ^
|
||||
* bytes4(keccak256('getApproved(uint256)')) ^
|
||||
* bytes4(keccak256('setApprovalForAll(address,bool)')) ^
|
||||
* bytes4(keccak256('isApprovedForAll(address,address)')) ^
|
||||
* bytes4(keccak256('transferFrom(address,address,uint256)')) ^
|
||||
* bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^
|
||||
* bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)'))
|
||||
*/
|
||||
|
||||
constructor()
|
||||
public
|
||||
{
|
||||
// register the supported interfaces to conform to ERC721 via ERC165
|
||||
_registerInterface(_InterfaceId_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));
|
||||
return _ownedTokensCount[owner];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Gets the owner of the specified token ID
|
||||
* @param tokenId uint256 ID of the token to query the owner of
|
||||
* @return owner 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));
|
||||
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);
|
||||
require(msg.sender == owner || isApprovedForAll(owner, msg.sender));
|
||||
|
||||
_tokenApprovals[tokenId] = to;
|
||||
emit Approval(owner, to, tokenId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Gets the approved address for a token ID, or zero if no address set
|
||||
* @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));
|
||||
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);
|
||||
_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
|
||||
{
|
||||
require(_isApprovedOrOwner(msg.sender, tokenId));
|
||||
require(to != address(0));
|
||||
|
||||
_clearApproval(from, tokenId);
|
||||
_removeTokenFrom(from, tokenId);
|
||||
_addTokenTo(to, tokenId);
|
||||
|
||||
emit Transfer(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
|
||||
{
|
||||
// solium-disable-next-line arg-overflow
|
||||
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 data
|
||||
)
|
||||
public
|
||||
{
|
||||
transferFrom(from, to, tokenId);
|
||||
// solium-disable-next-line arg-overflow
|
||||
require(_checkAndCallSafeTransfer(from, to, tokenId, data));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns whether the specified token exists
|
||||
* @param tokenId uint256 ID of the token to query the existence of
|
||||
* @return 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)
|
||||
{
|
||||
address owner = ownerOf(tokenId);
|
||||
// Disable solium check because of
|
||||
// https://github.com/duaraghav8/Solium/issues/175
|
||||
// solium-disable-next-line operator-whitespace
|
||||
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 by the msg.sender
|
||||
*/
|
||||
function _mint(address to, uint256 tokenId) internal {
|
||||
require(to != address(0));
|
||||
_addTokenTo(to, tokenId);
|
||||
emit Transfer(address(0), to, 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 by the msg.sender
|
||||
*/
|
||||
function _burn(address owner, uint256 tokenId) internal {
|
||||
_clearApproval(owner, tokenId);
|
||||
_removeTokenFrom(owner, tokenId);
|
||||
emit Transfer(owner, address(0), tokenId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Internal function to clear current approval of a given token ID
|
||||
* Reverts if the given address is not indeed the owner of the token
|
||||
* @param owner owner of the token
|
||||
* @param tokenId uint256 ID of the token to be transferred
|
||||
*/
|
||||
function _clearApproval(address owner, uint256 tokenId) internal {
|
||||
require(ownerOf(tokenId) == owner);
|
||||
if (_tokenApprovals[tokenId] != address(0)) {
|
||||
_tokenApprovals[tokenId] = address(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Internal function to add a token ID to the list of a given address
|
||||
* @param to address representing the new owner of the given token ID
|
||||
* @param tokenId uint256 ID of the token to be added to the tokens list of the given address
|
||||
*/
|
||||
function _addTokenTo(address to, uint256 tokenId) internal {
|
||||
require(_tokenOwner[tokenId] == address(0));
|
||||
_tokenOwner[tokenId] = to;
|
||||
_ownedTokensCount[to] = _ownedTokensCount[to].add(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Internal function to remove a token ID from the list of a given address
|
||||
* @param from address representing the previous owner of the given token ID
|
||||
* @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
|
||||
*/
|
||||
function _removeTokenFrom(address from, uint256 tokenId) internal {
|
||||
require(ownerOf(tokenId) == from);
|
||||
_ownedTokensCount[from] = _ownedTokensCount[from].sub(1);
|
||||
_tokenOwner[tokenId] = address(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Internal function to invoke `onERC721Received` on a target address
|
||||
* The call is not executed if the target address is not a contract
|
||||
* @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 whether the call correctly returned the expected magic value
|
||||
*/
|
||||
function _checkAndCallSafeTransfer(
|
||||
address from,
|
||||
address to,
|
||||
uint256 tokenId,
|
||||
bytes data
|
||||
)
|
||||
internal
|
||||
returns (bool)
|
||||
{
|
||||
if (!to.isContract()) {
|
||||
return true;
|
||||
}
|
||||
bytes4 retval = IERC721Receiver(to).onERC721Received(
|
||||
msg.sender, from, tokenId, data);
|
||||
return (retval == _ERC721_RECEIVED);
|
||||
}
|
||||
}
|
||||
146
contracts/token/ERC721/ERC721Enumerable.sol
Normal file
146
contracts/token/ERC721/ERC721Enumerable.sol
Normal file
@ -0,0 +1,146 @@
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
import "./IERC721Enumerable.sol";
|
||||
import "./ERC721.sol";
|
||||
import "../../introspection/ERC165.sol";
|
||||
|
||||
|
||||
contract ERC721Enumerable is ERC165, ERC721, IERC721Enumerable {
|
||||
// Mapping from owner to list of owned token IDs
|
||||
mapping(address => uint256[]) private _ownedTokens;
|
||||
|
||||
// Mapping from token ID to index of the owner tokens list
|
||||
mapping(uint256 => uint256) private _ownedTokensIndex;
|
||||
|
||||
// Array with all token ids, used for enumeration
|
||||
uint256[] private _allTokens;
|
||||
|
||||
// Mapping from token id to position in the allTokens array
|
||||
mapping(uint256 => uint256) private _allTokensIndex;
|
||||
|
||||
bytes4 private constant _InterfaceId_ERC721Enumerable = 0x780e9d63;
|
||||
/**
|
||||
* 0x780e9d63 ===
|
||||
* bytes4(keccak256('totalSupply()')) ^
|
||||
* bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) ^
|
||||
* bytes4(keccak256('tokenByIndex(uint256)'))
|
||||
*/
|
||||
|
||||
/**
|
||||
* @dev Constructor function
|
||||
*/
|
||||
constructor() public {
|
||||
// register the supported interface to conform to ERC721 via ERC165
|
||||
_registerInterface(_InterfaceId_ERC721Enumerable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Gets the token ID at a given index of the tokens list of the requested owner
|
||||
* @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
|
||||
returns (uint256)
|
||||
{
|
||||
require(index < balanceOf(owner));
|
||||
return _ownedTokens[owner][index];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Gets the total amount of tokens stored by the contract
|
||||
* @return uint256 representing the total amount of tokens
|
||||
*/
|
||||
function totalSupply() public view returns (uint256) {
|
||||
return _allTokens.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Gets the token ID at a given index of all the tokens in this contract
|
||||
* 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 returns (uint256) {
|
||||
require(index < totalSupply());
|
||||
return _allTokens[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Internal function to add a token ID to the list of a given address
|
||||
* @param to address representing the new owner of the given token ID
|
||||
* @param tokenId uint256 ID of the token to be added to the tokens list of the given address
|
||||
*/
|
||||
function _addTokenTo(address to, uint256 tokenId) internal {
|
||||
super._addTokenTo(to, tokenId);
|
||||
uint256 length = _ownedTokens[to].length;
|
||||
_ownedTokens[to].push(tokenId);
|
||||
_ownedTokensIndex[tokenId] = length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Internal function to remove a token ID from the list of a given address
|
||||
* @param from address representing the previous owner of the given token ID
|
||||
* @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
|
||||
*/
|
||||
function _removeTokenFrom(address from, uint256 tokenId) internal {
|
||||
super._removeTokenFrom(from, tokenId);
|
||||
|
||||
// To prevent a gap in the array, we store the last token in the index of the token to delete, and
|
||||
// then delete the last slot.
|
||||
uint256 tokenIndex = _ownedTokensIndex[tokenId];
|
||||
uint256 lastTokenIndex = _ownedTokens[from].length.sub(1);
|
||||
uint256 lastToken = _ownedTokens[from][lastTokenIndex];
|
||||
|
||||
_ownedTokens[from][tokenIndex] = lastToken;
|
||||
// This also deletes the contents at the last position of the array
|
||||
_ownedTokens[from].length--;
|
||||
|
||||
// Note that this will handle single-element arrays. In that case, both tokenIndex and lastTokenIndex are going to
|
||||
// be zero. Then we can make sure that we will remove tokenId from the ownedTokens list since we are first swapping
|
||||
// the lastToken to the first position, and then dropping the element placed in the last position of the list
|
||||
|
||||
_ownedTokensIndex[tokenId] = 0;
|
||||
_ownedTokensIndex[lastToken] = tokenIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Internal function to mint a new token
|
||||
* Reverts if the given token ID already exists
|
||||
* @param to address the beneficiary that will own the minted token
|
||||
* @param tokenId uint256 ID of the token to be minted by the msg.sender
|
||||
*/
|
||||
function _mint(address to, uint256 tokenId) internal {
|
||||
super._mint(to, tokenId);
|
||||
|
||||
_allTokensIndex[tokenId] = _allTokens.length;
|
||||
_allTokens.push(tokenId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Internal function to burn a specific token
|
||||
* Reverts if the token does not exist
|
||||
* @param owner owner of the token to burn
|
||||
* @param tokenId uint256 ID of the token being burned by the msg.sender
|
||||
*/
|
||||
function _burn(address owner, uint256 tokenId) internal {
|
||||
super._burn(owner, tokenId);
|
||||
|
||||
// Reorg all tokens array
|
||||
uint256 tokenIndex = _allTokensIndex[tokenId];
|
||||
uint256 lastTokenIndex = _allTokens.length.sub(1);
|
||||
uint256 lastToken = _allTokens[lastTokenIndex];
|
||||
|
||||
_allTokens[tokenIndex] = lastToken;
|
||||
_allTokens[lastTokenIndex] = 0;
|
||||
|
||||
_allTokens.length--;
|
||||
_allTokensIndex[tokenId] = 0;
|
||||
_allTokensIndex[lastToken] = tokenIndex;
|
||||
}
|
||||
}
|
||||
19
contracts/token/ERC721/ERC721Full.sol
Normal file
19
contracts/token/ERC721/ERC721Full.sol
Normal file
@ -0,0 +1,19 @@
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
import "./ERC721.sol";
|
||||
import "./ERC721Enumerable.sol";
|
||||
import "./ERC721Metadata.sol";
|
||||
|
||||
|
||||
/**
|
||||
* @title Full ERC721 Token
|
||||
* This implementation includes all the required and some optional functionality of the ERC721 standard
|
||||
* Moreover, it includes approve all functionality using operator terminology
|
||||
* @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
|
||||
*/
|
||||
contract ERC721Full is ERC721, ERC721Enumerable, ERC721Metadata {
|
||||
constructor(string name, string symbol) ERC721Metadata(name, symbol)
|
||||
public
|
||||
{
|
||||
}
|
||||
}
|
||||
88
contracts/token/ERC721/ERC721Metadata.sol
Normal file
88
contracts/token/ERC721/ERC721Metadata.sol
Normal file
@ -0,0 +1,88 @@
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
import "./ERC721.sol";
|
||||
import "./IERC721Metadata.sol";
|
||||
import "../../introspection/ERC165.sol";
|
||||
|
||||
|
||||
contract ERC721Metadata is ERC165, ERC721, IERC721Metadata {
|
||||
// Token name
|
||||
string internal _name;
|
||||
|
||||
// Token symbol
|
||||
string internal _symbol;
|
||||
|
||||
// Optional mapping for token URIs
|
||||
mapping(uint256 => string) private _tokenURIs;
|
||||
|
||||
bytes4 private constant InterfaceId_ERC721Metadata = 0x5b5e139f;
|
||||
/**
|
||||
* 0x5b5e139f ===
|
||||
* bytes4(keccak256('name()')) ^
|
||||
* bytes4(keccak256('symbol()')) ^
|
||||
* bytes4(keccak256('tokenURI(uint256)'))
|
||||
*/
|
||||
|
||||
/**
|
||||
* @dev Constructor function
|
||||
*/
|
||||
constructor(string name, string symbol) public {
|
||||
_name = name;
|
||||
_symbol = symbol;
|
||||
|
||||
// register the supported interfaces to conform to ERC721 via ERC165
|
||||
_registerInterface(InterfaceId_ERC721Metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Gets the token name
|
||||
* @return string representing the token name
|
||||
*/
|
||||
function name() external view returns (string) {
|
||||
return _name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Gets the token symbol
|
||||
* @return string representing the token symbol
|
||||
*/
|
||||
function symbol() external view returns (string) {
|
||||
return _symbol;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns an URI for a given token ID
|
||||
* Throws if the token ID does not exist. May return an empty string.
|
||||
* @param tokenId uint256 ID of the token to query
|
||||
*/
|
||||
function tokenURI(uint256 tokenId) public view returns (string) {
|
||||
require(_exists(tokenId));
|
||||
return _tokenURIs[tokenId];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Internal function to set the token URI for a given token
|
||||
* Reverts if the token ID does not exist
|
||||
* @param tokenId uint256 ID of the token to set its URI
|
||||
* @param uri string URI to assign
|
||||
*/
|
||||
function _setTokenURI(uint256 tokenId, string uri) internal {
|
||||
require(_exists(tokenId));
|
||||
_tokenURIs[tokenId] = uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Internal function to burn a specific token
|
||||
* Reverts if the token does not exist
|
||||
* @param owner owner of the token to burn
|
||||
* @param tokenId uint256 ID of the token being burned by the msg.sender
|
||||
*/
|
||||
function _burn(address owner, uint256 tokenId) internal {
|
||||
super._burn(owner, tokenId);
|
||||
|
||||
// Clear metadata (if any)
|
||||
if (bytes(_tokenURIs[tokenId]).length != 0) {
|
||||
delete _tokenURIs[tokenId];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
import "./ERC721.sol";
|
||||
import "./ERC721Full.sol";
|
||||
import "../../access/roles/MinterRole.sol";
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ import "../../access/roles/MinterRole.sol";
|
||||
* @title ERC721Mintable
|
||||
* @dev ERC721 minting logic
|
||||
*/
|
||||
contract ERC721Mintable is ERC721, MinterRole {
|
||||
contract ERC721Mintable is ERC721Full, MinterRole {
|
||||
event MintingFinished();
|
||||
|
||||
bool private _mintingFinished = false;
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
import "./ERC721Basic.sol";
|
||||
import "./ERC721.sol";
|
||||
import "../../lifecycle/Pausable.sol";
|
||||
|
||||
|
||||
/**
|
||||
* @title ERC721 Non-Fungible Pausable token
|
||||
* @dev ERC721Basic modified with pausable transfers.
|
||||
* @dev ERC721 modified with pausable transfers.
|
||||
**/
|
||||
contract ERC721Pausable is ERC721Basic, Pausable {
|
||||
contract ERC721Pausable is ERC721, Pausable {
|
||||
function approve(
|
||||
address to,
|
||||
uint256 tokenId
|
||||
|
||||
@ -1,40 +1,50 @@
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
import "./IERC721Basic.sol";
|
||||
import "../../introspection/IERC165.sol";
|
||||
|
||||
|
||||
/**
|
||||
* @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
|
||||
* @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
|
||||
* @title ERC721 Non-Fungible Token Standard basic interface
|
||||
* @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
|
||||
*/
|
||||
contract IERC721Enumerable is IERC721Basic {
|
||||
function totalSupply() public view returns (uint256);
|
||||
function tokenOfOwnerByIndex(
|
||||
address owner,
|
||||
uint256 index
|
||||
contract IERC721 is IERC165 {
|
||||
|
||||
event Transfer(
|
||||
address indexed from,
|
||||
address indexed to,
|
||||
uint256 indexed tokenId
|
||||
);
|
||||
event Approval(
|
||||
address indexed owner,
|
||||
address indexed approved,
|
||||
uint256 indexed tokenId
|
||||
);
|
||||
event ApprovalForAll(
|
||||
address indexed owner,
|
||||
address indexed operator,
|
||||
bool approved
|
||||
);
|
||||
|
||||
function balanceOf(address owner) public view returns (uint256 balance);
|
||||
function ownerOf(uint256 tokenId) public view returns (address owner);
|
||||
|
||||
function approve(address to, uint256 tokenId) public;
|
||||
function getApproved(uint256 tokenId)
|
||||
public view returns (address operator);
|
||||
|
||||
function setApprovalForAll(address operator, bool _approved) public;
|
||||
function isApprovedForAll(address owner, address operator)
|
||||
public view returns (bool);
|
||||
|
||||
function transferFrom(address from, address to, uint256 tokenId) public;
|
||||
function safeTransferFrom(address from, address to, uint256 tokenId)
|
||||
public;
|
||||
|
||||
function safeTransferFrom(
|
||||
address from,
|
||||
address to,
|
||||
uint256 tokenId,
|
||||
bytes data
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256 tokenId);
|
||||
|
||||
function tokenByIndex(uint256 index) public view returns (uint256);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @title ERC-721 Non-Fungible Token Standard, optional metadata extension
|
||||
* @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
|
||||
*/
|
||||
contract IERC721Metadata is IERC721Basic {
|
||||
function name() external view returns (string);
|
||||
function symbol() external view returns (string);
|
||||
function tokenURI(uint256 tokenId) public view returns (string);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @title ERC-721 Non-Fungible Token Standard, full implementation interface
|
||||
* @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
|
||||
*/
|
||||
contract IERC721 is IERC721Basic, IERC721Enumerable, IERC721Metadata {
|
||||
public;
|
||||
}
|
||||
|
||||
@ -1,50 +0,0 @@
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
import "../../introspection/IERC165.sol";
|
||||
|
||||
|
||||
/**
|
||||
* @title ERC721 Non-Fungible Token Standard basic interface
|
||||
* @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
|
||||
*/
|
||||
contract IERC721Basic is IERC165 {
|
||||
|
||||
event Transfer(
|
||||
address indexed from,
|
||||
address indexed to,
|
||||
uint256 indexed tokenId
|
||||
);
|
||||
event Approval(
|
||||
address indexed owner,
|
||||
address indexed approved,
|
||||
uint256 indexed tokenId
|
||||
);
|
||||
event ApprovalForAll(
|
||||
address indexed owner,
|
||||
address indexed operator,
|
||||
bool approved
|
||||
);
|
||||
|
||||
function balanceOf(address owner) public view returns (uint256 balance);
|
||||
function ownerOf(uint256 tokenId) public view returns (address owner);
|
||||
|
||||
function approve(address to, uint256 tokenId) public;
|
||||
function getApproved(uint256 tokenId)
|
||||
public view returns (address operator);
|
||||
|
||||
function setApprovalForAll(address operator, bool approved) public;
|
||||
function isApprovedForAll(address owner, address operator)
|
||||
public view returns (bool);
|
||||
|
||||
function transferFrom(address from, address to, uint256 tokenId) public;
|
||||
function safeTransferFrom(address from, address to, uint256 tokenId)
|
||||
public;
|
||||
|
||||
function safeTransferFrom(
|
||||
address from,
|
||||
address to,
|
||||
uint256 tokenId,
|
||||
bytes data
|
||||
)
|
||||
public;
|
||||
}
|
||||
21
contracts/token/ERC721/IERC721Enumerable.sol
Normal file
21
contracts/token/ERC721/IERC721Enumerable.sol
Normal file
@ -0,0 +1,21 @@
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
import "./IERC721.sol";
|
||||
|
||||
|
||||
/**
|
||||
* @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
|
||||
* @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
|
||||
*/
|
||||
contract IERC721Enumerable is IERC721 {
|
||||
function totalSupply() public view returns (uint256);
|
||||
function tokenOfOwnerByIndex(
|
||||
address owner,
|
||||
uint256 index
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256 tokenId);
|
||||
|
||||
function tokenByIndex(uint256 index) public view returns (uint256);
|
||||
}
|
||||
13
contracts/token/ERC721/IERC721Full.sol
Normal file
13
contracts/token/ERC721/IERC721Full.sol
Normal file
@ -0,0 +1,13 @@
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
import "./IERC721.sol";
|
||||
import "./IERC721Enumerable.sol";
|
||||
import "./IERC721Metadata.sol";
|
||||
|
||||
|
||||
/**
|
||||
* @title ERC-721 Non-Fungible Token Standard, full implementation interface
|
||||
* @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
|
||||
*/
|
||||
contract IERC721Full is IERC721, IERC721Enumerable, IERC721Metadata {
|
||||
}
|
||||
14
contracts/token/ERC721/IERC721Metadata.sol
Normal file
14
contracts/token/ERC721/IERC721Metadata.sol
Normal file
@ -0,0 +1,14 @@
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
import "./IERC721.sol";
|
||||
|
||||
|
||||
/**
|
||||
* @title ERC-721 Non-Fungible Token Standard, optional metadata extension
|
||||
* @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
|
||||
*/
|
||||
contract IERC721Metadata is IERC721 {
|
||||
function name() external view returns (string name);
|
||||
function symbol() external view returns (string symbol);
|
||||
function tokenURI(uint256 tokenId) public view returns (string);
|
||||
}
|
||||
Reference in New Issue
Block a user