ETH Price: $3,048.00 (+0.34%)

Transaction Decoder

Block:
11964722 at Mar-03-2021 10:25:34 AM +UTC
Transaction Fee:
0.0385 ETH $117.35
Gas Used:
500,000 Gas / 77 Gwei

Account State Difference:

  Address   Before After State Difference Code
0x3f1e01F6...042949a2C
4.202034801294437529 Eth
Nonce: 50124
4.163534801294437529 Eth
Nonce: 50125
0.0385
(Nanopool)
3,209.157095864332352995 Eth3,209.195595864332352995 Eth0.0385

Execution Trace

0xd9f61a4a96f66afe09c6f55b72aeaf1590ac8495.39125215( )
  • WalletSimple.sendMultiSig( toAddress=0x60F80121C31A0d46B5279700f9DF786054aa5eE5, value=225000000000000000, data=0x, expireTime=1615360688, sequenceId=6635, signature=0xCB1BD2E25887FA39E11F9E5C4A46717817B881AE8D8ACDA36731D983B95AD3DE366F84924A0011EAB4329FD468DDD55CE0EEB01F141151F11B68001CDA6CBE4B1C )
    • ETH 0.225 MintableToken.CALL( )
      File 1 of 2: WalletSimple
      pragma solidity ^0.4.14;
      
      /**
       * Contract that exposes the needed erc20 token functions
       */
      
      contract ERC20Interface {
        // Send _value amount of tokens to address _to
        function transfer(address _to, uint256 _value) returns (bool success);
        // Get the account balance of another account with address _owner
        function balanceOf(address _owner) constant returns (uint256 balance);
      }
      
      /**
       * Contract that will forward any incoming Ether to its creator
       */
      contract Forwarder {
        // Address to which any funds sent to this contract will be forwarded
        address public parentAddress;
        event ForwarderDeposited(address from, uint value, bytes data);
      
        event TokensFlushed(
          address tokenContractAddress, // The contract address of the token
          uint value // Amount of token sent
        );
      
        /**
         * Create the contract, and set the destination address to that of the creator
         */
        function Forwarder() {
          parentAddress = msg.sender;
        }
      
        /**
         * Modifier that will execute internal code block only if the sender is a parent of the forwarder contract
         */
        modifier onlyParent {
          if (msg.sender != parentAddress) {
            throw;
          }
          _;
        }
      
        /**
         * Default function; Gets called when Ether is deposited, and forwards it to the destination address
         */
        function() payable {
          if (!parentAddress.call.value(msg.value)(msg.data))
            throw;
          // Fire off the deposited event if we can forward it  
          ForwarderDeposited(msg.sender, msg.value, msg.data);
        }
      
        /**
         * Execute a token transfer of the full balance from the forwarder token to the main wallet contract
         * @param tokenContractAddress the address of the erc20 token contract
         */
        function flushTokens(address tokenContractAddress) onlyParent {
          ERC20Interface instance = ERC20Interface(tokenContractAddress);
          var forwarderAddress = address(this);
          var forwarderBalance = instance.balanceOf(forwarderAddress);
          if (forwarderBalance == 0) {
            return;
          }
          if (!instance.transfer(parentAddress, forwarderBalance)) {
            throw;
          }
          TokensFlushed(tokenContractAddress, forwarderBalance);
        }
      
        /**
         * It is possible that funds were sent to this address before the contract was deployed.
         * We can flush those funds to the destination address.
         */
        function flush() {
          if (!parentAddress.call.value(this.balance)())
            throw;
        }
      }
      
      /**
       * Basic multi-signer wallet designed for use in a co-signing environment where 2 signatures are required to move funds.
       * Typically used in a 2-of-3 signing configuration. Uses ecrecover to allow for 2 signatures in a single transaction.
       */
      contract WalletSimple {
        // Events
        event Deposited(address from, uint value, bytes data);
        event SafeModeActivated(address msgSender);
        event Transacted(
          address msgSender, // Address of the sender of the message initiating the transaction
          address otherSigner, // Address of the signer (second signature) used to initiate the transaction
          bytes32 operation, // Operation hash (sha3 of toAddress, value, data, expireTime, sequenceId)
          address toAddress, // The address the transaction was sent to
          uint value, // Amount of Wei sent to the address
          bytes data // Data sent when invoking the transaction
        );
        event TokenTransacted(
          address msgSender, // Address of the sender of the message initiating the transaction
          address otherSigner, // Address of the signer (second signature) used to initiate the transaction
          bytes32 operation, // Operation hash (sha3 of toAddress, value, tokenContractAddress, expireTime, sequenceId)
          address toAddress, // The address the transaction was sent to
          uint value, // Amount of token sent
          address tokenContractAddress // The contract address of the token
        );
      
        // Public fields
        address[] public signers; // The addresses that can co-sign transactions on the wallet
        bool public safeMode = false; // When active, wallet may only send to signer addresses
      
        // Internal fields
        uint constant SEQUENCE_ID_WINDOW_SIZE = 10;
        uint[10] recentSequenceIds;
      
        /**
         * Modifier that will execute internal code block only if the sender is an authorized signer on this wallet
         */
        modifier onlysigner {
          if (!isSigner(msg.sender)) {
            throw;
          }
          _;
        }
      
        /**
         * Set up a simple multi-sig wallet by specifying the signers allowed to be used on this wallet.
         * 2 signers will be required to send a transaction from this wallet.
         * Note: The sender is NOT automatically added to the list of signers.
         * Signers CANNOT be changed once they are set
         *
         * @param allowedSigners An array of signers on the wallet
         */
        function WalletSimple(address[] allowedSigners) {
          if (allowedSigners.length != 3) {
            // Invalid number of signers
            throw;
          }
          signers = allowedSigners;
        }
      
        /**
         * Gets called when a transaction is received without calling a method
         */
        function() payable {
          if (msg.value > 0) {
            // Fire deposited event if we are receiving funds
            Deposited(msg.sender, msg.value, msg.data);
          }
        }
      
        /**
         * Create a new contract (and also address) that forwards funds to this contract
         * returns address of newly created forwarder address
         */
        function createForwarder() onlysigner returns (address) {
          return new Forwarder();
        }
      
        /**
         * Execute a multi-signature transaction from this wallet using 2 signers: one from msg.sender and the other from ecrecover.
         * The signature is a signed form (using eth.sign) of tightly packed toAddress, value, data, expireTime and sequenceId
         * Sequence IDs are numbers starting from 1. They are used to prevent replay attacks and may not be repeated.
         *
         * @param toAddress the destination address to send an outgoing transaction
         * @param value the amount in Wei to be sent
         * @param data the data to send to the toAddress when invoking the transaction
         * @param expireTime the number of seconds since 1970 for which this transaction is valid
         * @param sequenceId the unique sequence id obtainable from getNextSequenceId
         * @param signature the result of eth.sign on the operationHash sha3(toAddress, value, data, expireTime, sequenceId)
         */
        function sendMultiSig(address toAddress, uint value, bytes data, uint expireTime, uint sequenceId, bytes signature) onlysigner {
          // Verify the other signer
          var operationHash = sha3("ETHER", toAddress, value, data, expireTime, sequenceId);
          
          var otherSigner = verifyMultiSig(toAddress, operationHash, signature, expireTime, sequenceId);
      
          // Success, send the transaction
          if (!(toAddress.call.value(value)(data))) {
            // Failed executing transaction
            throw;
          }
          Transacted(msg.sender, otherSigner, operationHash, toAddress, value, data);
        }
        
        /**
         * Execute a multi-signature token transfer from this wallet using 2 signers: one from msg.sender and the other from ecrecover.
         * The signature is a signed form (using eth.sign) of tightly packed toAddress, value, tokenContractAddress, expireTime and sequenceId
         * Sequence IDs are numbers starting from 1. They are used to prevent replay attacks and may not be repeated.
         *
         * @param toAddress the destination address to send an outgoing transaction
         * @param value the amount in tokens to be sent
         * @param tokenContractAddress the address of the erc20 token contract
         * @param expireTime the number of seconds since 1970 for which this transaction is valid
         * @param sequenceId the unique sequence id obtainable from getNextSequenceId
         * @param signature the result of eth.sign on the operationHash sha3(toAddress, value, tokenContractAddress, expireTime, sequenceId)
         */
        function sendMultiSigToken(address toAddress, uint value, address tokenContractAddress, uint expireTime, uint sequenceId, bytes signature) onlysigner {
          // Verify the other signer
          var operationHash = sha3("ERC20", toAddress, value, tokenContractAddress, expireTime, sequenceId);
          
          var otherSigner = verifyMultiSig(toAddress, operationHash, signature, expireTime, sequenceId);
          
          ERC20Interface instance = ERC20Interface(tokenContractAddress);
          if (!instance.transfer(toAddress, value)) {
              throw;
          }
          TokenTransacted(msg.sender, otherSigner, operationHash, toAddress, value, tokenContractAddress);
        }
      
        /**
         * Execute a token flush from one of the forwarder addresses. This transfer needs only a single signature and can be done by any signer
         *
         * @param forwarderAddress the address of the forwarder address to flush the tokens from
         * @param tokenContractAddress the address of the erc20 token contract
         */
        function flushForwarderTokens(address forwarderAddress, address tokenContractAddress) onlysigner {    
          Forwarder forwarder = Forwarder(forwarderAddress);
          forwarder.flushTokens(tokenContractAddress);
        }  
        
        /**
         * Do common multisig verification for both eth sends and erc20token transfers
         *
         * @param toAddress the destination address to send an outgoing transaction
         * @param operationHash the sha3 of the toAddress, value, data/tokenContractAddress and expireTime
         * @param signature the tightly packed signature of r, s, and v as an array of 65 bytes (returned by eth.sign)
         * @param expireTime the number of seconds since 1970 for which this transaction is valid
         * @param sequenceId the unique sequence id obtainable from getNextSequenceId
         * returns address of the address to send tokens or eth to
         */
        function verifyMultiSig(address toAddress, bytes32 operationHash, bytes signature, uint expireTime, uint sequenceId) private returns (address) {
      
          var otherSigner = recoverAddressFromSignature(operationHash, signature);
      
          // Verify if we are in safe mode. In safe mode, the wallet can only send to signers
          if (safeMode && !isSigner(toAddress)) {
            // We are in safe mode and the toAddress is not a signer. Disallow!
            throw;
          }
          // Verify that the transaction has not expired
          if (expireTime < block.timestamp) {
            // Transaction expired
            throw;
          }
      
          // Try to insert the sequence ID. Will throw if the sequence id was invalid
          tryInsertSequenceId(sequenceId);
      
          if (!isSigner(otherSigner)) {
            // Other signer not on this wallet or operation does not match arguments
            throw;
          }
          if (otherSigner == msg.sender) {
            // Cannot approve own transaction
            throw;
          }
      
          return otherSigner;
        }
      
        /**
         * Irrevocably puts contract into safe mode. When in this mode, transactions may only be sent to signing addresses.
         */
        function activateSafeMode() onlysigner {
          safeMode = true;
          SafeModeActivated(msg.sender);
        }
      
        /**
         * Determine if an address is a signer on this wallet
         * @param signer address to check
         * returns boolean indicating whether address is signer or not
         */
        function isSigner(address signer) returns (bool) {
          // Iterate through all signers on the wallet and
          for (uint i = 0; i < signers.length; i++) {
            if (signers[i] == signer) {
              return true;
            }
          }
          return false;
        }
      
        /**
         * Gets the second signer's address using ecrecover
         * @param operationHash the sha3 of the toAddress, value, data/tokenContractAddress and expireTime
         * @param signature the tightly packed signature of r, s, and v as an array of 65 bytes (returned by eth.sign)
         * returns address recovered from the signature
         */
        function recoverAddressFromSignature(bytes32 operationHash, bytes signature) private returns (address) {
          if (signature.length != 65) {
            throw;
          }
          // We need to unpack the signature, which is given as an array of 65 bytes (from eth.sign)
          bytes32 r;
          bytes32 s;
          uint8 v;
          assembly {
            r := mload(add(signature, 32))
            s := mload(add(signature, 64))
            v := and(mload(add(signature, 65)), 255)
          }
          if (v < 27) {
            v += 27; // Ethereum versions are 27 or 28 as opposed to 0 or 1 which is submitted by some signing libs
          }
          return ecrecover(operationHash, v, r, s);
        }
      
        /**
         * Verify that the sequence id has not been used before and inserts it. Throws if the sequence ID was not accepted.
         * We collect a window of up to 10 recent sequence ids, and allow any sequence id that is not in the window and
         * greater than the minimum element in the window.
         * @param sequenceId to insert into array of stored ids
         */
        function tryInsertSequenceId(uint sequenceId) onlysigner private {
          // Keep a pointer to the lowest value element in the window
          uint lowestValueIndex = 0;
          for (uint i = 0; i < SEQUENCE_ID_WINDOW_SIZE; i++) {
            if (recentSequenceIds[i] == sequenceId) {
              // This sequence ID has been used before. Disallow!
              throw;
            }
            if (recentSequenceIds[i] < recentSequenceIds[lowestValueIndex]) {
              lowestValueIndex = i;
            }
          }
          if (sequenceId < recentSequenceIds[lowestValueIndex]) {
            // The sequence ID being used is lower than the lowest value in the window
            // so we cannot accept it as it may have been used before
            throw;
          }
          if (sequenceId > (recentSequenceIds[lowestValueIndex] + 10000)) {
            // Block sequence IDs which are much higher than the lowest value
            // This prevents people blocking the contract by using very large sequence IDs quickly
            throw;
          }
          recentSequenceIds[lowestValueIndex] = sequenceId;
        }
      
        /**
         * Gets the next available sequence ID for signing when using executeAndConfirm
         * returns the sequenceId one higher than the highest currently stored
         */
        function getNextSequenceId() returns (uint) {
          uint highestSequenceId = 0;
          for (uint i = 0; i < SEQUENCE_ID_WINDOW_SIZE; i++) {
            if (recentSequenceIds[i] > highestSequenceId) {
              highestSequenceId = recentSequenceIds[i];
            }
          }
          return highestSequenceId + 1;
        }
      }

      File 2 of 2: MintableToken
      pragma solidity ^0.5.0;
      pragma experimental ABIEncoderV2;
      
      /*
       * @dev Provides information about the current execution context, including the
       * sender of the transaction and its data. While these are generally available
       * via msg.sender and msg.data, they should not be accessed in such a direct
       * manner, since when dealing with GSN meta-transactions the account sending and
       * paying for execution may not be the actual sender (as far as an application
       * is concerned).
       *
       * This contract is only required for intermediate, library-like contracts.
       */
      contract Context {
          // Empty internal constructor, to prevent people from mistakenly deploying
          // an instance of this contract, which should be used via inheritance.
          constructor () internal { }
          // solhint-disable-previous-line no-empty-blocks
      
          function _msgSender() internal view returns (address payable) {
              return msg.sender;
          }
      
          function _msgData() internal view returns (bytes memory) {
              this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
              return msg.data;
          }
      }
      
      /**
       * @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 is Context {
          address private _owner;
      
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
      
          /**
           * @dev Initializes the contract setting the deployer as the initial owner.
           */
          constructor () internal {
              address msgSender = _msgSender();
              _owner = msgSender;
              emit OwnershipTransferred(address(0), msgSender);
          }
      
          /**
           * @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 _msgSender() == _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;
          }
      }
      
      /**
       * @dev Interface of the ERC165 standard, as defined in the
       * https://eips.ethereum.org/EIPS/eip-165[EIP].
       *
       * 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
           * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
           * 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);
      }
      
      /**
       * @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 {setApprovalForAll}.
           */
          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 {setApprovalForAll}.
           */
          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;
      }
      
      /**
       * @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);
      }
      
      /**
       * @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 {IERC721-safeTransferFrom}. 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);
      }
      
      /**
       * @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) {
              return sub(a, b, "SafeMath: subtraction overflow");
          }
      
          /**
           * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
           * overflow (when the result is negative).
           *
           * Counterpart to Solidity's `-` operator.
           *
           * Requirements:
           * - Subtraction cannot overflow.
           *
           * _Available since v2.4.0._
           */
          function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              require(b <= a, errorMessage);
              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) {
              return div(a, b, "SafeMath: division by zero");
          }
      
          /**
           * @dev Returns the integer division of two unsigned integers. Reverts with custom message 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.
           *
           * _Available since v2.4.0._
           */
          function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              // Solidity only automatically asserts when dividing by 0
              require(b > 0, errorMessage);
              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) {
              return mod(a, b, "SafeMath: modulo by zero");
          }
      
          /**
           * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
           * Reverts with custom message 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.
           *
           * _Available since v2.4.0._
           */
          function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              require(b != 0, errorMessage);
              return a % b;
          }
      }
      
      /**
       * @dev Collection of functions related to the address type
       */
      library Address {
          /**
           * @dev Returns true if `account` is a contract.
           *
           * [IMPORTANT]
           * ====
           * It is unsafe to assume that an address for which this function returns
           * false is an externally-owned account (EOA) and not a contract.
           *
           * Among others, `isContract` will return false for the following 
           * types of addresses:
           *
           *  - an externally-owned account
           *  - a contract in construction
           *  - an address where a contract will be created
           *  - an address where a contract lived, but was destroyed
           * ====
           */
          function isContract(address account) internal view returns (bool) {
              // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
              // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
              // for accounts without code, i.e. `keccak256('')`
              bytes32 codehash;
              bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
              // solhint-disable-next-line no-inline-assembly
              assembly { codehash := extcodehash(account) }
              return (codehash != accountHash && codehash != 0x0);
          }
      
          /**
           * @dev Converts an `address` into `address payable`. Note that this is
           * simply a type cast: the actual underlying value is not changed.
           *
           * _Available since v2.4.0._
           */
          function toPayable(address account) internal pure returns (address payable) {
              return address(uint160(account));
          }
      
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           *
           * _Available since v2.4.0._
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              require(address(this).balance >= amount, "Address: insufficient balance");
      
              // solhint-disable-next-line avoid-call-value
              (bool success, ) = recipient.call.value(amount)("");
              require(success, "Address: unable to send value, recipient may have reverted");
          }
      }
      
      /**
       * @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 {
              // The {SafeMath} overflow check can be skipped here, see the comment at the top
              counter._value += 1;
          }
      
          function decrement(Counter storage counter) internal {
              counter._value = counter._value.sub(1);
          }
      }
      
      /**
       * @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;
          }
      }
      
      /**
       * @title ERC721 Non-Fungible Token Standard basic implementation
       * @dev see https://eips.ethereum.org/EIPS/eip-721
       */
      contract ERC721 is Context, 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)')) == 0xe985e9c5
           *     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(_msgSender() == owner || isApprovedForAll(owner, _msgSender()),
                  "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 != _msgSender(), "ERC721: approve to caller");
      
              _operatorApprovals[_msgSender()][to] = approved;
              emit ApprovalForAll(_msgSender(), 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(_msgSender(), 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 {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 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 {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 _msgSender() 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 {
              require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
              _safeTransferFrom(from, to, tokenId, _data);
          }
      
          /**
           * @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) internal {
              _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 safely mint a new token.
           * Reverts if the given token ID already exists.
           * 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.
           * @param to The address that will own the minted token
           * @param tokenId uint256 ID of the token to be minted
           */
          function _safeMint(address to, uint256 tokenId) internal {
              _safeMint(to, tokenId, "");
          }
      
          /**
           * @dev Internal function to safely mint a new token.
           * Reverts if the given token ID already exists.
           * 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.
           * @param to The address that will own the minted token
           * @param tokenId uint256 ID of the token to be minted
           * @param _data bytes data to send along with a safe transfer check
           */
          function _safeMint(address to, uint256 tokenId, bytes memory _data) internal {
              _mint(to, tokenId);
              require(_checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
          }
      
          /**
           * @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} 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 {IERC721Receiver-onERC721Received} on a target address.
           * The call is not executed if the target address is not a contract.
           *
           * This is an internal detail of the `ERC721` contract and its use 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;
              }
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = to.call(abi.encodeWithSelector(
                  IERC721Receiver(to).onERC721Received.selector,
                  _msgSender(),
                  from,
                  tokenId,
                  _data
              ));
              if (!success) {
                  if (returndata.length > 0) {
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert("ERC721: transfer to non ERC721Receiver implementer");
                  }
              } else {
                  bytes4 retval = abi.decode(returndata, (bytes4));
                  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);
              }
          }
      }
      
      /**
       * @title ERC721 Burnable Token
       * @dev ERC721 Token that can be irreversibly burned (destroyed).
       */
      contract ERC721Burnable is Context, ERC721 {
          /**
           * @dev Burns a specific ERC721 token.
           * @param tokenId uint256 id of the ERC721 token to be burned.
           */
          function burn(uint256 tokenId) public {
              //solhint-disable-next-line max-line-length
              require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721Burnable: caller is not owner nor approved");
              _burn(tokenId);
          }
      }
      
      /**
       * @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);
      }
      
      /**
       * @title ERC-721 Non-Fungible Token with optional enumeration extension logic
       * @dev See https://eips.ethereum.org/EIPS/eip-721
       */
      contract ERC721Enumerable is Context, ERC165, ERC721, IERC721Enumerable {
          // Mapping from owner to list of owned token IDs
          mapping(address => uint256[]) private _ownedTokens;
      
          // Mapping from token ID to index of the owner tokens list
          mapping(uint256 => uint256) private _ownedTokensIndex;
      
          // Array with all token ids, used for enumeration
          uint256[] private _allTokens;
      
          // Mapping from token id to position in the allTokens array
          mapping(uint256 => uint256) private _allTokensIndex;
      
          /*
           *     bytes4(keccak256('totalSupply()')) == 0x18160ddd
           *     bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) == 0x2f745c59
           *     bytes4(keccak256('tokenByIndex(uint256)')) == 0x4f6ccce7
           *
           *     => 0x18160ddd ^ 0x2f745c59 ^ 0x4f6ccce7 == 0x780e9d63
           */
          bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63;
      
          /**
           * @dev Constructor function.
           */
          constructor () public {
              // register the supported interface to conform to ERC721Enumerable via ERC165
              _registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE);
          }
      
          /**
           * @dev Gets the token ID at a given index of the tokens list of the requested owner.
           * @param owner address owning the tokens list to be accessed
           * @param index uint256 representing the index to be accessed of the requested tokens list
           * @return uint256 token ID at the given index of the tokens list owned by the requested address
           */
          function tokenOfOwnerByIndex(address owner, uint256 index) public view returns (uint256) {
              require(index < balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
              return _ownedTokens[owner][index];
          }
      
          /**
           * @dev Gets the total amount of tokens stored by the contract.
           * @return uint256 representing the total amount of tokens
           */
          function totalSupply() public view returns (uint256) {
              return _allTokens.length;
          }
      
          /**
           * @dev Gets the token ID at a given index of all the tokens in this contract
           * Reverts if the index is greater or equal to the total number of tokens.
           * @param index uint256 representing the index to be accessed of the tokens list
           * @return uint256 token ID at the given index of the tokens list
           */
          function tokenByIndex(uint256 index) public view returns (uint256) {
              require(index < totalSupply(), "ERC721Enumerable: global index out of bounds");
              return _allTokens[index];
          }
      
          /**
           * @dev Internal function to transfer ownership of a given token ID to another address.
           * As opposed to transferFrom, this imposes no restrictions on msg.sender.
           * @param from current owner of the token
           * @param to address to receive the ownership of the given token ID
           * @param tokenId uint256 ID of the token to be transferred
           */
          function _transferFrom(address from, address to, uint256 tokenId) internal {
              super._transferFrom(from, to, tokenId);
      
              _removeTokenFromOwnerEnumeration(from, tokenId);
      
              _addTokenToOwnerEnumeration(to, tokenId);
          }
      
          /**
           * @dev Internal function to mint a new token.
           * Reverts if the given token ID already exists.
           * @param to address the beneficiary that will own the minted token
           * @param tokenId uint256 ID of the token to be minted
           */
          function _mint(address to, uint256 tokenId) internal {
              super._mint(to, tokenId);
      
              _addTokenToOwnerEnumeration(to, tokenId);
      
              _addTokenToAllTokensEnumeration(tokenId);
          }
      
          /**
           * @dev Internal function to burn a specific token.
           * Reverts if the token does not exist.
           * Deprecated, use {ERC721-_burn} instead.
           * @param owner owner of the token to burn
           * @param tokenId uint256 ID of the token being burned
           */
          function _burn(address owner, uint256 tokenId) internal {
              super._burn(owner, tokenId);
      
              _removeTokenFromOwnerEnumeration(owner, tokenId);
              // Since tokenId will be deleted, we can clear its slot in _ownedTokensIndex to trigger a gas refund
              _ownedTokensIndex[tokenId] = 0;
      
              _removeTokenFromAllTokensEnumeration(tokenId);
          }
      
          /**
           * @dev Gets the list of token IDs of the requested owner.
           * @param owner address owning the tokens
           * @return uint256[] List of token IDs owned by the requested address
           */
          function _tokensOfOwner(address owner) internal view returns (uint256[] storage) {
              return _ownedTokens[owner];
          }
      
          /**
           * @dev Private function to add a token to this extension's ownership-tracking data structures.
           * @param to address representing the new owner of the given token ID
           * @param tokenId uint256 ID of the token to be added to the tokens list of the given address
           */
          function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
              _ownedTokensIndex[tokenId] = _ownedTokens[to].length;
              _ownedTokens[to].push(tokenId);
          }
      
          /**
           * @dev Private function to add a token to this extension's token tracking data structures.
           * @param tokenId uint256 ID of the token to be added to the tokens list
           */
          function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
              _allTokensIndex[tokenId] = _allTokens.length;
              _allTokens.push(tokenId);
          }
      
          /**
           * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
           * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
           * gas optimizations e.g. when performing a transfer operation (avoiding double writes).
           * This has O(1) time complexity, but alters the order of the _ownedTokens array.
           * @param from address representing the previous owner of the given token ID
           * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
           */
          function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
              // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
              // then delete the last slot (swap and pop).
      
              uint256 lastTokenIndex = _ownedTokens[from].length.sub(1);
              uint256 tokenIndex = _ownedTokensIndex[tokenId];
      
              // When the token to delete is the last token, the swap operation is unnecessary
              if (tokenIndex != lastTokenIndex) {
                  uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];
      
                  _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
                  _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
              }
      
              // This also deletes the contents at the last position of the array
              _ownedTokens[from].length--;
      
              // Note that _ownedTokensIndex[tokenId] hasn't been cleared: it still points to the old slot (now occupied by
              // lastTokenId, or just over the end of the array if the token was the last one).
          }
      
          /**
           * @dev Private function to remove a token from this extension's token tracking data structures.
           * This has O(1) time complexity, but alters the order of the _allTokens array.
           * @param tokenId uint256 ID of the token to be removed from the tokens list
           */
          function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
              // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
              // then delete the last slot (swap and pop).
      
              uint256 lastTokenIndex = _allTokens.length.sub(1);
              uint256 tokenIndex = _allTokensIndex[tokenId];
      
              // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
              // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
              // an 'if' statement (like in _removeTokenFromOwnerEnumeration)
              uint256 lastTokenId = _allTokens[lastTokenIndex];
      
              _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
              _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
      
              // This also deletes the contents at the last position of the array
              _allTokens.length--;
              _allTokensIndex[tokenId] = 0;
          }
      }
      
      library UintLibrary {
          function toString(uint256 _i) internal pure returns (string memory) {
              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);
          }
      }
      
      library StringLibrary {
          using UintLibrary for uint256;
      
          function append(string memory _a, string memory _b) internal pure returns (string memory) {
              bytes memory _ba = bytes(_a);
              bytes memory _bb = bytes(_b);
              bytes memory bab = new bytes(_ba.length + _bb.length);
              uint k = 0;
              for (uint i = 0; i < _ba.length; i++) bab[k++] = _ba[i];
              for (uint i = 0; i < _bb.length; i++) bab[k++] = _bb[i];
              return string(bab);
          }
      
          function append(string memory _a, string memory _b, string memory _c) internal pure returns (string memory) {
              bytes memory _ba = bytes(_a);
              bytes memory _bb = bytes(_b);
              bytes memory _bc = bytes(_c);
              bytes memory bbb = new bytes(_ba.length + _bb.length + _bc.length);
              uint k = 0;
              for (uint i = 0; i < _ba.length; i++) bbb[k++] = _ba[i];
              for (uint i = 0; i < _bb.length; i++) bbb[k++] = _bb[i];
              for (uint i = 0; i < _bc.length; i++) bbb[k++] = _bc[i];
              return string(bbb);
          }
      
          function recover(string memory message, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
              bytes memory msgBytes = bytes(message);
              bytes memory fullMessage = concat(
                  bytes("\x19Ethereum Signed Message:\n"),
                  bytes(msgBytes.length.toString()),
                  msgBytes,
                  new bytes(0), new bytes(0), new bytes(0), new bytes(0)
              );
              return ecrecover(keccak256(fullMessage), v, r, s);
          }
      
          function concat(bytes memory _ba, bytes memory _bb, bytes memory _bc, bytes memory _bd, bytes memory _be, bytes memory _bf, bytes memory _bg) internal pure returns (bytes memory) {
              bytes memory resultBytes = new bytes(_ba.length + _bb.length + _bc.length + _bd.length + _be.length + _bf.length + _bg.length);
              uint k = 0;
              for (uint i = 0; i < _ba.length; i++) resultBytes[k++] = _ba[i];
              for (uint i = 0; i < _bb.length; i++) resultBytes[k++] = _bb[i];
              for (uint i = 0; i < _bc.length; i++) resultBytes[k++] = _bc[i];
              for (uint i = 0; i < _bd.length; i++) resultBytes[k++] = _bd[i];
              for (uint i = 0; i < _be.length; i++) resultBytes[k++] = _be[i];
              for (uint i = 0; i < _bf.length; i++) resultBytes[k++] = _bf[i];
              for (uint i = 0; i < _bg.length; i++) resultBytes[k++] = _bg[i];
              return resultBytes;
          }
      }
      
      contract HasContractURI is ERC165 {
      
          string public contractURI;
      
          /*
           * bytes4(keccak256('contractURI()')) == 0xe8a3d485
           */
          bytes4 private constant _INTERFACE_ID_CONTRACT_URI = 0xe8a3d485;
      
          constructor(string memory _contractURI) public {
              contractURI = _contractURI;
              _registerInterface(_INTERFACE_ID_CONTRACT_URI);
          }
      
          /**
           * @dev Internal function to set the contract URI
           * @param _contractURI string URI prefix to assign
           */
          function _setContractURI(string memory _contractURI) internal {
              contractURI = _contractURI;
          }
      }
      
      contract HasTokenURI {
          using StringLibrary for string;
      
          //Token URI prefix
          string public tokenURIPrefix;
      
          // Optional mapping for token URIs
          mapping(uint256 => string) private _tokenURIs;
      
          constructor(string memory _tokenURIPrefix) public {
              tokenURIPrefix = _tokenURIPrefix;
          }
      
          /**
           * @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) internal view returns (string memory) {
              return tokenURIPrefix.append(_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 {
              _tokenURIs[tokenId] = uri;
          }
      
          /**
           * @dev Internal function to set the token URI prefix.
           * @param _tokenURIPrefix string URI prefix to assign
           */
          function _setTokenURIPrefix(string memory _tokenURIPrefix) internal {
              tokenURIPrefix = _tokenURIPrefix;
          }
      
          function _clearTokenURI(uint256 tokenId) internal {
              if (bytes(_tokenURIs[tokenId]).length != 0) {
                  delete _tokenURIs[tokenId];
              }
          }
      }
      
      contract HasSecondarySaleFees is ERC165 {
      
          event SecondarySaleFees(uint256 tokenId, address[] recipients, uint[] bps);
      
          /*
           * bytes4(keccak256('getFeeBps(uint256)')) == 0x0ebd4c7f
           * bytes4(keccak256('getFeeRecipients(uint256)')) == 0xb9c4d9fb
           *
           * => 0x0ebd4c7f ^ 0xb9c4d9fb == 0xb7799584
           */
          bytes4 private constant _INTERFACE_ID_FEES = 0xb7799584;
      
          constructor() public {
              _registerInterface(_INTERFACE_ID_FEES);
          }
      
          function getFeeRecipients(uint256 id) public view returns (address payable[] memory);
          function getFeeBps(uint256 id) public view returns (uint[] memory);
      }
      
      /**
       * @title Full ERC721 Token with support for tokenURIPrefix
       * 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 ERC721Base is HasSecondarySaleFees, ERC721, HasContractURI, HasTokenURI, ERC721Enumerable {
          // Token name
          string public name;
      
          // Token symbol
          string public symbol;
      
          struct Fee {
              address payable recipient;
              uint256 value;
          }
      
          // id => fees
          mapping (uint256 => Fee[]) public fees;
      
          /*
           *     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, string memory contractURI, string memory _tokenURIPrefix) HasContractURI(contractURI) HasTokenURI(_tokenURIPrefix) public {
              name = _name;
              symbol = _symbol;
      
              // register the supported interfaces to conform to ERC721 via ERC165
              _registerInterface(_INTERFACE_ID_ERC721_METADATA);
          }
      
          function getFeeRecipients(uint256 id) public view returns (address payable[] memory) {
              Fee[] memory _fees = fees[id];
              address payable[] memory result = new address payable[](_fees.length);
              for (uint i = 0; i < _fees.length; i++) {
                  result[i] = _fees[i].recipient;
              }
              return result;
          }
      
          function getFeeBps(uint256 id) public view returns (uint[] memory) {
              Fee[] memory _fees = fees[id];
              uint[] memory result = new uint[](_fees.length);
              for (uint i = 0; i < _fees.length; i++) {
                  result[i] = _fees[i].value;
              }
              return result;
          }
      
          function _mint(address to, uint256 tokenId, Fee[] memory _fees) internal {
              _mint(to, tokenId);
              address[] memory recipients = new address[](_fees.length);
              uint[] memory bps = new uint[](_fees.length);
              for (uint i = 0; i < _fees.length; i++) {
                  require(_fees[i].recipient != address(0x0), "Recipient should be present");
                  require(_fees[i].value != 0, "Fee value should be positive");
                  fees[tokenId].push(_fees[i]);
                  recipients[i] = _fees[i].recipient;
                  bps[i] = _fees[i].value;
              }
              if (_fees.length > 0) {
                  emit SecondarySaleFees(tokenId, recipients, bps);
              }
          }
      
          /**
           * @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 super._tokenURI(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");
              super._setTokenURI(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);
              _clearTokenURI(tokenId);
          }
      }
      
      
      
      
      
      
      
      /**
       * @title MintableToken
       * @dev anyone can mint token.
       */
      contract MintableToken is Ownable, IERC721, IERC721Metadata, ERC721Burnable, ERC721Base {
      
          constructor (string memory name, string memory symbol, address newOwner, string memory contractURI, string memory tokenURIPrefix) public ERC721Base(name, symbol, contractURI, tokenURIPrefix) {
              _registerInterface(bytes4(keccak256('MINT_WITH_ADDRESS')));
              transferOwnership(newOwner);
          }
      
          function mint(uint256 tokenId, uint8 v, bytes32 r, bytes32 s, Fee[] memory _fees, string memory tokenURI) public {
              require(owner() == ecrecover(keccak256(abi.encodePacked(this, tokenId)), v, r, s), "owner should sign tokenId");
              _mint(msg.sender, tokenId, _fees);
              _setTokenURI(tokenId, tokenURI);
          }
      
          function setTokenURIPrefix(string memory tokenURIPrefix) public onlyOwner {
              _setTokenURIPrefix(tokenURIPrefix);
          }
      
          function setContractURI(string memory contractURI) public onlyOwner {
              _setContractURI(contractURI);
          }
      }