Squashed commit of the following:
commit fcf35eb806100de300bd9803ce3150dde1ecc424
Author: Francisco Giordano <frangio.1@gmail.com>
Date: Wed Jul 17 17:16:04 2019 -0300
remove all docsite dependency
commit eeaee9a9d43d70704f6ab17b5126ddbd52b93a50
Author: Francisco Giordano <frangio.1@gmail.com>
Date: Wed Jul 17 17:15:23 2019 -0300
update solidity-docgen
commit f021ff951829ea0c155186749819403c6b76e803
Author: Francisco Giordano <frangio.1@gmail.com>
Date: Wed Jul 17 17:05:06 2019 -0300
update docsite script for new setup
commit ff887699d381cfbbe3acf1f1c0de8e22b58480f3
Merge: c938aa1d 84f85a41
Author: Francisco Giordano <frangio.1@gmail.com>
Date: Wed Jul 17 16:46:46 2019 -0300
Merge branch 'master' into antora
commit c938aa1d9ed05ac83a34e2cebd8353f8331ad6d6
Author: Francisco Giordano <frangio.1@gmail.com>
Date: Tue Jul 16 18:24:29 2019 -0300
make component name shorter
commit 5bbd6931e02cbbd8864c82655ad0f390ceead5f3
Author: Francisco Giordano <frangio.1@gmail.com>
Date: Wed Jul 10 20:16:17 2019 -0300
add all info to docs templates
commit 39682c4515d7cf0f0368ed557f50d2709174208a
Author: Francisco Giordano <frangio.1@gmail.com>
Date: Wed Jul 10 20:13:49 2019 -0300
fix npm docsite script
commit 7ae46bd4a0437abf66150d54d05adf46e3de2cab
Author: Francisco Giordano <frangio.1@gmail.com>
Date: Wed Jul 10 18:48:05 2019 -0300
convert inline docs to asciidoc
commit cfdfd3dee4b4bf582fde22c8cb6e17a603d6e0c8
Author: Francisco Giordano <frangio.1@gmail.com>
Date: Wed Jul 10 17:34:52 2019 -0300
add missing contract names in readmes
commit 15b6a2f9bfb546cf1d3bf4f104278b118bf1b3f4
Author: Francisco Giordano <frangio.1@gmail.com>
Date: Wed Jul 10 17:16:47 2019 -0300
fix script path
commit 80d82b909f9460d1450d401f00b3f309da506b29
Author: Francisco Giordano <frangio.1@gmail.com>
Date: Wed Jul 10 17:13:53 2019 -0300
update version of solidity-docgen
commit a870b6c607b9c2d0012f8a60a4ed1a1c8b7e8ebd
Author: Francisco Giordano <frangio.1@gmail.com>
Date: Wed Jul 10 17:03:53 2019 -0300
add nav generation of api ref
commit 069cff4a25b83752650b54b86d85608c2f547e5e
Author: Francisco Giordano <frangio.1@gmail.com>
Date: Wed Jul 10 16:32:14 2019 -0300
initial migration to asciidoc and new docgen version
commit 55216eed0a6551da913c8d1da4b2a0d0d3faa1a8
Author: Francisco Giordano <frangio.1@gmail.com>
Date: Tue Jun 25 20:39:35 2019 -0300
add basic api doc example
commit 0cbe50ce2173b6d1d9a698329d91220f58822a53
Author: Francisco Giordano <frangio.1@gmail.com>
Date: Tue Jun 25 19:31:31 2019 -0300
add sidebars
commit 256fc942845307258ac9dc25aace48117fa10f79
Author: Francisco Giordano <frangio.1@gmail.com>
Date: Tue Jun 25 15:22:38 2019 -0300
add page titles
commit f4d0effa70e1fc0662729863e8ee72a8821bc458
Author: Francisco Giordano <frangio.1@gmail.com>
Date: Tue Jun 25 15:19:41 2019 -0300
add contracts index file
commit b73b06359979f7d933df7f2b283c50cb1c31b2a0
Author: Francisco Giordano <frangio.1@gmail.com>
Date: Tue Jun 25 15:14:52 2019 -0300
fix header levels
commit fb57d9b820f09a1b7c04eed1a205be0e45866cac
Author: Francisco Giordano <frangio.1@gmail.com>
Date: Tue Jun 25 15:11:47 2019 -0300
switch format to preferred asciidoctor format
commit 032181d8804137332c71534753929d080a31a71f
Author: Francisco Giordano <frangio.1@gmail.com>
Date: Tue Jun 25 15:05:38 2019 -0300
initialize antora component and convert docs to asciidoc
200 lines
8.5 KiB
Solidity
200 lines
8.5 KiB
Solidity
pragma solidity ^0.5.0;
|
|
|
|
import "./IERC721Enumerable.sol";
|
|
import "./ERC721.sol";
|
|
import "../../introspection/ERC165.sol";
|
|
|
|
/**
|
|
* @title ERC-721 Non-Fungible Token with optional enumeration extension logic
|
|
* @dev See https://eips.ethereum.org/EIPS/eip-721
|
|
*/
|
|
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(keccak256('totalSupply()')) == 0x18160ddd
|
|
* bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) == 0x2f745c59
|
|
* bytes4(keccak256('tokenByIndex(uint256)')) == 0x4f6ccce7
|
|
*
|
|
* => 0x18160ddd ^ 0x2f745c59 ^ 0x4f6ccce7 == 0x780e9d63
|
|
*/
|
|
bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63;
|
|
|
|
/**
|
|
* @dev Constructor function.
|
|
*/
|
|
constructor () public {
|
|
// register the supported interface to conform to ERC721Enumerable via ERC165
|
|
_registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE);
|
|
}
|
|
|
|
/**
|
|
* @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), "ERC721Enumerable: owner index out of bounds");
|
|
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(), "ERC721Enumerable: global index out of bounds");
|
|
return _allTokens[index];
|
|
}
|
|
|
|
/**
|
|
* @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 {
|
|
super._transferFrom(from, to, tokenId);
|
|
|
|
_removeTokenFromOwnerEnumeration(from, tokenId);
|
|
|
|
_addTokenToOwnerEnumeration(to, tokenId);
|
|
}
|
|
|
|
/**
|
|
* @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
|
|
*/
|
|
function _mint(address to, uint256 tokenId) internal {
|
|
super._mint(to, tokenId);
|
|
|
|
_addTokenToOwnerEnumeration(to, tokenId);
|
|
|
|
_addTokenToAllTokensEnumeration(tokenId);
|
|
}
|
|
|
|
/**
|
|
* @dev Internal function to burn a specific token.
|
|
* Reverts if the token does not exist.
|
|
* Deprecated, use {ERC721-_burn} 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 {
|
|
super._burn(owner, tokenId);
|
|
|
|
_removeTokenFromOwnerEnumeration(owner, tokenId);
|
|
// Since tokenId will be deleted, we can clear its slot in _ownedTokensIndex to trigger a gas refund
|
|
_ownedTokensIndex[tokenId] = 0;
|
|
|
|
_removeTokenFromAllTokensEnumeration(tokenId);
|
|
}
|
|
|
|
/**
|
|
* @dev Gets the list of token IDs of the requested owner.
|
|
* @param owner address owning the tokens
|
|
* @return uint256[] List of token IDs owned by the requested address
|
|
*/
|
|
function _tokensOfOwner(address owner) internal view returns (uint256[] storage) {
|
|
return _ownedTokens[owner];
|
|
}
|
|
|
|
/**
|
|
* @dev Private function to add a token to this extension's ownership-tracking data structures.
|
|
* @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 _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
|
|
_ownedTokensIndex[tokenId] = _ownedTokens[to].length;
|
|
_ownedTokens[to].push(tokenId);
|
|
}
|
|
|
|
/**
|
|
* @dev Private function to add a token to this extension's token tracking data structures.
|
|
* @param tokenId uint256 ID of the token to be added to the tokens list
|
|
*/
|
|
function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
|
|
_allTokensIndex[tokenId] = _allTokens.length;
|
|
_allTokens.push(tokenId);
|
|
}
|
|
|
|
/**
|
|
* @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
|
|
* while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
|
|
* gas optimizations e.g. when performing a transfer operation (avoiding double writes).
|
|
* This has O(1) time complexity, but alters the order of the _ownedTokens array.
|
|
* @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 _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
|
|
// To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
|
|
// then delete the last slot (swap and pop).
|
|
|
|
uint256 lastTokenIndex = _ownedTokens[from].length.sub(1);
|
|
uint256 tokenIndex = _ownedTokensIndex[tokenId];
|
|
|
|
// When the token to delete is the last token, the swap operation is unnecessary
|
|
if (tokenIndex != lastTokenIndex) {
|
|
uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];
|
|
|
|
_ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
|
|
_ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
|
|
}
|
|
|
|
// This also deletes the contents at the last position of the array
|
|
_ownedTokens[from].length--;
|
|
|
|
// Note that _ownedTokensIndex[tokenId] hasn't been cleared: it still points to the old slot (now occupied by
|
|
// lastTokenId, or just over the end of the array if the token was the last one).
|
|
}
|
|
|
|
/**
|
|
* @dev Private function to remove a token from this extension's token tracking data structures.
|
|
* This has O(1) time complexity, but alters the order of the _allTokens array.
|
|
* @param tokenId uint256 ID of the token to be removed from the tokens list
|
|
*/
|
|
function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
|
|
// To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
|
|
// then delete the last slot (swap and pop).
|
|
|
|
uint256 lastTokenIndex = _allTokens.length.sub(1);
|
|
uint256 tokenIndex = _allTokensIndex[tokenId];
|
|
|
|
// When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
|
|
// rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
|
|
// an 'if' statement (like in _removeTokenFromOwnerEnumeration)
|
|
uint256 lastTokenId = _allTokens[lastTokenIndex];
|
|
|
|
_allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
|
|
_allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
|
|
|
|
// This also deletes the contents at the last position of the array
|
|
_allTokens.length--;
|
|
_allTokensIndex[tokenId] = 0;
|
|
}
|
|
}
|