/** * Source Code first verified at https://etherscan.io on Thursday, March 28, 2019 (UTC) */ pragma solidity 0.5.5; /** * @dev Standard interface for a dex proxy contract. */ interface Proxy { /** * @dev Executes an action. * @param _target Target of execution. * @param _a Address usually representing from. * @param _b Address usually representing to. * @param _c Integer usually repersenting amount/value/id. */ function execute( address _target, address _a, address _b, uint256 _c ) external; } /** * @dev Xcert interface. */ interface Xcert // is ERC721 metadata enumerable { /** * @dev Creates a new Xcert. * @param _to The address that will own the created Xcert. * @param _id The Xcert to be created by the msg.sender. * @param _imprint Cryptographic asset imprint. */ function create( address _to, uint256 _id, bytes32 _imprint ) external; /** * @dev Change URI base. * @param _uriBase New uriBase. */ function setUriBase( string calldata _uriBase ) external; /** * @dev Returns a bytes4 of keccak256 of json schema representing 0xcert Protocol convention. * @return Schema id. */ function schemaId() external view returns (bytes32 _schemaId); /** * @dev Returns imprint for Xcert. * @param _tokenId Id of the Xcert. * @return Token imprint. */ function tokenImprint( uint256 _tokenId ) external view returns(bytes32 imprint); } /** * @dev Xcert burnable interface. */ interface XcertBurnable // is Xcert { /** * @dev Destroys a specified Xcert. Reverts if not called from Xcert owner or operator. * @param _tokenId Id of the Xcert we want to destroy. */ function destroy( uint256 _tokenId ) external; } /** * @dev Xcert nutable interface. */ interface XcertMutable // is Xcert { /** * @dev Updates Xcert imprint. * @param _tokenId Id of the Xcert. * @param _imprint New imprint. */ function updateTokenImprint( uint256 _tokenId, bytes32 _imprint ) external; } /** * @dev Xcert pausable interface. */ interface XcertPausable // is Xcert { /** * @dev Sets if Xcerts transfers are paused (can be performed) or not. * @param _isPaused Pause status. */ function setPause( bool _isPaused ) external; } /** * @dev Xcert revokable interface. */ interface XcertRevokable // is Xcert { /** * @dev Revokes a specified Xcert. Reverts if not called from contract owner or authorized * address. * @param _tokenId Id of the Xcert we want to destroy. */ function revoke( uint256 _tokenId ) external; } /** * @dev Math operations with safety checks that throw on error. This contract is based on the * source code at: * https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/math/SafeMath.sol. */ library SafeMath { /** * @dev Error constants. */ string constant OVERFLOW = "008001"; string constant SUBTRAHEND_GREATER_THEN_MINUEND = "008002"; string constant DIVISION_BY_ZERO = "008003"; /** * @dev Multiplies two numbers, reverts on overflow. * @param _factor1 Factor number. * @param _factor2 Factor number. * @return The product of the two factors. */ function mul( uint256 _factor1, uint256 _factor2 ) internal pure returns (uint256 product) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (_factor1 == 0) { return 0; } product = _factor1 * _factor2; require(product / _factor1 == _factor2, OVERFLOW); } /** * @dev Integer division of two numbers, truncating the quotient, reverts on division by zero. * @param _dividend Dividend number. * @param _divisor Divisor number. * @return The quotient. */ function div( uint256 _dividend, uint256 _divisor ) internal pure returns (uint256 quotient) { // Solidity automatically asserts when dividing by 0, using all gas. require(_divisor > 0, DIVISION_BY_ZERO); quotient = _dividend / _divisor; // assert(_dividend == _divisor * quotient + _dividend % _divisor); // There is no case in which this doesn't hold. } /** * @dev Substracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). * @param _minuend Minuend number. * @param _subtrahend Subtrahend number. * @return Difference. */ function sub( uint256 _minuend, uint256 _subtrahend ) internal pure returns (uint256 difference) { require(_subtrahend <= _minuend, SUBTRAHEND_GREATER_THEN_MINUEND); difference = _minuend - _subtrahend; } /** * @dev Adds two numbers, reverts on overflow. * @param _addend1 Number. * @param _addend2 Number. * @return Sum. */ function add( uint256 _addend1, uint256 _addend2 ) internal pure returns (uint256 sum) { sum = _addend1 + _addend2; require(sum >= _addend1, OVERFLOW); } /** * @dev Divides two numbers and returns the remainder (unsigned integer modulo), reverts when * dividing by zero. * @param _dividend Number. * @param _divisor Number. * @return Remainder. */ function mod( uint256 _dividend, uint256 _divisor ) internal pure returns (uint256 remainder) { require(_divisor != 0, DIVISION_BY_ZERO); remainder = _dividend % _divisor; } } /** * @title Contract for setting abilities. * @dev For optimization purposes the abilities are represented as a bitfield. Maximum number of * abilities is therefore 256. This is an example(for simplicity is made for max 8 abilities) of how * this works. * 00000001 Ability A - number representation 1 * 00000010 Ability B - number representation 2 * 00000100 Ability C - number representation 4 * 00001000 Ability D - number representation 8 * 00010000 Ability E - number representation 16 * etc ... * To grant abilities B and C, we would need a bitfield of 00000110 which is represented by number * 6, in other words, the sum of abilities B and C. The same concept works for revoking abilities * and checking if someone has multiple abilities. */ contract Abilitable { using SafeMath for uint; /** * @dev Error constants. */ string constant NOT_AUTHORIZED = "017001"; string constant CANNOT_REVOKE_OWN_SUPER_ABILITY = "017002"; string constant INVALID_INPUT = "017003"; /** * @dev Ability 1 (00000001) is a reserved ability called super ability. It is an * ability to grant or revoke abilities of other accounts. Other abilities are determined by the * implementing contract. */ uint8 constant SUPER_ABILITY = 1; /** * @dev Maps address to ability ids. */ mapping(address => uint256) public addressToAbility; /** * @dev Emits when an address is granted an ability. * @param _target Address to which we are granting abilities. * @param _abilities Number representing bitfield of abilities we are granting. */ event GrantAbilities( address indexed _target, uint256 indexed _abilities ); /** * @dev Emits when an address gets an ability revoked. * @param _target Address of which we are revoking an ability. * @param _abilities Number representing bitfield of abilities we are revoking. */ event RevokeAbilities( address indexed _target, uint256 indexed _abilities ); /** * @dev Guarantees that msg.sender has certain abilities. */ modifier hasAbilities( uint256 _abilities ) { require(_abilities > 0, INVALID_INPUT); require( addressToAbility[msg.sender] & _abilities == _abilities, NOT_AUTHORIZED ); _; } /** * @dev Contract constructor. * Sets SUPER_ABILITY ability to the sender account. */ constructor() public { addressToAbility[msg.sender] = SUPER_ABILITY; emit GrantAbilities(msg.sender, SUPER_ABILITY); } /** * @dev Grants specific abilities to specified address. * @param _target Address to grant abilities to. * @param _abilities Number representing bitfield of abilities we are granting. */ function grantAbilities( address _target, uint256 _abilities ) external hasAbilities(SUPER_ABILITY) { addressToAbility[_target] |= _abilities; emit GrantAbilities(_target, _abilities); } /** * @dev Unassigns specific abilities from specified address. * @param _target Address of which we revoke abilites. * @param _abilities Number representing bitfield of abilities we are revoking. * @param _allowSuperRevoke Additional check that prevents you from removing your own super * ability by mistake. */ function revokeAbilities( address _target, uint256 _abilities, bool _allowSuperRevoke ) external hasAbilities(SUPER_ABILITY) { if (!_allowSuperRevoke && msg.sender == _target) { require((_abilities & 1) == 0, CANNOT_REVOKE_OWN_SUPER_ABILITY); } addressToAbility[_target] &= ~_abilities; emit RevokeAbilities(_target, _abilities); } /** * @dev Check if an address has a specific ability. Throws if checking for 0. * @param _target Address for which we want to check if it has a specific abilities. * @param _abilities Number representing bitfield of abilities we are checking. */ function isAble( address _target, uint256 _abilities ) external view returns (bool) { require(_abilities > 0, INVALID_INPUT); return (addressToAbility[_target] & _abilities) == _abilities; } } /** * @dev ERC-721 non-fungible token standard. * See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md. */ interface ERC721 { /** * @dev Emits when ownership of any NFT changes by any mechanism. This event emits when NFTs are * created (`from` == 0) and destroyed (`to` == 0). Exception: during contract creation, any * number of NFTs may be created and assigned without emitting Transfer. At the time of any * transfer, the approved address for that NFT (if any) is reset to none. */ event Transfer( address indexed _from, address indexed _to, uint256 indexed _tokenId ); /** * @dev This emits when the approved address for an NFT is changed or reaffirmed. The zero * address indicates there is no approved address. When a Transfer event emits, this also * indicates that the approved address for that NFT (if any) is reset to none. */ event Approval( address indexed _owner, address indexed _approved, uint256 indexed _tokenId ); /** * @dev This emits when an operator is enabled or disabled for an owner. The operator can manage * all NFTs of the owner. */ event ApprovalForAll( address indexed _owner, address indexed _operator, bool _approved ); /** * @dev Transfers the ownership of an NFT from one address to another address. * @notice 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,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 calldata _data ) external; /** * @dev Transfers the ownership of an NFT from one address to another address. * @notice 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; /** * @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. * @notice The caller is responsible to confirm that `_to` is capable of receiving NFTs or else * they mayb be permanently lost. * @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 ) external; /** * @dev Set or reaffirm the approved address for an NFT. * @notice 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; /** * @dev Enables or disables approval for a third party ("operator") to manage all of * `msg.sender`'s assets. It also emits the ApprovalForAll event. * @notice The contract MUST allow multiple operators per owner. * @param _operator Address to add to the set of authorized operators. * @param _approved True if the operators is approved, false to revoke approval. */ function setApprovalForAll( address _operator, bool _approved ) external; /** * @dev Returns the number of NFTs owned by `_owner`. NFTs assigned to the zero address are * considered invalid, and this function throws for queries about the zero address. * @param _owner Address for whom to query the balance. * @return Balance of _owner. */ function balanceOf( address _owner ) external view returns (uint256); /** * @dev Returns the address of the owner of the NFT. NFTs assigned to zero address are considered * invalid, and queries about them do throw. * @param _tokenId The identifier for an NFT. * @return Address of _tokenId owner. */ function ownerOf( uint256 _tokenId ) external view returns (address); /** * @dev Get the approved address for a single NFT. * @notice Throws if `_tokenId` is not a valid NFT. * @param _tokenId The NFT to find the approved address for. * @return Address that _tokenId is approved for. */ function getApproved( uint256 _tokenId ) external view returns (address); /** * @dev Returns true if `_operator` is an approved operator for `_owner`, false otherwise. * @param _owner The address that owns the NFTs. * @param _operator The address that acts on behalf of the owner. * @return True if approved for all, false otherwise. */ function isApprovedForAll( address _owner, address _operator ) external view returns (bool); } /** * @dev Optional metadata extension for ERC-721 non-fungible token standard. * See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md. */ interface ERC721Metadata { /** * @dev Returns a descriptive name for a collection of NFTs in this contract. * @return Representing name. */ function name() external view returns (string memory _name); /** * @dev Returns a abbreviated name for a collection of NFTs in this contract. * @return Representing symbol. */ function symbol() external view returns (string memory _symbol); /** * @dev Returns a distinct Uniform Resource Identifier (URI) for a given asset. It Throws if * `_tokenId` is not a valid NFT. URIs are defined in RFC3986. The URI may point to a JSON file * that conforms to the "ERC721 Metadata JSON Schema". * @return URI of _tokenId. */ function tokenURI(uint256 _tokenId) external view returns (string memory); } /** * @dev Optional enumeration extension for ERC-721 non-fungible token standard. * See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md. */ interface ERC721Enumerable { /** * @dev Returns a count of valid NFTs tracked by this contract, where each one of them has an * assigned and queryable owner not equal to the zero address. * @return Total supply of NFTs. */ function totalSupply() external view returns (uint256); /** * @dev Returns the token identifier for the `_index`th NFT. Sort order is not specified. * @param _index A counter less than `totalSupply()`. * @return Token id. */ function tokenByIndex( uint256 _index ) external view returns (uint256); /** * @dev Returns the token identifier for the `_index`th NFT assigned to `_owner`. Sort order is * not specified. It throws if `_index` >= `balanceOf(_owner)` or if `_owner` is the zero address, * representing invalid NFTs. * @param _owner An address where we are interested in NFTs owned by them. * @param _index A counter less than `balanceOf(_owner)`. * @return Token id. */ function tokenOfOwnerByIndex( address _owner, uint256 _index ) external view returns (uint256); } /** * @dev ERC-721 interface for accepting safe transfers. * See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md. */ interface ERC721TokenReceiver { /** * @dev Handle the receipt of a NFT. The ERC721 smart contract calls this function on the * recipient after a `transfer`. This function MAY throw to revert and reject the transfer. Return * of other than the magic value MUST result in the transaction being reverted. * Returns `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` unless throwing. * @notice The contract address is always the message sender. A wallet/broker/auction application * MUST implement the wallet interface if it will accept safe transfers. * @param _operator The address which called `safeTransferFrom` function. * @param _from The address which previously owned the token. * @param _tokenId The NFT identifier which is being transferred. * @param _data Additional data with no specified format. * @return Returns `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`. */ function onERC721Received( address _operator, address _from, uint256 _tokenId, bytes calldata _data ) external returns(bytes4); } /** * @dev A standard for detecting smart contract interfaces. * See: https://eips.ethereum.org/EIPS/eip-165. */ interface ERC165 { /** * @dev Checks if the smart contract implements a specific interface. * @notice This function uses less than 30,000 gas. * @param _interfaceID The interface identifier, as specified in ERC-165. */ function supportsInterface( bytes4 _interfaceID ) external view returns (bool); } /** * @dev Implementation of standard to publish supported interfaces. */ contract SupportsInterface is ERC165 { /** * @dev Mapping of supported intefraces. * @notice You must not set element 0xffffffff to true. */ mapping(bytes4 => bool) internal supportedInterfaces; /** * @dev Contract constructor. */ constructor() public { supportedInterfaces[0x01ffc9a7] = true; // ERC165 } /** * @dev Function to check which interfaces are suported by this contract. * @param _interfaceID Id of the interface. */ function supportsInterface( bytes4 _interfaceID ) external view returns (bool) { return supportedInterfaces[_interfaceID]; } } /** * @dev Utility library of inline functions on addresses. */ library AddressUtils { /** * @dev Returns whether the target address is a contract. * @param _addr Address to check. * @return True if _addr is a contract, false if not. */ function isContract( address _addr ) internal view returns (bool addressCheck) { uint256 size; /** * XXX Currently there is no better way to check if there is a contract in an address than to * check the size of the code at that address. * See https://ethereum.stackexchange.com/a/14016/36603 for more details about how this works. * TODO: Check this again before the Serenity release, because all addresses will be * contracts then. */ assembly { size := extcodesize(_addr) } // solhint-disable-line addressCheck = size > 0; } } /** * @dev Optional metadata enumerable implementation for ERC-721 non-fungible token standard. */ contract NFTokenMetadataEnumerable is ERC721, ERC721Metadata, ERC721Enumerable, SupportsInterface { using SafeMath for uint256; using AddressUtils for address; /** * @dev Error constants. */ string constant ZERO_ADDRESS = "006001"; string constant NOT_VALID_NFT = "006002"; string constant NOT_OWNER_OR_OPERATOR = "006003"; string constant NOT_OWNER_APPROWED_OR_OPERATOR = "006004"; string constant NOT_ABLE_TO_RECEIVE_NFT = "006005"; string constant NFT_ALREADY_EXISTS = "006006"; string constant INVALID_INDEX = "006007"; /** * @dev Magic value of a smart contract that can recieve NFT. * Equal to: bytes4(keccak256("onERC721Received(address,address,uint256,bytes)")). */ bytes4 constant MAGIC_ON_ERC721_RECEIVED = 0x150b7a02; /** * @dev A descriptive name for a collection of NFTs. */ string internal nftName; /** * @dev An abbreviated name for NFTs. */ string internal nftSymbol; /** * @dev URI base for NFT metadata. NFT URI is made from base + NFT id. */ string public uriBase; /** * @dev Array of all NFT IDs. */ uint256[] internal tokens; /** * @dev Mapping from token ID its index in global tokens array. */ mapping(uint256 => uint256) internal idToIndex; /** * @dev Mapping from owner to list of owned NFT IDs. */ mapping(address => uint256[]) internal ownerToIds; /** * @dev Mapping from NFT ID to its index in the owner tokens list. */ mapping(uint256 => uint256) internal idToOwnerIndex; /** * @dev A mapping from NFT ID to the address that owns it. */ mapping (uint256 => address) internal idToOwner; /** * @dev Mapping from NFT ID to approved address. */ mapping (uint256 => address) internal idToApproval; /** * @dev Mapping from owner address to mapping of operator addresses. */ mapping (address => mapping (address => bool)) internal ownerToOperators; /** * @dev Emits when ownership of any NFT changes by any mechanism. This event emits when NFTs are * created (`from` == 0) and destroyed (`to` == 0). Exception: during contract creation, any * number of NFTs may be created and assigned without emitting Transfer. At the time of any * transfer, the approved address for that NFT (if any) is reset to none. * @param _from Sender of NFT (if address is zero address it indicates token creation). * @param _to Receiver of NFT (if address is zero address it indicates token destruction). * @param _tokenId The NFT that got transfered. */ event Transfer( address indexed _from, address indexed _to, uint256 indexed _tokenId ); /** * @dev This emits when the approved address for an NFT is changed or reaffirmed. The zero * address indicates there is no approved address. When a Transfer event emits, this also * indicates that the approved address for that NFT (if any) is reset to none. * @param _owner Owner of NFT. * @param _approved Address that we are approving. * @param _tokenId NFT which we are approving. */ event Approval( address indexed _owner, address indexed _approved, uint256 indexed _tokenId ); /** * @dev This emits when an operator is enabled or disabled for an owner. The operator can manage * all NFTs of the owner. * @param _owner Owner of NFT. * @param _operator Address to which we are setting operator rights. * @param _approved Status of operator rights(true if operator rights are given and false if * revoked). */ event ApprovalForAll( address indexed _owner, address indexed _operator, bool _approved ); /** * @dev Contract constructor. * @notice When implementing this contract don't forget to set nftName, nftSymbol and uriBase. */ constructor() public { supportedInterfaces[0x80ac58cd] = true; // ERC721 supportedInterfaces[0x5b5e139f] = true; // ERC721Metadata supportedInterfaces[0x780e9d63] = true; // ERC721Enumerable } /** * @dev Transfers the ownership of an NFT from one address to another address. * @notice 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 calldata _data ) external { _safeTransferFrom(_from, _to, _tokenId, _data); } /** * @dev Transfers the ownership of an NFT from one address to another address. * @notice 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 { _safeTransferFrom(_from, _to, _tokenId, ""); } /** * @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. * @notice The caller is responsible to confirm that `_to` is capable of receiving NFTs or else * they maybe be permanently lost. * @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 ) external { _transferFrom(_from, _to, _tokenId); } /** * @dev Set or reaffirm the approved address for an NFT. * @notice 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 Address to be approved for the given NFT ID. * @param _tokenId ID of the token to be approved. */ function approve( address _approved, uint256 _tokenId ) external { // can operate address tokenOwner = idToOwner[_tokenId]; require( tokenOwner == msg.sender || ownerToOperators[tokenOwner][msg.sender], NOT_OWNER_OR_OPERATOR ); idToApproval[_tokenId] = _approved; emit Approval(tokenOwner, _approved, _tokenId); } /** * @dev Enables or disables approval for a third party ("operator") to manage all of * `msg.sender`'s assets. It also emits the ApprovalForAll event. * @notice This works even if sender doesn't own any tokens at the time. * @param _operator Address to add to the set of authorized operators. * @param _approved True if the operators is approved, false to revoke approval. */ function setApprovalForAll( address _operator, bool _approved ) external { ownerToOperators[msg.sender][_operator] = _approved; emit ApprovalForAll(msg.sender, _operator, _approved); } /** * @dev Returns the number of NFTs owned by `_owner`. NFTs assigned to the zero address are * considered invalid, and this function throws for queries about the zero address. * @param _owner Address for whom to query the balance. * @return Balance of _owner. */ function balanceOf( address _owner ) external view returns (uint256) { require(_owner != address(0), ZERO_ADDRESS); return ownerToIds[_owner].length; } /** * @dev Returns the address of the owner of the NFT. NFTs assigned to zero address are considered * invalid, and queries about them do throw. * @param _tokenId The identifier for an NFT. * @return Address of _tokenId owner. */ function ownerOf( uint256 _tokenId ) external view returns (address _owner) { _owner = idToOwner[_tokenId]; require(_owner != address(0), NOT_VALID_NFT); } /** * @dev Get the approved address for a single NFT. * @notice Throws if `_tokenId` is not a valid NFT. * @param _tokenId ID of the NFT to query the approval of. * @return Address that _tokenId is approved for. */ function getApproved( uint256 _tokenId ) external view returns (address) { require(idToOwner[_tokenId] != address(0), NOT_VALID_NFT); return idToApproval[_tokenId]; } /** * @dev Checks if `_operator` is an approved operator for `_owner`. * @param _owner The address that owns the NFTs. * @param _operator The address that acts on behalf of the owner. * @return True if approved for all, false otherwise. */ function isApprovedForAll( address _owner, address _operator ) external view returns (bool) { return ownerToOperators[_owner][_operator]; } /** * @dev Returns the count of all existing NFTs. * @return Total supply of NFTs. */ function totalSupply() external view returns (uint256) { return tokens.length; } /** * @dev Returns NFT ID by its index. * @param _index A counter less than `totalSupply()`. * @return Token id. */ function tokenByIndex( uint256 _index ) external view returns (uint256) { require(_index < tokens.length, INVALID_INDEX); return tokens[_index]; } /** * @dev returns the n-th NFT ID from a list of owner's tokens. * @param _owner Token owner's address. * @param _index Index number representing n-th token in owner's list of tokens. * @return Token id. */ function tokenOfOwnerByIndex( address _owner, uint256 _index ) external view returns (uint256) { require(_index < ownerToIds[_owner].length, INVALID_INDEX); return ownerToIds[_owner][_index]; } /** * @dev Returns a descriptive name for a collection of NFTs. * @return Representing name. */ function name() external view returns (string memory _name) { _name = nftName; } /** * @dev Returns an abbreviated name for NFTs. * @return Representing symbol. */ function symbol() external view returns (string memory _symbol) { _symbol = nftSymbol; } /** * @notice A distinct Uniform Resource Identifier (URI) for a given asset. * @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC 3986. The URI may point * to a JSON file that conforms to the "ERC721 Metadata JSON Schema". * @param _tokenId Id for which we want URI. * @return URI of _tokenId. */ function tokenURI( uint256 _tokenId ) external view returns (string memory) { require(idToOwner[_tokenId] != address(0), NOT_VALID_NFT); if (bytes(uriBase).length > 0) { return string(abi.encodePacked(uriBase, _uint2str(_tokenId))); } return ""; } /** * @dev Set a distinct URI (RFC 3986) base for all nfts. * @notice this is a internal function which should be called from user-implemented external * function. Its purpose is to show and properly initialize data structures when using this * implementation. * @param _uriBase String representing RFC 3986 URI base. */ function _setUriBase( string memory _uriBase ) internal { uriBase = _uriBase; } /** * @dev Creates a new NFT. * @notice This is a private function which should be called from user-implemented external * function. Its purpose is to show and properly initialize data structures when using this * implementation. * @param _to The address that will own the created NFT. * @param _tokenId of the NFT to be created by the msg.sender. */ function _create( address _to, uint256 _tokenId ) internal { require(_to != address(0), ZERO_ADDRESS); require(idToOwner[_tokenId] == address(0), NFT_ALREADY_EXISTS); // add NFT idToOwner[_tokenId] = _to; uint256 length = ownerToIds[_to].push(_tokenId); idToOwnerIndex[_tokenId] = length - 1; // add to tokens array length = tokens.push(_tokenId); idToIndex[_tokenId] = length - 1; emit Transfer(address(0), _to, _tokenId); } /** * @dev Destroys a NFT. * @notice This is a private function which should be called from user-implemented external * destroy function. Its purpose is to show and properly initialize data structures when using this * implementation. * @param _tokenId ID of the NFT to be destroyed. */ function _destroy( uint256 _tokenId ) internal { // valid NFT address owner = idToOwner[_tokenId]; require(owner != address(0), NOT_VALID_NFT); // clear approval if (idToApproval[_tokenId] != address(0)) { delete idToApproval[_tokenId]; } // remove NFT assert(ownerToIds[owner].length > 0); uint256 tokenToRemoveIndex = idToOwnerIndex[_tokenId]; uint256 lastTokenIndex = ownerToIds[owner].length - 1; uint256 lastToken; if (lastTokenIndex != tokenToRemoveIndex) { lastToken = ownerToIds[owner][lastTokenIndex]; ownerToIds[owner][tokenToRemoveIndex] = lastToken; idToOwnerIndex[lastToken] = tokenToRemoveIndex; } delete idToOwner[_tokenId]; delete idToOwnerIndex[_tokenId]; ownerToIds[owner].length--; // remove from tokens array assert(tokens.length > 0); uint256 tokenIndex = idToIndex[_tokenId]; lastTokenIndex = tokens.length - 1; lastToken = tokens[lastTokenIndex]; tokens[tokenIndex] = lastToken; tokens.length--; // Consider adding a conditional check for the last token in order to save GAS. idToIndex[lastToken] = tokenIndex; idToIndex[_tokenId] = 0; emit Transfer(owner, address(0), _tokenId); } /** * @dev Helper methods that actually does the transfer. * @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 ) internal { // valid NFT require(_from != address(0), ZERO_ADDRESS); require(idToOwner[_tokenId] == _from, NOT_VALID_NFT); require(_to != address(0), ZERO_ADDRESS); // can transfer require( _from == msg.sender || idToApproval[_tokenId] == msg.sender || ownerToOperators[_from][msg.sender], NOT_OWNER_APPROWED_OR_OPERATOR ); // clear approval if (idToApproval[_tokenId] != address(0)) { delete idToApproval[_tokenId]; } // remove NFT assert(ownerToIds[_from].length > 0); uint256 tokenToRemoveIndex = idToOwnerIndex[_tokenId]; uint256 lastTokenIndex = ownerToIds[_from].length - 1; if (lastTokenIndex != tokenToRemoveIndex) { uint256 lastToken = ownerToIds[_from][lastTokenIndex]; ownerToIds[_from][tokenToRemoveIndex] = lastToken; idToOwnerIndex[lastToken] = tokenToRemoveIndex; } ownerToIds[_from].length--; // add NFT idToOwner[_tokenId] = _to; uint256 length = ownerToIds[_to].push(_tokenId); idToOwnerIndex[_tokenId] = length - 1; emit Transfer(_from, _to, _tokenId); } /** * @dev Helper function that actually does the safeTransfer. * @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 memory _data ) internal { if (_to.isContract()) { require( ERC721TokenReceiver(_to) .onERC721Received(msg.sender, _from, _tokenId, _data) == MAGIC_ON_ERC721_RECEIVED, NOT_ABLE_TO_RECEIVE_NFT ); } _transferFrom(_from, _to, _tokenId); } /** * @dev Helper function that changes uint to string representation. * @return String representation. */ function _uint2str( uint256 _i ) internal pure returns (string memory str) { if (_i == 0) { return "0"; } uint256 j = _i; uint256 length; while (j != 0) { length++; j /= 10; } bytes memory bstr = new bytes(length); uint256 k = length - 1; j = _i; while (j != 0) { bstr[k--] = byte(uint8(48 + j % 10)); j /= 10; } str = string(bstr); } } /** * @dev Xcert implementation. */ contract XcertToken is Xcert, XcertBurnable, XcertMutable, XcertPausable, XcertRevokable, NFTokenMetadataEnumerable, Abilitable { /** * @dev List of abilities (gathered from all extensions): */ uint8 constant ABILITY_CREATE_ASSET = 2; uint8 constant ABILITY_REVOKE_ASSET = 4; uint8 constant ABILITY_TOGGLE_TRANSFERS = 8; uint8 constant ABILITY_UPDATE_ASSET_IMPRINT = 16; /// ABILITY_ALLOW_CREATE_ASSET = 32 - A specific ability that is bounded to atomic orders. /// When creating a new Xcert trough `OrderGateway`, the order maker has to have this ability. uint8 constant ABILITY_UPDATE_URI_BASE = 64; /** * @dev List of capabilities (supportInterface bytes4 representations). */ bytes4 constant MUTABLE = 0xbda0e852; bytes4 constant BURNABLE = 0x9d118770; bytes4 constant PAUSABLE = 0xbedb86fb; bytes4 constant REVOKABLE = 0x20c5429b; /** * @dev Error constants. */ string constant CAPABILITY_NOT_SUPPORTED = "007001"; string constant TRANSFERS_DISABLED = "007002"; string constant NOT_VALID_XCERT = "007003"; string constant NOT_OWNER_OR_OPERATOR = "007004"; /** * @dev This emits when ability of beeing able to transfer Xcerts changes (paused/unpaused). */ event IsPaused(bool isPaused); /** * @dev Emits when imprint of a token is changed. * @param _tokenId Id of the Xcert. * @param _imprint Cryptographic asset imprint. */ event TokenImprintUpdate( uint256 indexed _tokenId, bytes32 _imprint ); /** * @dev Unique ID which determines each Xcert smart contract type by its JSON convention. * @notice Calculated as keccak256(jsonSchema). */ bytes32 internal nftSchemaId; /** * @dev Maps NFT ID to imprint. */ mapping (uint256 => bytes32) internal idToImprint; /** * @dev Maps address to authorization of contract. */ mapping (address => bool) internal addressToAuthorized; /** * @dev Are Xcerts transfers paused (can be performed) or not. */ bool public isPaused; /** * @dev Contract constructor. * @notice When implementing this contract don't forget to set nftSchemaId, nftName, nftSymbol * and uriBase. */ constructor() public { supportedInterfaces[0xe08725ee] = true; // Xcert } /** * @dev Creates a new Xcert. * @param _to The address that will own the created Xcert. * @param _id The Xcert to be created by the msg.sender. * @param _imprint Cryptographic asset imprint. */ function create( address _to, uint256 _id, bytes32 _imprint ) external hasAbilities(ABILITY_CREATE_ASSET) { super._create(_to, _id); idToImprint[_id] = _imprint; } /** * @dev Change URI base. * @param _uriBase New uriBase. */ function setUriBase( string calldata _uriBase ) external hasAbilities(ABILITY_UPDATE_URI_BASE) { super._setUriBase(_uriBase); } /** * @dev Revokes(destroys) a specified Xcert. Reverts if not called from contract owner or * authorized address. * @param _tokenId Id of the Xcert we want to destroy. */ function revoke( uint256 _tokenId ) external hasAbilities(ABILITY_REVOKE_ASSET) { require(supportedInterfaces[REVOKABLE], CAPABILITY_NOT_SUPPORTED); super._destroy(_tokenId); delete idToImprint[_tokenId]; } /** * @dev Sets if Xcerts transfers are paused (can be performed) or not. * @param _isPaused Pause status. */ function setPause( bool _isPaused ) external hasAbilities(ABILITY_TOGGLE_TRANSFERS) { require(supportedInterfaces[PAUSABLE], CAPABILITY_NOT_SUPPORTED); isPaused = _isPaused; emit IsPaused(_isPaused); } /** * @dev Updates Xcert imprint. * @param _tokenId Id of the Xcert. * @param _imprint New imprint. */ function updateTokenImprint( uint256 _tokenId, bytes32 _imprint ) external hasAbilities(ABILITY_UPDATE_ASSET_IMPRINT) { require(supportedInterfaces[MUTABLE], CAPABILITY_NOT_SUPPORTED); require(idToOwner[_tokenId] != address(0), NOT_VALID_XCERT); idToImprint[_tokenId] = _imprint; emit TokenImprintUpdate(_tokenId, _imprint); } /** * @dev Destroys a specified Xcert. Reverts if not called from Xcert owner or operator. * @param _tokenId Id of the Xcert we want to destroy. */ function destroy( uint256 _tokenId ) external { require(supportedInterfaces[BURNABLE], CAPABILITY_NOT_SUPPORTED); address tokenOwner = idToOwner[_tokenId]; super._destroy(_tokenId); require( tokenOwner == msg.sender || ownerToOperators[tokenOwner][msg.sender], NOT_OWNER_OR_OPERATOR ); delete idToImprint[_tokenId]; } /** * @dev Returns a bytes32 of sha256 of json schema representing 0xcert Protocol convention. * @return Schema id. */ function schemaId() external view returns (bytes32 _schemaId) { _schemaId = nftSchemaId; } /** * @dev Returns imprint for Xcert. * @param _tokenId Id of the Xcert. * @return Token imprint. */ function tokenImprint( uint256 _tokenId ) external view returns(bytes32 imprint) { imprint = idToImprint[_tokenId]; } /** * @dev Helper methods that actually does the transfer. * @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 ) internal { /** * if (supportedInterfaces[0xbedb86fb]) * { * require(!isPaused, TRANSFERS_DISABLED); * } * There is no need to check for pausable capability here since by using logical deduction we * can say based on code above that: * !supportedInterfaces[0xbedb86fb] => !isPaused * isPaused => supportedInterfaces[0xbedb86fb] * (supportedInterfaces[0xbedb86fb] ∧ isPaused) <=> isPaused. * This saves 200 gas. */ require(!isPaused, TRANSFERS_DISABLED); super._transferFrom(_from, _to, _tokenId); } } /** * @title XcertCreateProxy - creates a token on behalf of contracts that have been approved via * decentralized governance. */ contract XcertCreateProxy is Abilitable { /** * @dev List of abilities: * 2 - Ability to execute create. */ uint8 constant ABILITY_TO_EXECUTE = 2; /** * @dev Creates a new NFT. * @param _xcert Address of the Xcert contract on which the creation will be perfomed. * @param _to The address that will own the created NFT. * @param _id The NFT to be created by the msg.sender. * @param _imprint Cryptographic asset imprint. */ function create( address _xcert, address _to, uint256 _id, bytes32 _imprint ) external hasAbilities(ABILITY_TO_EXECUTE) { Xcert(_xcert).create(_to, _id, _imprint); } } pragma experimental ABIEncoderV2; /** * @dev Decentralize exchange, creating, updating and other actions for fundgible and non-fundgible * tokens powered by atomic swaps. */ contract OrderGateway is Abilitable { /** * @dev List of abilities: * 2 - Ability to set proxies. */ uint8 constant ABILITY_TO_SET_PROXIES = 2; /** * @dev Xcert abilities. */ uint8 constant ABILITY_ALLOW_CREATE_ASSET = 32; /** * @dev Error constants. */ string constant INVALID_SIGNATURE_KIND = "015001"; string constant INVALID_PROXY = "015002"; string constant TAKER_NOT_EQUAL_TO_SENDER = "015003"; string constant SENDER_NOT_TAKER_OR_MAKER = "015004"; string constant CLAIM_EXPIRED = "015005"; string constant INVALID_SIGNATURE = "015006"; string constant ORDER_CANCELED = "015007"; string constant ORDER_ALREADY_PERFORMED = "015008"; string constant MAKER_NOT_EQUAL_TO_SENDER = "015009"; string constant SIGNER_NOT_AUTHORIZED = "015010"; /** * @dev Enum of available signature kinds. * @param eth_sign Signature using eth sign. * @param trezor Signature from Trezor hardware wallet. * It differs from web3.eth_sign in the encoding of message length * (Bitcoin varint encoding vs ascii-decimal, the latter is not * self-terminating which leads to ambiguities). * See also: * https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer * https://github.com/trezor/trezor-mcu/blob/master/firmware/ethereum.c#L602 * https://github.com/trezor/trezor-mcu/blob/master/firmware/crypto.c#L36 * @param eip721 Signature using eip721. */ enum SignatureKind { eth_sign, trezor, eip712 } /** * Enum of available action kinds. */ enum ActionKind { create, transfer } /** * @dev Structure representing what to send and where. * @param kind Enum representing action kind. * @param proxy Id representing approved proxy address. * @param token Address of the token we are sending. * @param param1 Address of the sender or imprint. * @param to Address of the receiver. * @param value Amount of ERC20 or ID of ERC721. */ struct ActionData { ActionKind kind; uint32 proxy; address token; bytes32 param1; address to; uint256 value; } /** * @dev Structure representing the signature parts. * @param r ECDSA signature parameter r. * @param s ECDSA signature parameter s. * @param v ECDSA signature parameter v. * @param kind Type of signature. */ struct SignatureData { bytes32 r; bytes32 s; uint8 v; SignatureKind kind; } /** * @dev Structure representing the data needed to do the order. * @param maker Address of the one that made the claim. * @param taker Address of the one that is executing the claim. * @param actions Data of all the actions that should accure it this order. * @param signature Data from the signed claim. * @param seed Arbitrary number to facilitate uniqueness of the order's hash. Usually timestamp. * @param expiration Timestamp of when the claim expires. 0 if indefinet. */ struct OrderData { address maker; address taker; ActionData[] actions; uint256 seed; uint256 expiration; } /** * @dev Valid proxy contract addresses. */ address[] public proxies; /** * @dev Mapping of all cancelled orders. */ mapping(bytes32 => bool) public orderCancelled; /** * @dev Mapping of all performed orders. */ mapping(bytes32 => bool) public orderPerformed; /** * @dev This event emmits when tokens change ownership. */ event Perform( address indexed _maker, address indexed _taker, bytes32 _claim ); /** * @dev This event emmits when transfer order is cancelled. */ event Cancel( address indexed _maker, address indexed _taker, bytes32 _claim ); /** * @dev This event emmits when proxy address is changed.. */ event ProxyChange( uint256 indexed _index, address _proxy ); /** * @dev Adds a verified proxy address. * @notice Can be done through a multisig wallet in the future. * @param _proxy Proxy address. */ function addProxy( address _proxy ) external hasAbilities(ABILITY_TO_SET_PROXIES) { uint256 length = proxies.push(_proxy); emit ProxyChange(length - 1, _proxy); } /** * @dev Removes a proxy address. * @notice Can be done through a multisig wallet in the future. * @param _index Index of proxy we are removing. */ function removeProxy( uint256 _index ) external hasAbilities(ABILITY_TO_SET_PROXIES) { proxies[_index] = address(0); emit ProxyChange(_index, address(0)); } /** * @dev Performs the atomic swap that can exchange, create, update and do other actions for * fungible and non-fungible tokens. * @param _data Data required to make the order. * @param _signature Data from the signature. */ function perform( OrderData memory _data, SignatureData memory _signature ) public { require(_data.taker == msg.sender, TAKER_NOT_EQUAL_TO_SENDER); require(_data.expiration >= now, CLAIM_EXPIRED); bytes32 claim = getOrderDataClaim(_data); require( isValidSignature( _data.maker, claim, _signature ), INVALID_SIGNATURE ); require(!orderCancelled[claim], ORDER_CANCELED); require(!orderPerformed[claim], ORDER_ALREADY_PERFORMED); orderPerformed[claim] = true; _doActions(_data); emit Perform( _data.maker, _data.taker, claim ); } /** * @dev Cancels order. * @notice You can cancel the same order multiple times. There is no check for whether the order * was already canceled due to gas optimization. You should either check orderCancelled variable * or listen to Cancel event if you want to check if an order is already canceled. * @param _data Data of order to cancel. */ function cancel( OrderData memory _data ) public { require(_data.maker == msg.sender, MAKER_NOT_EQUAL_TO_SENDER); bytes32 claim = getOrderDataClaim(_data); require(!orderPerformed[claim], ORDER_ALREADY_PERFORMED); orderCancelled[claim] = true; emit Cancel( _data.maker, _data.taker, claim ); } /** * @dev Calculates keccak-256 hash of OrderData from parameters. * @param _orderData Data needed for atomic swap. * @return keccak-hash of order data. */ function getOrderDataClaim( OrderData memory _orderData ) public view returns (bytes32) { bytes32 temp = 0x0; for(uint256 i = 0; i < _orderData.actions.length; i++) { temp = keccak256( abi.encodePacked( temp, _orderData.actions[i].kind, _orderData.actions[i].proxy, _orderData.actions[i].token, _orderData.actions[i].param1, _orderData.actions[i].to, _orderData.actions[i].value ) ); } return keccak256( abi.encodePacked( address(this), _orderData.maker, _orderData.taker, temp, _orderData.seed, _orderData.expiration ) ); } /** * @dev Verifies if claim signature is valid. * @param _signer address of signer. * @param _claim Signed Keccak-256 hash. * @param _signature Signature data. */ function isValidSignature( address _signer, bytes32 _claim, SignatureData memory _signature ) public pure returns (bool) { if (_signature.kind == SignatureKind.eth_sign) { return _signer == ecrecover( keccak256( abi.encodePacked( "\x19Ethereum Signed Message:\n32", _claim ) ), _signature.v, _signature.r, _signature.s ); } else if (_signature.kind == SignatureKind.trezor) { return _signer == ecrecover( keccak256( abi.encodePacked( "\x19Ethereum Signed Message:\n\x20", _claim ) ), _signature.v, _signature.r, _signature.s ); } else if (_signature.kind == SignatureKind.eip712) { return _signer == ecrecover( _claim, _signature.v, _signature.r, _signature.s ); } revert(INVALID_SIGNATURE_KIND); } /** * @dev Helper function that makes transfes. * @param _order Data needed for order. */ function _doActions( OrderData memory _order ) private { for(uint256 i = 0; i < _order.actions.length; i++) { require( proxies[_order.actions[i].proxy] != address(0), INVALID_PROXY ); if (_order.actions[i].kind == ActionKind.create) { require( Abilitable(_order.actions[i].token).isAble(_order.maker, ABILITY_ALLOW_CREATE_ASSET), SIGNER_NOT_AUTHORIZED ); XcertCreateProxy(proxies[_order.actions[i].proxy]).create( _order.actions[i].token, _order.actions[i].to, _order.actions[i].value, _order.actions[i].param1 ); } else if (_order.actions[i].kind == ActionKind.transfer) { address from = address(uint160(bytes20(_order.actions[i].param1))); require( from == _order.maker || from == _order.taker, SENDER_NOT_TAKER_OR_MAKER ); Proxy(proxies[_order.actions[i].proxy]).execute( _order.actions[i].token, from, _order.actions[i].to, _order.actions[i].value ); } } } }