aboutsummaryrefslogtreecommitdiffstats
path: root/contracts/erc721/contracts/src/ERC721Token.sol
diff options
context:
space:
mode:
Diffstat (limited to 'contracts/erc721/contracts/src/ERC721Token.sol')
-rw-r--r--contracts/erc721/contracts/src/ERC721Token.sol277
1 files changed, 277 insertions, 0 deletions
diff --git a/contracts/erc721/contracts/src/ERC721Token.sol b/contracts/erc721/contracts/src/ERC721Token.sol
new file mode 100644
index 000000000..32f70ccf8
--- /dev/null
+++ b/contracts/erc721/contracts/src/ERC721Token.sol
@@ -0,0 +1,277 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+pragma solidity ^0.4.24;
+
+import "./interfaces/IERC721Token.sol";
+import "./interfaces/IERC721Receiver.sol";
+import "@0x/contracts-utils/contracts/src/SafeMath.sol";
+
+
+contract ERC721Token is
+ IERC721Token,
+ SafeMath
+{
+ // Function selector for ERC721Receiver.onERC721Received
+ // 0x150b7a02
+ bytes4 constant internal ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));
+
+ // Mapping of tokenId => owner
+ mapping (uint256 => address) internal owners;
+
+ // Mapping of tokenId => approved address
+ mapping (uint256 => address) internal approvals;
+
+ // Mapping of owner => number of tokens owned
+ mapping (address => uint256) internal balances;
+
+ // Mapping of owner => operator => approved
+ mapping (address => mapping (address => bool)) internal operatorApprovals;
+
+ /// @notice Transfers the ownership of an NFT from one address to another address
+ /// @dev Throws unless `msg.sender` is the current owner, an authorized
+ /// operator, or the approved address for this NFT. Throws if `_from` is
+ /// not the current owner. Throws if `_to` is the zero address. Throws if
+ /// `_tokenId` is not a valid NFT. When transfer is complete, this function
+ /// checks if `_to` is a smart contract (code size > 0). If so, it calls
+ /// `onERC721Received` on `_to` and throws if the return value is not
+ /// `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
+ /// @param _from The current owner of the NFT
+ /// @param _to The new owner
+ /// @param _tokenId The NFT to transfer
+ /// @param _data Additional data with no specified format, sent in call to `_to`
+ function safeTransferFrom(
+ address _from,
+ address _to,
+ uint256 _tokenId,
+ bytes _data
+ )
+ external
+ {
+ transferFrom(
+ _from,
+ _to,
+ _tokenId
+ );
+
+ uint256 receiverCodeSize;
+ assembly {
+ receiverCodeSize := extcodesize(_to)
+ }
+ if (receiverCodeSize > 0) {
+ bytes4 selector = IERC721Receiver(_to).onERC721Received(
+ msg.sender,
+ _from,
+ _tokenId,
+ _data
+ );
+ require(
+ selector == ERC721_RECEIVED,
+ "ERC721_INVALID_SELECTOR"
+ );
+ }
+ }
+
+ /// @notice Transfers the ownership of an NFT from one address to another address
+ /// @dev This works identically to the other function with an extra data parameter,
+ /// except this function just sets data to "".
+ /// @param _from The current owner of the NFT
+ /// @param _to The new owner
+ /// @param _tokenId The NFT to transfer
+ function safeTransferFrom(
+ address _from,
+ address _to,
+ uint256 _tokenId
+ )
+ external
+ {
+ transferFrom(
+ _from,
+ _to,
+ _tokenId
+ );
+
+ uint256 receiverCodeSize;
+ assembly {
+ receiverCodeSize := extcodesize(_to)
+ }
+ if (receiverCodeSize > 0) {
+ bytes4 selector = IERC721Receiver(_to).onERC721Received(
+ msg.sender,
+ _from,
+ _tokenId,
+ ""
+ );
+ require(
+ selector == ERC721_RECEIVED,
+ "ERC721_INVALID_SELECTOR"
+ );
+ }
+ }
+
+ /// @notice Change or reaffirm the approved address for an NFT
+ /// @dev The zero address indicates there is no approved address.
+ /// Throws unless `msg.sender` is the current NFT owner, or an authorized
+ /// operator of the current owner.
+ /// @param _approved The new approved NFT controller
+ /// @param _tokenId The NFT to approve
+ function approve(address _approved, uint256 _tokenId)
+ external
+ {
+ address owner = ownerOf(_tokenId);
+ require(
+ msg.sender == owner || isApprovedForAll(owner, msg.sender),
+ "ERC721_INVALID_SENDER"
+ );
+
+ approvals[_tokenId] = _approved;
+ emit Approval(
+ owner,
+ _approved,
+ _tokenId
+ );
+ }
+
+ /// @notice Enable or disable approval for a third party ("operator") to manage
+ /// all of `msg.sender`'s assets
+ /// @dev Emits the ApprovalForAll event. The contract MUST allow
+ /// multiple operators per owner.
+ /// @param _operator Address to add to the set of authorized operators
+ /// @param _approved True if the operator is approved, false to revoke approval
+ function setApprovalForAll(address _operator, bool _approved)
+ external
+ {
+ operatorApprovals[msg.sender][_operator] = _approved;
+ emit ApprovalForAll(
+ msg.sender,
+ _operator,
+ _approved
+ );
+ }
+
+ /// @notice Count all NFTs assigned to an owner
+ /// @dev NFTs assigned to the zero address are considered invalid, and this
+ /// function throws for queries about the zero address.
+ /// @param _owner An address for whom to query the balance
+ /// @return The number of NFTs owned by `_owner`, possibly zero
+ function balanceOf(address _owner)
+ external
+ view
+ returns (uint256)
+ {
+ require(
+ _owner != address(0),
+ "ERC721_ZERO_OWNER"
+ );
+ return balances[_owner];
+ }
+
+ /// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
+ /// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
+ /// THEY MAY BE PERMANENTLY LOST
+ /// @dev Throws unless `msg.sender` is the current owner, an authorized
+ /// operator, or the approved address for this NFT. Throws if `_from` is
+ /// not the current owner. Throws if `_to` is the zero address. Throws if
+ /// `_tokenId` is not a valid NFT.
+ /// @param _from The current owner of the NFT
+ /// @param _to The new owner
+ /// @param _tokenId The NFT to transfer
+ function transferFrom(
+ address _from,
+ address _to,
+ uint256 _tokenId
+ )
+ public
+ {
+ require(
+ _to != address(0),
+ "ERC721_ZERO_TO_ADDRESS"
+ );
+
+ address owner = ownerOf(_tokenId);
+ require(
+ _from == owner,
+ "ERC721_OWNER_MISMATCH"
+ );
+
+ address spender = msg.sender;
+ address approvedAddress = getApproved(_tokenId);
+ require(
+ spender == owner ||
+ isApprovedForAll(owner, spender) ||
+ approvedAddress == spender,
+ "ERC721_INVALID_SPENDER"
+ );
+
+ if (approvedAddress != address(0)) {
+ approvals[_tokenId] = address(0);
+ }
+
+ owners[_tokenId] = _to;
+ balances[_from] = safeSub(balances[_from], 1);
+ balances[_to] = safeAdd(balances[_to], 1);
+
+ emit Transfer(
+ _from,
+ _to,
+ _tokenId
+ );
+ }
+
+ /// @notice Find the owner of an NFT
+ /// @dev NFTs assigned to zero address are considered invalid, and queries
+ /// about them do throw.
+ /// @param _tokenId The identifier for an NFT
+ /// @return The address of the owner of the NFT
+ function ownerOf(uint256 _tokenId)
+ public
+ view
+ returns (address)
+ {
+ address owner = owners[_tokenId];
+ require(
+ owner != address(0),
+ "ERC721_ZERO_OWNER"
+ );
+ return owner;
+ }
+
+ /// @notice Get the approved address for a single NFT
+ /// @dev Throws if `_tokenId` is not a valid NFT.
+ /// @param _tokenId The NFT to find the approved address for
+ /// @return The approved address for this NFT, or the zero address if there is none
+ function getApproved(uint256 _tokenId)
+ public
+ view
+ returns (address)
+ {
+ return approvals[_tokenId];
+ }
+
+ /// @notice Query if an address is an authorized operator for another address
+ /// @param _owner The address that owns the NFTs
+ /// @param _operator The address that acts on behalf of the owner
+ /// @return True if `_operator` is an approved operator for `_owner`, false otherwise
+ function isApprovedForAll(address _owner, address _operator)
+ public
+ view
+ returns (bool)
+ {
+ return operatorApprovals[_owner][_operator];
+ }
+}