ETH Price: $2,021.59 (+4.04%)

Transaction Decoder

Block:
9534462 at Feb-22-2020 05:20:42 PM +UTC
Transaction Fee:
0.000581966 ETH $1.18
Gas Used:
290,983 Gas / 2 Gwei

Emitted Events:

155 MANAToken.Transfer( from=[Receiver] CampaignBank, to=[Sender] 0x625e92018352fa6c8bafcd2e4bb4e4532bfa2ae6, value=10000000000000000000 )
156 CampaignBank.Rewarded( targetContract=MANAToken, hashedSig=38BC0C856CAFC9C18FFC1DB4F700B192F35FC0D6ADEB4ABE0EFBA41DB6127152, payload=0xA9059CBB000000000000000000000000625E92018352FA6C8BAFCD2E4BB4E4532BFA2AE60000000000000000000000000000000000000000000000008AC7230489E80000, signedTimestamp=1582475768, signature=0x367D46FAF118F30C8CC2EC158528A4B3F4E7641FBBBE97DBB305E2DDFE45129A40160BA6F195198AEA4236EDB0C28B98160FDFE909C1374627B75536BA5BCCFE1B, sender=[Sender] 0x625e92018352fa6c8bafcd2e4bb4e4532bfa2ae6 )
157 ERC721Collection.Transfer( from=0x00000000...000000000, to=[Sender] 0x625e92018352fa6c8bafcd2e4bb4e4532bfa2ae6, tokenId=7082 )
158 ERC721Collection.Issue( _beneficiary=[Sender] 0x625e92018352fa6c8bafcd2e4bb4e4532bfa2ae6, _tokenId=7082, _wearableIdKey=3FBB6561A88BE6E078D07E03C5CC159FA9066F7E7CF5E89E9EAF2566D3EEDF35, _wearableId=mana_hat, _issuedId=363 )
159 CampaignBank.Rewarded( targetContract=ERC721Collection, hashedSig=CFEC71F38F0F6E669CB3E50D1B9128C2210C2744FA33DE7B7331FCA1A735ADA7, payload=0x5669C94F000000000000000000000000625E92018352FA6C8BAFCD2E4BB4E4532BFA2AE6000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000086D616E615F686174000000000000000000000000000000000000000000000000, signedTimestamp=1582475768, signature=0x4F6286838202F4F8700FA8ED1D8E69B9A052C76B167F743F4FA3CBC328C20DDA15B8FF55663D3DFACAC97D85BB5356E0D43622F5CD115D2CFA2FD66C135B7F4F1B, sender=[Sender] 0x625e92018352fa6c8bafcd2e4bb4e4532bfa2ae6 )

Account State Difference:

  Address   Before After State Difference Code
0x0F5D2fB2...8908cC942
0x625e9201...32BFa2ae6
0.002482675614531226 Eth
Nonce: 5
0.001900709614531226 Eth
Nonce: 6
0.000581966
(MiningPoolHub: Old Address)
2,711.298746910358718441 Eth2,711.299328876358718441 Eth0.000581966
0xD35147BE...E97ED6818
0xF26D144B...2C5529e62

Execution Trace

CampaignBank.claimManyRewards( targetContract=[0x0F5D2fB29fb7d3CFeE444a200298f468908cC942, 0xD35147BE6401dcb20811f2104c33dE8E97ED6818], payload=[qQWcuwAAAAAAAAAAAAAAAGJekgGDUvpsi6/NLku05FMr+irmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiscjBInoAAA=, VmnJTwAAAAAAAAAAAAAAAGJekgGDUvpsi6/NLku05FMr+irmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACG1hbmFfaGF0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA], expirationTimestamp=[1582475768, 1582475768], signature=[Nn1G+vEY8wyMwuwVhSiks/TnZB+7vpfbswXi3f5FEppAFgum8ZUZiupCNu2wwouYFg/f6QnBN0Ynt1U2ulvM/hs=, T2KGg4IC9PhwD6jtHY5puaBSx2sWf3Q/T6PLwyjCDdoVuP9VZj09+srJfYW7U1bg1DYi9c0RXSz6L9ZsE1t/Txs=] )
  • Null: 0x000...001.18e6f298( )
  • MANAToken.transfer( _to=0x625e92018352FA6C8BaFCd2E4BB4e4532BFa2ae6, _value=10000000000000000000 ) => ( True )
  • Null: 0x000...001.9fa8b7ec( )
  • ERC721Collection.issueToken( _beneficiary=0x625e92018352FA6C8BaFCd2E4BB4e4532BFa2ae6, _wearableId=mana_hat )
    • ERC721Collection.STATICCALL( )
      File 1 of 3: CampaignBank
      pragma solidity ^0.5.0;
      pragma experimental ABIEncoderV2;
      
      /**
       * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
       *
       * These functions can be used to verify that a message was signed by the holder
       * of the private keys of a given address.
       */
      library ECDSA {
          /**
           * @dev Returns the address that signed a hashed message (`hash`) with
           * `signature`. This address can then be used for verification purposes.
           *
           * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
           * this function rejects them by requiring the `s` value to be in the lower
           * half order, and the `v` value to be either 27 or 28.
           *
           * NOTE: This call _does not revert_ if the signature is invalid, or
           * if the signer is otherwise unable to be retrieved. In those scenarios,
           * the zero address is returned.
           *
           * IMPORTANT: `hash` _must_ be the result of a hash operation for the
           * verification to be secure: it is possible to craft signatures that
           * recover to arbitrary addresses for non-hashed data. A safe way to ensure
           * this is by receiving a hash of the original message (which may otherwise
           * be too long), and then calling {toEthSignedMessageHash} on it.
           */
          function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
              // Check the signature length
              if (signature.length != 65) {
                  return (address(0));
              }
      
              // Divide the signature in r, s and v variables
              bytes32 r;
              bytes32 s;
              uint8 v;
      
              // ecrecover takes the signature parameters, and the only way to get them
              // currently is to use assembly.
              // solhint-disable-next-line no-inline-assembly
              assembly {
                  r := mload(add(signature, 0x20))
                  s := mload(add(signature, 0x40))
                  v := byte(0, mload(add(signature, 0x60)))
              }
      
              // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
              // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
              // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
              // signatures from current libraries generate a unique signature with an s-value in the lower half order.
              //
              // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
              // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
              // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
              // these malleable signatures as well.
              if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                  return address(0);
              }
      
              if (v != 27 && v != 28) {
                  return address(0);
              }
      
              // If the signature is valid (and not malleable), return the signer address
              return ecrecover(hash, v, r, s);
          }
      
          /**
           * @dev Returns an Ethereum Signed Message, created from a `hash`. This
           * replicates the behavior of the
           * https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`]
           * JSON-RPC method.
           *
           * See {recover}.
           */
          function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
              // 32 is the length in bytes of hash,
              // enforced by the type signature above
              return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
          }
      }
      
      
      /**
       * @dev Contract module which provides a basic access control mechanism, where
       * there is an account (an owner) that can be granted exclusive access to
       * specific functions.
       *
       * This module is used through inheritance. It will make available the modifier
       * `onlyOwner`, which can be applied to your functions to restrict their use to
       * the owner.
       */
      contract Ownable {
          address private _owner;
      
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
      
          /**
           * @dev Initializes the contract setting the deployer as the initial owner.
           */
          constructor () internal {
              _owner = msg.sender;
              emit OwnershipTransferred(address(0), _owner);
          }
      
          /**
           * @dev Returns the address of the current owner.
           */
          function owner() public view returns (address) {
              return _owner;
          }
      
          /**
           * @dev Throws if called by any account other than the owner.
           */
          modifier onlyOwner() {
              require(isOwner(), "Ownable: caller is not the owner");
              _;
          }
      
          /**
           * @dev Returns true if the caller is the current owner.
           */
          function isOwner() public view returns (bool) {
              return msg.sender == _owner;
          }
      
          /**
           * @dev Leaves the contract without owner. It will not be possible to call
           * `onlyOwner` functions anymore. Can only be called by the current owner.
           *
           * NOTE: Renouncing ownership will leave the contract without an owner,
           * thereby removing any functionality that is only available to the owner.
           */
          function renounceOwnership() public onlyOwner {
              emit OwnershipTransferred(_owner, address(0));
              _owner = address(0);
          }
      
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           * Can only be called by the current owner.
           */
          function transferOwnership(address newOwner) public onlyOwner {
              _transferOwnership(newOwner);
          }
      
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           */
          function _transferOwnership(address newOwner) internal {
              require(newOwner != address(0), "Ownable: new owner is the zero address");
              emit OwnershipTransferred(_owner, newOwner);
              _owner = newOwner;
          }
      }
      
      contract CampaignBank is Ownable {
          using ECDSA for bytes32;
      
          mapping(address => bool) public trustedSigner;
          mapping(bytes32 => bool) public alreadySent;
      
          event RegisteredSigner(
              address indexed sender,
              address indexed signer,
              uint256 indexed date
          );
          event RemovedSigner(
              address indexed sender,
              address indexed signer,
              uint256 indexed date
          );
          event Rewarded(
              address indexed targetContract,
              bytes32 indexed hashedSig,
              bytes payload,
              uint256 signedTimestamp,
              bytes signature,
              address sender
          );
      
          constructor() public Ownable() {}
      
          function registerTrustedSigner(address target, bool allowed)
              public
              onlyOwner
          {
              if (allowed && !trustedSigner[target]) {
                  trustedSigner[target] = true;
                  emit RegisteredSigner(msg.sender, target, block.timestamp);
              } else if (!allowed && trustedSigner[target]) {
                  trustedSigner[target] = false;
                  emit RemovedSigner(msg.sender, target, block.timestamp);
              }
          }
      
          event TransactionRelayed(
              address indexed sender,
              address indexed targetContract,
              bytes payload,
              uint256 value,
              bytes signature
          );
      
          function claimManyRewards(
              address[] memory targetContract,
              bytes[] memory payload,
              uint256[] memory expirationTimestamp,
              bytes[] memory signature
          ) public {
              require(
                  targetContract.length == payload.length,
                  "Arrays should be of the same size"
              );
              require(
                  targetContract.length == expirationTimestamp.length,
                  "Arrays should be of the same size"
              );
              require(
                  targetContract.length == signature.length,
                  "Arrays should be of the same size"
              );
              uint256 length = targetContract.length;
      
              for (uint256 i = 0; i < length; i++) {
                  if (
                      !claimReward(
                          targetContract[i],
                          payload[i],
                          expirationTimestamp[i],
                          signature[i]
                      )
                  ) {
                      revert("Transaction failed");
                  }
              }
          }
      
          function claimReward(
              address targetContract,
              bytes memory payload,
              uint256 expirationTimestamp,
              bytes memory signature
          ) public returns (bool) {
              require(block.timestamp < expirationTimestamp, "Signature too old");
      
              bytes memory blob = abi.encode(
                  targetContract,
                  payload,
                  expirationTimestamp
              );
              bytes32 signed = keccak256(blob);
              bytes32 verify = signed.toEthSignedMessageHash();
              require(!alreadySent[signed], "Already sent!");
      
              require(
                  trustedSigner[verify.recover(signature)],
                  "Invalid signature provided"
              );
      
              alreadySent[signed] = true;
      
              bool result;
              (result,) = targetContract.call(payload);
              if (!result) {
                  revert("Failed call");
              }
      
              emit Rewarded(
                  targetContract,
                  signed,
                  payload,
                  expirationTimestamp,
                  signature,
                  msg.sender
              );
      
              return true;
          }
      
          function halt() public onlyOwner {
              selfdestruct(address(uint256(owner())));
          }
      }

      File 2 of 3: MANAToken
      pragma solidity ^0.4.11;
      
      contract ERC20Basic {
        uint256 public totalSupply;
        function balanceOf(address who) constant returns (uint256);
        function transfer(address to, uint256 value) returns (bool);
        event Transfer(address indexed from, address indexed to, uint256 value);
      }
      
      contract Ownable {
        address public owner;
      
      
        /**
         * @dev The Ownable constructor sets the original `owner` of the contract to the sender
         * account.
         */
        function Ownable() {
          owner = msg.sender;
        }
      
      
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
          require(msg.sender == owner);
          _;
        }
      
      
        /**
         * @dev Allows the current owner to transfer control of the contract to a newOwner.
         * @param newOwner The address to transfer ownership to.
         */
        function transferOwnership(address newOwner) onlyOwner {
          if (newOwner != address(0)) {
            owner = newOwner;
          }
        }
      
      }
      
      contract Pausable is Ownable {
        event Pause();
        event Unpause();
      
        bool public paused = false;
      
      
        /**
         * @dev modifier to allow actions only when the contract IS paused
         */
        modifier whenNotPaused() {
          require(!paused);
          _;
        }
      
        /**
         * @dev modifier to allow actions only when the contract IS NOT paused
         */
        modifier whenPaused {
          require(paused);
          _;
        }
      
        /**
         * @dev called by the owner to pause, triggers stopped state
         */
        function pause() onlyOwner whenNotPaused returns (bool) {
          paused = true;
          Pause();
          return true;
        }
      
        /**
         * @dev called by the owner to unpause, returns to normal state
         */
        function unpause() onlyOwner whenPaused returns (bool) {
          paused = false;
          Unpause();
          return true;
        }
      }
      
      contract ERC20 is ERC20Basic {
        function allowance(address owner, address spender) constant returns (uint256);
        function transferFrom(address from, address to, uint256 value) returns (bool);
        function approve(address spender, uint256 value) returns (bool);
        event Approval(address indexed owner, address indexed spender, uint256 value);
      }
      
      library SafeMath {
        function mul(uint256 a, uint256 b) internal constant returns (uint256) {
          uint256 c = a * b;
          assert(a == 0 || c / a == b);
          return c;
        }
      
        function div(uint256 a, uint256 b) internal constant returns (uint256) {
          // assert(b > 0); // Solidity automatically throws when dividing by 0
          uint256 c = a / b;
          // assert(a == b * c + a % b); // There is no case in which this doesn't hold
          return c;
        }
      
        function sub(uint256 a, uint256 b) internal constant returns (uint256) {
          assert(b <= a);
          return a - b;
        }
      
        function add(uint256 a, uint256 b) internal constant returns (uint256) {
          uint256 c = a + b;
          assert(c >= a);
          return c;
        }
      }
      
      contract BasicToken is ERC20Basic {
        using SafeMath for uint256;
      
        mapping(address => uint256) balances;
      
        /**
        * @dev transfer token for a specified address
        * @param _to The address to transfer to.
        * @param _value The amount to be transferred.
        */
        function transfer(address _to, uint256 _value) returns (bool) {
          balances[msg.sender] = balances[msg.sender].sub(_value);
          balances[_to] = balances[_to].add(_value);
          Transfer(msg.sender, _to, _value);
          return true;
        }
      
        /**
        * @dev Gets the balance of the specified address.
        * @param _owner The address to query the the balance of. 
        * @return An uint256 representing the amount owned by the passed address.
        */
        function balanceOf(address _owner) constant returns (uint256 balance) {
          return balances[_owner];
        }
      
      }
      
      contract StandardToken is ERC20, BasicToken {
      
        mapping (address => mapping (address => uint256)) allowed;
      
      
        /**
         * @dev Transfer tokens from one address to another
         * @param _from address The address which you want to send tokens from
         * @param _to address The address which you want to transfer to
         * @param _value uint256 the amout of tokens to be transfered
         */
        function transferFrom(address _from, address _to, uint256 _value) returns (bool) {
          var _allowance = allowed[_from][msg.sender];
      
          // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
          // require (_value <= _allowance);
      
          balances[_to] = balances[_to].add(_value);
          balances[_from] = balances[_from].sub(_value);
          allowed[_from][msg.sender] = _allowance.sub(_value);
          Transfer(_from, _to, _value);
          return true;
        }
      
        /**
         * @dev Aprove the passed address to spend the specified amount of tokens on behalf of msg.sender.
         * @param _spender The address which will spend the funds.
         * @param _value The amount of tokens to be spent.
         */
        function approve(address _spender, uint256 _value) returns (bool) {
      
          // To change the approve amount you first have to reduce the addresses`
          //  allowance to zero by calling `approve(_spender, 0)` if it is not
          //  already 0 to mitigate the race condition described here:
          //  https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
          require((_value == 0) || (allowed[msg.sender][_spender] == 0));
      
          allowed[msg.sender][_spender] = _value;
          Approval(msg.sender, _spender, _value);
          return true;
        }
      
        /**
         * @dev Function to check the amount of tokens that an owner allowed to a spender.
         * @param _owner address The address which owns the funds.
         * @param _spender address The address which will spend the funds.
         * @return A uint256 specifing the amount of tokens still avaible for the spender.
         */
        function allowance(address _owner, address _spender) constant returns (uint256 remaining) {
          return allowed[_owner][_spender];
        }
      
      }
      
      contract MintableToken is StandardToken, Ownable {
        event Mint(address indexed to, uint256 amount);
        event MintFinished();
      
        bool public mintingFinished = false;
      
      
        modifier canMint() {
          require(!mintingFinished);
          _;
        }
      
        /**
         * @dev Function to mint tokens
         * @param _to The address that will recieve the minted tokens.
         * @param _amount The amount of tokens to mint.
         * @return A boolean that indicates if the operation was successful.
         */
        function mint(address _to, uint256 _amount) onlyOwner canMint returns (bool) {
          totalSupply = totalSupply.add(_amount);
          balances[_to] = balances[_to].add(_amount);
          Mint(_to, _amount);
          return true;
        }
      
        /**
         * @dev Function to stop minting new tokens.
         * @return True if the operation was successful.
         */
        function finishMinting() onlyOwner returns (bool) {
          mintingFinished = true;
          MintFinished();
          return true;
        }
      }
      
      contract PausableToken is StandardToken, Pausable {
      
        function transfer(address _to, uint _value) whenNotPaused returns (bool) {
          return super.transfer(_to, _value);
        }
      
        function transferFrom(address _from, address _to, uint _value) whenNotPaused returns (bool) {
          return super.transferFrom(_from, _to, _value);
        }
      }
      
      contract BurnableToken is StandardToken {
      
          event Burn(address indexed burner, uint256 value);
      
          /**
           * @dev Burns a specified amount of tokens.
           * @param _value The amount of tokens to burn. 
           */
          function burn(uint256 _value) public {
              require(_value > 0);
      
              address burner = msg.sender;
              balances[burner] = balances[burner].sub(_value);
              totalSupply = totalSupply.sub(_value);
              Burn(msg.sender, _value);
          }
      
      }
      
      contract MANAToken is BurnableToken, PausableToken, MintableToken {
      
          string public constant symbol = "MANA";
      
          string public constant name = "Decentraland MANA";
      
          uint8 public constant decimals = 18;
      
          function burn(uint256 _value) whenNotPaused public {
              super.burn(_value);
          }
      }

      File 3 of 3: ERC721Collection
      // File: @openzeppelin/contracts/ownership/Ownable.sol
      
      pragma solidity ^0.5.0;
      
      /**
       * @dev Contract module which provides a basic access control mechanism, where
       * there is an account (an owner) that can be granted exclusive access to
       * specific functions.
       *
       * This module is used through inheritance. It will make available the modifier
       * `onlyOwner`, which can be aplied to your functions to restrict their use to
       * the owner.
       */
      contract Ownable {
          address private _owner;
      
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
      
          /**
           * @dev Initializes the contract setting the deployer as the initial owner.
           */
          constructor () internal {
              _owner = msg.sender;
              emit OwnershipTransferred(address(0), _owner);
          }
      
          /**
           * @dev Returns the address of the current owner.
           */
          function owner() public view returns (address) {
              return _owner;
          }
      
          /**
           * @dev Throws if called by any account other than the owner.
           */
          modifier onlyOwner() {
              require(isOwner(), "Ownable: caller is not the owner");
              _;
          }
      
          /**
           * @dev Returns true if the caller is the current owner.
           */
          function isOwner() public view returns (bool) {
              return msg.sender == _owner;
          }
      
          /**
           * @dev Leaves the contract without owner. It will not be possible to call
           * `onlyOwner` functions anymore. Can only be called by the current owner.
           *
           * > Note: Renouncing ownership will leave the contract without an owner,
           * thereby removing any functionality that is only available to the owner.
           */
          function renounceOwnership() public onlyOwner {
              emit OwnershipTransferred(_owner, address(0));
              _owner = address(0);
          }
      
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           * Can only be called by the current owner.
           */
          function transferOwnership(address newOwner) public onlyOwner {
              _transferOwnership(newOwner);
          }
      
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           */
          function _transferOwnership(address newOwner) internal {
              require(newOwner != address(0), "Ownable: new owner is the zero address");
              emit OwnershipTransferred(_owner, newOwner);
              _owner = newOwner;
          }
      }
      
      // File: @openzeppelin/contracts/introspection/IERC165.sol
      
      pragma solidity ^0.5.0;
      
      /**
       * @dev Interface of the ERC165 standard, as defined in the
       * [EIP](https://eips.ethereum.org/EIPS/eip-165).
       *
       * Implementers can declare support of contract interfaces, which can then be
       * queried by others (`ERC165Checker`).
       *
       * For an implementation, see `ERC165`.
       */
      interface IERC165 {
          /**
           * @dev Returns true if this contract implements the interface defined by
           * `interfaceId`. See the corresponding
           * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
           * to learn more about how these ids are created.
           *
           * This function call must use less than 30 000 gas.
           */
          function supportsInterface(bytes4 interfaceId) external view returns (bool);
      }
      
      // File: @openzeppelin/contracts/token/ERC721/IERC721.sol
      
      pragma solidity ^0.5.0;
      
      
      /**
       * @dev Required interface of an ERC721 compliant contract.
       */
      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);
      
          /**
           * @dev Returns the number of NFTs in `owner`'s account.
           */
          function balanceOf(address owner) public view returns (uint256 balance);
      
          /**
           * @dev Returns the owner of the NFT specified by `tokenId`.
           */
          function ownerOf(uint256 tokenId) public view returns (address owner);
      
          /**
           * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to
           * another (`to`).
           *
           * 
           *
           * Requirements:
           * - `from`, `to` cannot be zero.
           * - `tokenId` must be owned by `from`.
           * - If the caller is not `from`, it must be have been allowed to move this
           * NFT by either `approve` or `setApproveForAll`.
           */
          function safeTransferFrom(address from, address to, uint256 tokenId) public;
          /**
           * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to
           * another (`to`).
           *
           * Requirements:
           * - If the caller is not `from`, it must be approved to move this NFT by
           * either `approve` or `setApproveForAll`.
           */
          function transferFrom(address from, address to, uint256 tokenId) public;
          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 safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public;
      }
      
      // File: @openzeppelin/contracts/token/ERC721/IERC721Receiver.sol
      
      pragma solidity ^0.5.0;
      
      /**
       * @title ERC721 token receiver interface
       * @dev Interface for any contract that wants to support safeTransfers
       * from ERC721 asset contracts.
       */
      contract IERC721Receiver {
          /**
           * @notice Handle the receipt of an NFT
           * @dev The ERC721 smart contract calls this function on the recipient
           * after a `safeTransfer`. This function MUST return the function selector,
           * otherwise the caller will revert the transaction. The selector to be
           * returned can be obtained as `this.onERC721Received.selector`. This
           * function MAY throw to revert and reject the transfer.
           * Note: the ERC721 contract address is always the message sender.
           * @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 bytes4 `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
           */
          function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data)
          public returns (bytes4);
      }
      
      // File: @openzeppelin/contracts/math/SafeMath.sol
      
      pragma solidity ^0.5.0;
      
      /**
       * @dev Wrappers over Solidity's arithmetic operations with added overflow
       * checks.
       *
       * Arithmetic operations in Solidity wrap on overflow. This can easily result
       * in bugs, because programmers usually assume that an overflow raises an
       * error, which is the standard behavior in high level programming languages.
       * `SafeMath` restores this intuition by reverting the transaction when an
       * operation overflows.
       *
       * Using this library instead of the unchecked operations eliminates an entire
       * class of bugs, so it's recommended to use it always.
       */
      library SafeMath {
          /**
           * @dev Returns the addition of two unsigned integers, reverting on
           * overflow.
           *
           * Counterpart to Solidity's `+` operator.
           *
           * Requirements:
           * - Addition cannot overflow.
           */
          function add(uint256 a, uint256 b) internal pure returns (uint256) {
              uint256 c = a + b;
              require(c >= a, "SafeMath: addition overflow");
      
              return c;
          }
      
          /**
           * @dev Returns the subtraction of two unsigned integers, reverting on
           * overflow (when the result is negative).
           *
           * Counterpart to Solidity's `-` operator.
           *
           * Requirements:
           * - Subtraction cannot overflow.
           */
          function sub(uint256 a, uint256 b) internal pure returns (uint256) {
              require(b <= a, "SafeMath: subtraction overflow");
              uint256 c = a - b;
      
              return c;
          }
      
          /**
           * @dev Returns the multiplication of two unsigned integers, reverting on
           * overflow.
           *
           * Counterpart to Solidity's `*` operator.
           *
           * Requirements:
           * - Multiplication cannot overflow.
           */
          function mul(uint256 a, uint256 b) internal pure returns (uint256) {
              // 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-contracts/pull/522
              if (a == 0) {
                  return 0;
              }
      
              uint256 c = a * b;
              require(c / a == b, "SafeMath: multiplication overflow");
      
              return c;
          }
      
          /**
           * @dev Returns the integer division of two unsigned integers. Reverts on
           * division by zero. The result is rounded towards zero.
           *
           * Counterpart to Solidity's `/` operator. Note: this function uses a
           * `revert` opcode (which leaves remaining gas untouched) while Solidity
           * uses an invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           * - The divisor cannot be zero.
           */
          function div(uint256 a, uint256 b) internal pure returns (uint256) {
              // Solidity only automatically asserts when dividing by 0
              require(b > 0, "SafeMath: division by zero");
              uint256 c = a / b;
              // assert(a == b * c + a % b); // There is no case in which this doesn't hold
      
              return c;
          }
      
          /**
           * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
           * Reverts when dividing by zero.
           *
           * Counterpart to Solidity's `%` operator. This function uses a `revert`
           * opcode (which leaves remaining gas untouched) while Solidity uses an
           * invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           * - The divisor cannot be zero.
           */
          function mod(uint256 a, uint256 b) internal pure returns (uint256) {
              require(b != 0, "SafeMath: modulo by zero");
              return a % b;
          }
      }
      
      // File: @openzeppelin/contracts/utils/Address.sol
      
      pragma solidity ^0.5.0;
      
      /**
       * @dev Collection of functions related to the address type,
       */
      library Address {
          /**
           * @dev Returns true if `account` is a contract.
           *
           * This test is non-exhaustive, and there may be false-negatives: during the
           * execution of a contract's constructor, its address will be reported as
           * not containing a contract.
           *
           * > It is unsafe to assume that an address for which this function returns
           * false is an externally-owned account (EOA) and not a contract.
           */
          function isContract(address account) internal view returns (bool) {
              // This method relies in extcodesize, which returns 0 for contracts in
              // construction, since the code is only stored at the end of the
              // constructor execution.
      
              uint256 size;
              // solhint-disable-next-line no-inline-assembly
              assembly { size := extcodesize(account) }
              return size > 0;
          }
      }
      
      // File: @openzeppelin/contracts/drafts/Counters.sol
      
      pragma solidity ^0.5.0;
      
      
      /**
       * @title Counters
       * @author Matt Condon (@shrugs)
       * @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number
       * of elements in a mapping, issuing ERC721 ids, or counting request ids.
       *
       * Include with `using Counters for Counters.Counter;`
       * Since it is not possible to overflow a 256 bit integer with increments of one, `increment` can skip the SafeMath
       * overflow check, thereby saving gas. This does assume however correct usage, in that the underlying `_value` is never
       * directly accessed.
       */
      library Counters {
          using SafeMath for uint256;
      
          struct Counter {
              // This variable should never be directly accessed by users of the library: interactions must be restricted to
              // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
              // this feature: see https://github.com/ethereum/solidity/issues/4637
              uint256 _value; // default: 0
          }
      
          function current(Counter storage counter) internal view returns (uint256) {
              return counter._value;
          }
      
          function increment(Counter storage counter) internal {
              counter._value += 1;
          }
      
          function decrement(Counter storage counter) internal {
              counter._value = counter._value.sub(1);
          }
      }
      
      // File: @openzeppelin/contracts/introspection/ERC165.sol
      
      pragma solidity ^0.5.0;
      
      
      /**
       * @dev Implementation of the `IERC165` interface.
       *
       * Contracts may inherit from this and call `_registerInterface` to declare
       * their support of an interface.
       */
      contract ERC165 is IERC165 {
          /*
           * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
           */
          bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
      
          /**
           * @dev Mapping of interface ids to whether or not it's supported.
           */
          mapping(bytes4 => bool) private _supportedInterfaces;
      
          constructor () internal {
              // Derived contracts need only register support for their own interfaces,
              // we register support for ERC165 itself here
              _registerInterface(_INTERFACE_ID_ERC165);
          }
      
          /**
           * @dev See `IERC165.supportsInterface`.
           *
           * Time complexity O(1), guaranteed to always use less than 30 000 gas.
           */
          function supportsInterface(bytes4 interfaceId) external view returns (bool) {
              return _supportedInterfaces[interfaceId];
          }
      
          /**
           * @dev Registers the contract as an implementer of the interface defined by
           * `interfaceId`. Support of the actual ERC165 interface is automatic and
           * registering its interface id is not required.
           *
           * See `IERC165.supportsInterface`.
           *
           * Requirements:
           *
           * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
           */
          function _registerInterface(bytes4 interfaceId) internal {
              require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
              _supportedInterfaces[interfaceId] = true;
          }
      }
      
      // File: @openzeppelin/contracts/token/ERC721/ERC721.sol
      
      pragma solidity ^0.5.0;
      
      
      
      
      
      
      
      /**
       * @title ERC721 Non-Fungible Token Standard basic implementation
       * @dev see https://eips.ethereum.org/EIPS/eip-721
       */
      contract ERC721 is ERC165, IERC721 {
          using SafeMath for uint256;
          using Address for address;
          using Counters for Counters.Counter;
      
          // 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 => Counters.Counter) private _ownedTokensCount;
      
          // Mapping from owner to operator approvals
          mapping (address => mapping (address => bool)) private _operatorApprovals;
      
          /*
           *     bytes4(keccak256('balanceOf(address)')) == 0x70a08231
           *     bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e
           *     bytes4(keccak256('approve(address,uint256)')) == 0x095ea7b3
           *     bytes4(keccak256('getApproved(uint256)')) == 0x081812fc
           *     bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465
           *     bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c
           *     bytes4(keccak256('transferFrom(address,address,uint256)')) == 0x23b872dd
           *     bytes4(keccak256('safeTransferFrom(address,address,uint256)')) == 0x42842e0e
           *     bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) == 0xb88d4fde
           *
           *     => 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^
           *        0xa22cb465 ^ 0xe985e9c ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd
           */
          bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
      
          constructor () public {
              // register the supported interfaces to conform to ERC721 via ERC165
              _registerInterface(_INTERFACE_ID_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), "ERC721: balance query for the zero address");
      
              return _ownedTokensCount[owner].current();
          }
      
          /**
           * @dev Gets the owner of the specified token ID.
           * @param tokenId uint256 ID of the token to query the owner of
           * @return 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), "ERC721: owner query for nonexistent token");
      
              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, "ERC721: approval to current owner");
      
              require(msg.sender == owner || isApprovedForAll(owner, msg.sender),
                  "ERC721: approve caller is not owner nor approved for all"
              );
      
              _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), "ERC721: approved query for nonexistent token");
      
              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, "ERC721: approve to caller");
      
              _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 {
              //solhint-disable-next-line max-line-length
              require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721: transfer caller is not owner nor approved");
      
              _transferFrom(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 {
              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 memory _data) public {
              transferFrom(from, to, tokenId);
              require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
          }
      
          /**
           * @dev Returns whether the specified token exists.
           * @param tokenId uint256 ID of the token to query the existence of
           * @return bool 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) {
              require(_exists(tokenId), "ERC721: operator query for nonexistent token");
              address owner = ownerOf(tokenId);
              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
           */
          function _mint(address to, uint256 tokenId) internal {
              require(to != address(0), "ERC721: mint to the zero address");
              require(!_exists(tokenId), "ERC721: token already minted");
      
              _tokenOwner[tokenId] = to;
              _ownedTokensCount[to].increment();
      
              emit Transfer(address(0), to, tokenId);
          }
      
          /**
           * @dev Internal function to burn a specific token.
           * Reverts if the token does not exist.
           * Deprecated, use _burn(uint256) 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 {
              require(ownerOf(tokenId) == owner, "ERC721: burn of token that is not own");
      
              _clearApproval(tokenId);
      
              _ownedTokensCount[owner].decrement();
              _tokenOwner[tokenId] = address(0);
      
              emit Transfer(owner, address(0), 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
           */
          function _burn(uint256 tokenId) internal {
              _burn(ownerOf(tokenId), tokenId);
          }
      
          /**
           * @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 {
              require(ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
              require(to != address(0), "ERC721: transfer to the zero address");
      
              _clearApproval(tokenId);
      
              _ownedTokensCount[from].decrement();
              _ownedTokensCount[to].increment();
      
              _tokenOwner[tokenId] = to;
      
              emit Transfer(from, to, tokenId);
          }
      
          /**
           * @dev Internal function to invoke `onERC721Received` on a target address.
           * The call is not executed if the target address is not a contract.
           *
           * This function is deprecated.
           * @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 bool whether the call correctly returned the expected magic value
           */
          function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)
              internal returns (bool)
          {
              if (!to.isContract()) {
                  return true;
              }
      
              bytes4 retval = IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, _data);
              return (retval == _ERC721_RECEIVED);
          }
      
          /**
           * @dev Private function to clear current approval of a given token ID.
           * @param tokenId uint256 ID of the token to be transferred
           */
          function _clearApproval(uint256 tokenId) private {
              if (_tokenApprovals[tokenId] != address(0)) {
                  _tokenApprovals[tokenId] = address(0);
              }
          }
      }
      
      // File: @openzeppelin/contracts/token/ERC721/IERC721Enumerable.sol
      
      pragma solidity ^0.5.0;
      
      
      /**
       * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
       * @dev See https://eips.ethereum.org/EIPS/eip-721
       */
      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);
      }
      
      // File: @openzeppelin/contracts/token/ERC721/ERC721Enumerable.sol
      
      pragma solidity ^0.5.0;
      
      
      
      
      /**
       * @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 _burn(uint256) 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;
          }
      }
      
      // File: @openzeppelin/contracts/token/ERC721/IERC721Metadata.sol
      
      pragma solidity ^0.5.0;
      
      
      /**
       * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
       * @dev See https://eips.ethereum.org/EIPS/eip-721
       */
      contract IERC721Metadata is IERC721 {
          function name() external view returns (string memory);
          function symbol() external view returns (string memory);
          function tokenURI(uint256 tokenId) external view returns (string memory);
      }
      
      // File: @openzeppelin/contracts/token/ERC721/ERC721Metadata.sol
      
      pragma solidity ^0.5.0;
      
      
      
      
      contract ERC721Metadata is ERC165, ERC721, IERC721Metadata {
          // Token name
          string private _name;
      
          // Token symbol
          string private _symbol;
      
          // Optional mapping for token URIs
          mapping(uint256 => string) private _tokenURIs;
      
          /*
           *     bytes4(keccak256('name()')) == 0x06fdde03
           *     bytes4(keccak256('symbol()')) == 0x95d89b41
           *     bytes4(keccak256('tokenURI(uint256)')) == 0xc87b56dd
           *
           *     => 0x06fdde03 ^ 0x95d89b41 ^ 0xc87b56dd == 0x5b5e139f
           */
          bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f;
      
          /**
           * @dev Constructor function
           */
          constructor (string memory name, string memory symbol) public {
              _name = name;
              _symbol = symbol;
      
              // register the supported interfaces to conform to ERC721 via ERC165
              _registerInterface(_INTERFACE_ID_ERC721_METADATA);
          }
      
          /**
           * @dev Gets the token name.
           * @return string representing the token name
           */
          function name() external view returns (string memory) {
              return _name;
          }
      
          /**
           * @dev Gets the token symbol.
           * @return string representing the token symbol
           */
          function symbol() external view returns (string memory) {
              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) external view returns (string memory) {
              require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
              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 memory uri) internal {
              require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
              _tokenURIs[tokenId] = uri;
          }
      
          /**
           * @dev Internal function to burn a specific token.
           * Reverts if the token does not exist.
           * Deprecated, use _burn(uint256) instead.
           * @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];
              }
          }
      }
      
      // File: @openzeppelin/contracts/token/ERC721/ERC721Full.sol
      
      pragma solidity ^0.5.0;
      
      
      
      
      /**
       * @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://eips.ethereum.org/EIPS/eip-721
       */
      contract ERC721Full is ERC721, ERC721Enumerable, ERC721Metadata {
          constructor (string memory name, string memory symbol) public ERC721Metadata(name, symbol) {
              // solhint-disable-previous-line no-empty-blocks
          }
      }
      
      // File: contracts/ERC721Collection.sol
      
      pragma solidity ^0.5.11;
      
      
      
      
      contract ERC721Collection is Ownable, ERC721Full {
          mapping(bytes32 => uint256) public maxIssuance;
          mapping(bytes32 => uint) public issued;
          mapping(uint256 => string) internal _tokenPaths;
          mapping(address => bool) public allowed;
      
          string[] public wearables;
      
          string public baseURI;
          bool public isComplete;
      
          event BaseURI(string _oldBaseURI, string _newBaseURI);
          event Allowed(address indexed _operator, bool _allowed);
          event AddWearable(bytes32 indexed _wearableIdKey, string _wearableId, uint256 _maxIssuance);
          event Issue(address indexed _beneficiary, uint256 indexed _tokenId, bytes32 indexed _wearableIdKey, string _wearableId, uint256 _issuedId);
          event Complete();
      
      
          /**
           * @dev Create the contract.
           * @param _name - name of the contract
           * @param _symbol - symbol of the contract
           * @param _operator - Address allowed to mint tokens
           * @param _baseURI - base URI for token URIs
           */
          constructor(string memory _name, string memory _symbol, address _operator, string memory _baseURI) public ERC721Full(_name, _symbol) {
              setAllowed(_operator, true);
              setBaseURI(_baseURI);
          }
      
          modifier onlyAllowed() {
              require(allowed[msg.sender], "Only an `allowed` address can issue tokens");
              _;
          }
      
      
          /**
           * @dev Issue a new NFT of the specified kind.
           * @notice that will throw if kind has reached its maximum or is invalid
           * @param _beneficiary - owner of the token
           * @param _wearableId - token wearable
           */
          function issueToken(address _beneficiary, string calldata _wearableId) external onlyAllowed {
              _issueToken(_beneficiary, _wearableId);
          }
      
          /**
           * @dev Issue NFTs.
           * @notice that will throw if kind has reached its maximum or is invalid
           * @param _beneficiaries - owner of the tokens
           * @param _wearableIds - token wearables
           */
          function issueTokens(address[] calldata _beneficiaries, bytes32[] calldata _wearableIds) external onlyAllowed {
              require(_beneficiaries.length == _wearableIds.length, "Parameters should have the same length");
      
              for(uint256 i = 0; i < _wearableIds.length; i++) {
                  _issueToken(_beneficiaries[i], _bytes32ToString(_wearableIds[i]));
              }
          }
      
      
          /**
           * @dev Set Base URI.
           * @param _baseURI - base URI for token URIs
           */
          function setBaseURI(string memory _baseURI) public onlyOwner {
              emit BaseURI(baseURI, _baseURI);
              baseURI = _baseURI;
          }
      
          /**
           * @dev Set allowed account to issue tokens.
           * @param _operator - Address allowed to issue tokens
           * @param _allowed - Whether is allowed or not
           */
          function setAllowed(address _operator, bool _allowed) public onlyOwner {
              require(_operator != address(0), "Invalid address");
              require(allowed[_operator] != _allowed, "You should set a different value");
      
              allowed[_operator] = _allowed;
              emit Allowed(_operator, _allowed);
          }
      
      
          /**
           * @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 queried
           * @return token URI
           */
          function tokenURI(uint256 _tokenId) external view returns (string memory) {
              require(_exists(_tokenId), "ERC721Metadata: received a URI query for a nonexistent token");
              return string(abi.encodePacked(baseURI, _tokenPaths[_tokenId]));
          }
      
      
          /**
           * @dev Transfers the ownership of given tokens ID to another address.
           * Usage of this method is discouraged, use {safeBatchTransferFrom} 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 _tokenIds uint256 ID of the token to be transferred
           */
          function batchTransferFrom(address _from, address _to, uint256[] calldata _tokenIds) external {
              for (uint256 i = 0; i < _tokenIds.length; i++) {
                  transferFrom(_from, _to, _tokenIds[i]);
              }
          }
      
          /**
           * @dev Returns the wearables length.
           * @return wearable length
           */
          function wearablesCount() external view returns (uint256) {
              return wearables.length;
          }
      
          /**
           * @dev Complete the collection.
           * @notice that it will only prevent for adding more wearables.
           * The issuance is still allowed.
           */
          function completeCollection() external onlyOwner {
              require(!isComplete, "The collection is already completed");
              isComplete = true;
              emit Complete();
          }
      
           /**
           * @dev Add a new wearable to the collection.
           * @notice that this method should only allow wearableIds less than or equal to 32 bytes
           * @param _wearableIds - wearable ids
           * @param _maxIssuances - total supply for the wearables
           */
          function addWearables(bytes32[] calldata _wearableIds, uint256[] calldata _maxIssuances) external onlyOwner {
              require(_wearableIds.length == _maxIssuances.length, "Parameters should have the same length");
      
              for (uint256 i = 0; i < _wearableIds.length; i++) {
                  addWearable(_bytes32ToString(_wearableIds[i]), _maxIssuances[i]);
              }
          }
      
          /**
           * @dev Add a new wearable to the collection.
           * @notice that this method allows wearableIds of any size. It should be used
           * if a wearableId is greater than 32 bytes
           * @param _wearableId - wearable id
           * @param _maxIssuance - total supply for the wearable
           */
          function addWearable(string memory _wearableId, uint256 _maxIssuance) public onlyOwner {
              require(!isComplete, "The collection is complete");
              bytes32 key = getWearableKey(_wearableId);
      
              require(maxIssuance[key] == 0, "Can not modify an existing wearable");
              require(_maxIssuance > 0, "Max issuance should be greater than 0");
      
              maxIssuance[key] = _maxIssuance;
              wearables.push(_wearableId);
      
              emit AddWearable(key, _wearableId, _maxIssuance);
          }
      
          /**
           * @dev Safely transfers the ownership of given token IDs to another address
           * If the target address is a contract, it must implement {IERC721Receiver-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 _tokenIds - uint256 IDs of the tokens to be transferred
           */
          function safeBatchTransferFrom(address _from, address _to, uint256[] memory _tokenIds) public {
              safeBatchTransferFrom(_from, _to, _tokenIds, "");
          }
      
          /**
           * @dev Safely transfers the ownership of given token IDs to another address
           * If the target address is a contract, it must implement {IERC721Receiver-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 _tokenIds - uint256 ID of the tokens to be transferred
           * @param _data bytes data to send along with a safe transfer check
           */
          function safeBatchTransferFrom(address _from, address _to, uint256[] memory _tokenIds, bytes memory _data) public {
              for (uint256 i = 0; i < _tokenIds.length; i++) {
                  safeTransferFrom(_from, _to, _tokenIds[i], _data);
              }
          }
      
          /**
           * @dev Get keccak256 of a wearableId.
           * @param _wearableId - token wearable
           * @return bytes32 keccak256 of the wearableId
           */
          function getWearableKey(string memory _wearableId) public pure returns (bytes32) {
              return keccak256(abi.encodePacked(_wearableId));
          }
      
          /**
           * @dev Issue a new NFT of the specified kind.
           * @notice that will throw if kind has reached its maximum or is invalid
           * @param _beneficiary - owner of the token
           * @param _wearableId - token wearable
           */
          function _issueToken(address _beneficiary, string memory _wearableId) internal {
              bytes32 key = getWearableKey(_wearableId);
              if (maxIssuance[key] > 0 && issued[key] < maxIssuance[key]) {
                  issued[key] = issued[key] + 1;
                  uint tokenId = this.totalSupply();
      
                  _mint(_beneficiary, tokenId);
                  _setTokenURI(
                      tokenId,
                      string(abi.encodePacked(_wearableId, "/", _uintToString(issued[key])))
                  );
      
                  emit Issue(_beneficiary, tokenId, key, _wearableId, issued[key]);
              } else {
                  revert("invalid: trying to issue an exhausted wearable of nft");
              }
          }
      
          /**
           * @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 as its URI
           * @param _uri - string URI to assign
           */
          function _setTokenURI(uint256 _tokenId, string memory _uri) internal {
              require(_exists(_tokenId), "ERC721Metadata: calling set URI for a nonexistent token");
              _tokenPaths[_tokenId] = _uri;
          }
      
          /**
           * @dev Convert bytes32 to string.
           * @param _x - to be converted to string.
           * @return string
           */
          function _bytes32ToString(bytes32 _x) internal pure returns (string memory) {
              bytes memory bytesString = new bytes(32);
              uint charCount = 0;
              for (uint j = 0; j < 32; j++) {
                  byte char = byte(bytes32(uint(_x) * 2 ** (8 * j)));
                  if (char != 0) {
                      bytesString[charCount] = char;
                      charCount++;
                  }
              }
              bytes memory bytesStringTrimmed = new bytes(charCount);
              for (uint j = 0; j < charCount; j++) {
                  bytesStringTrimmed[j] = bytesString[j];
              }
              return string(bytesStringTrimmed);
          }
      
      
           /**
           * @dev Convert uint to string.
           * @param _i - uint256 to be converted to string.
           * @return uint in string
           */
          function _uintToString(uint _i) internal pure returns (string memory _uintAsString) {
              if (_i == 0) {
                  return "0";
              }
              uint j = _i;
              uint len;
              while (j != 0) {
                  len++;
                  j /= 10;
              }
              bytes memory bstr = new bytes(len);
              uint k = len - 1;
              while (_i != 0) {
                  bstr[k--] = byte(uint8(48 + _i % 10));
                  _i /= 10;
              }
              return string(bstr);
          }
      }