From b09d7aada41db444368cf3754b1015b9a1d5e7fe Mon Sep 17 00:00:00 2001 From: Facundo Spagnuolo Date: Tue, 16 Jan 2018 15:57:59 -0300 Subject: [PATCH] Implement ERC721 required functionality --- contracts/token/ERC721Token.sol | 208 ++++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 contracts/token/ERC721Token.sol diff --git a/contracts/token/ERC721Token.sol b/contracts/token/ERC721Token.sol new file mode 100644 index 00000000000..53457dd239d --- /dev/null +++ b/contracts/token/ERC721Token.sol @@ -0,0 +1,208 @@ +pragma solidity ^0.4.18; + +import './ERC721.sol'; +import '../math/SafeMath.sol'; + +/** + * @title ERC721Token + * Generic implementation for the required functionality of the ERC721 standard + */ +contract ERC721Token is ERC721 { + using SafeMath for uint256; + + // Total amount of tokens + uint256 private totalTokens; + + // 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 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; + + /** + * @dev Guarantees msg.sender is owner of the given token + * @param _tokenId uint256 ID of the token to validate its ownership belongs to msg.sender + */ + modifier onlyOwnerOf(uint256 _tokenId) { + require(ownerOf(_tokenId) == msg.sender); + _; + } + + /** + * @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 totalTokens; + } + + /** + * @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) { + return ownedTokens[_owner].length; + } + + /** + * @dev Gets the list of tokens owned by a given address + * @param _owner address to query the tokens of + * @return uint256[] representing the list of tokens owned by the passed address + */ + function tokensOf(address _owner) public view returns (uint256[]) { + return ownedTokens[_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 Gets the approved address to take ownership of a given token ID + * @param _tokenId uint256 ID of the token to query the approval of + * @return address currently approved to take ownership of the given token ID + */ + function approvedFor(uint256 _tokenId) public view returns (address) { + return tokenApprovals[_tokenId]; + } + + /** + * @dev Transfers the ownership of a given token ID to another address + * @param _to address to receive the ownership of the given token ID + * @param _tokenId uint256 ID of the token to be transferred + */ + function transfer(address _to, uint256 _tokenId) public onlyOwnerOf(_tokenId) { + clearApprovalAndTransfer(msg.sender, _to, _tokenId); + } + + /** + * @dev Approves another address to claim for the ownership of the given token ID + * @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 onlyOwnerOf(_tokenId) { + address owner = ownerOf(_tokenId); + require(_to != owner); + if(approvedFor(_tokenId) == 0 && _to == 0) return; + tokenApprovals[_tokenId] = _to; + Approval(owner, _to, _tokenId); + } + + /** + * @dev Claims the ownership of a given token ID + * @param _tokenId uint256 ID of the token being claimed by the msg.sender + */ + function takeOwnership(uint256 _tokenId) public { + require(isApprovedFor(_tokenId)); + clearApprovalAndTransfer(ownerOf(_tokenId), msg.sender, _tokenId); + } + + /** + * @dev Mint token function + * @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)); + addToken(_to, _tokenId); + Transfer(0x0, _to, _tokenId); + } + + /** + * @dev Burns a specific token + * @param _tokenId uint256 ID of the token being burned by the msg.sender + */ + function burn(uint256 _tokenId) onlyOwnerOf(_tokenId) internal { + if(approvedFor(_tokenId) != 0) { + clearApproval(msg.sender, _tokenId); + } + removeToken(msg.sender, _tokenId); + Transfer(msg.sender, 0x0, _tokenId); + } + + /** + * @dev Internal function to clear current approval and transfer the ownership of a given token ID + * @param _from address which you want to send tokens from + * @param _to address which you want to transfer the token to + * @param _tokenId uint256 ID of the token to be transferred + */ + function clearApprovalAndTransfer(address _from, address _to, uint256 _tokenId) internal { + require(_to != address(0)); + require(_to != ownerOf(_tokenId)); + require(ownerOf(_tokenId) == _from); + + clearApproval(_from, _tokenId); + removeToken(_from, _tokenId); + addToken(_to, _tokenId); + Transfer(_from, _to, _tokenId); + } + + /** + * @dev Internal function to clear current approval of a given token ID + * @param _tokenId uint256 ID of the token to be transferred + */ + function clearApproval(address _owner, uint256 _tokenId) internal { + require(ownerOf(_tokenId) == _owner); + tokenApprovals[_tokenId] = 0; + Approval(_owner, 0, _tokenId); + } + + /** + * @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 addToken(address _to, uint256 _tokenId) internal { + require(tokenOwner[_tokenId] == address(0)); + tokenOwner[_tokenId] = _to; + uint256 length = balanceOf(_to); + ownedTokens[_to].push(_tokenId); + ownedTokensIndex[_tokenId] = length; + totalTokens = totalTokens.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 removeToken(address _from, uint256 _tokenId) internal { + require(balanceOf(_from) > 0); + require(ownerOf(_tokenId) == _from); + + uint256 tokenIndex = ownedTokensIndex[_tokenId]; + uint256 lastTokenIndex = balanceOf(_from).sub(1); + uint256 lastToken = ownedTokens[_from][lastTokenIndex]; + + tokenOwner[_tokenId] = 0; + ownedTokens[_from][tokenIndex] = lastToken; + ownedTokens[_from][lastTokenIndex] = 0; + ownedTokens[_from].length--; + ownedTokensIndex[_tokenId] = 0; + ownedTokensIndex[lastToken] = tokenIndex; + totalTokens = totalTokens.sub(1); + } + + /** + * @dev Tells whether the msg.sender is approved for the given token ID or not + * @param _tokenId uint256 ID of the token to query the approval of + * @return bool whether the msg.sender is approved for the given token ID or not + */ + function isApprovedFor(uint256 _tokenId) internal view returns (bool) { + return approvedFor(_tokenId) == msg.sender; + } +}