ETH Price: $2,521.16 (-3.77%)

Transaction Decoder

Block:
14507236 at Apr-02-2022 02:22:02 PM +UTC
Transaction Fee:
0.00544353872591394 ETH $13.72
Gas Used:
124,479 Gas / 43.73057886 Gwei

Emitted Events:

233 AdminUpgradeabilityProxy.0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb( 0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb, 0x000000000000000000000000a21342f796996954284b8dc6aae7ecbf8f83a9e4, 0x000000000000000000000000a21342f796996954284b8dc6aae7ecbf8f83a9e4, 0x000000000000000000000000c84ff195c904fcb2ee5ba8e70f8b84b2635a1bf2, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000080, 0000000000000000000000000000000000000000000000000000000000000001, 7a9fe22691c811ea339d9b73150e6911a5343dca000000000000000040800800, 0000000000000000000000000000000000000000000000000000000000000001, 0000000000000000000000000000000000000000000000000000000000000001 )
234 MultiGiveaway.ClaimedMultipleTokens( to=[Sender] 0xc84ff195c904fcb2ee5ba8e70f8b84b2635a1bf2, erc1155=, erc721=, erc20=[{name:amounts, type:uint256[], order:1, indexed:false, value:[], valueString:[]}, {name:contractAddresses, type:address[], order:2, indexed:false, value:[], valueString:[]}] )

Account State Difference:

  Address   Before After State Difference Code
(SBI Crypto Pool)
521.81017654280833394 Eth521.81036326130833394 Eth0.0001867185
0xa21342f7...F8f83A9e4
0xa342f5D8...7f4478dB5
0xC84FF195...2635a1bF2
0.01369867419678 Eth
Nonce: 4
0.00825513547086606 Eth
Nonce: 5
0.00544353872591394

Execution Trace

MultiGiveaway.claimMultipleTokens( )
  • AdminUpgradeabilityProxy.2eb2c2d6( )
    • Asset.safeBatchTransferFrom( from=0xa21342f796996954284B8DC6AAe7ecBF8f83A9e4, to=0xC84FF195c904fCB2Ee5ba8E70F8B84B2635a1bF2, ids=[55464657044963196816950587289035428064568320970692304673817341489688470358016], values=[1], data=0x )
      claimMultipleTokens[MultiGiveaway (ln:60)]
      File 1 of 3: MultiGiveaway
      //SPDX-License-Identifier: MIT
      pragma solidity 0.8.2;
      import "@openzeppelin/contracts-0.8/token/ERC721/IERC721Receiver.sol";
      import "@openzeppelin/contracts-0.8/token/ERC1155/IERC1155Receiver.sol";
      import "./ClaimERC1155ERC721ERC20.sol";
      import "../../common/BaseWithStorage/WithAdmin.sol";
      /// @title MultiGiveaway contract.
      /// @notice This contract manages claims for multiple token types.
      contract MultiGiveaway is WithAdmin, ClaimERC1155ERC721ERC20 {
          ///////////////////////////////  Data //////////////////////////////
          bytes4 private constant ERC1155_RECEIVED = 0xf23a6e61;
          bytes4 private constant ERC1155_BATCH_RECEIVED = 0xbc197c81;
          bytes4 internal constant ERC721_RECEIVED = 0x150b7a02;
          bytes4 internal constant ERC721_BATCH_RECEIVED = 0x4b808c46;
          mapping(address => mapping(bytes32 => bool)) public claimed;
          mapping(bytes32 => uint256) internal _expiryTime;
          ///////////////////////////////  Events //////////////////////////////
          event NewGiveaway(bytes32 merkleRoot, uint256 expiryTime);
          ///////////////////////////////  Constructor /////////////////////////
          constructor(address admin) {
              _admin = admin;
          }
          ///////////////////////////////  Functions ///////////////////////////
          /// @notice Function to add a new giveaway.
          /// @param merkleRoot The merkle root hash of the claim data.
          /// @param expiryTime The expiry time for the giveaway.
          function addNewGiveaway(bytes32 merkleRoot, uint256 expiryTime) external onlyAdmin {
              _expiryTime[merkleRoot] = expiryTime;
              emit NewGiveaway(merkleRoot, expiryTime);
          }
          /// @notice Function to check which giveaways have been claimed by a particular user.
          /// @param user The user (intended token destination) address.
          /// @param rootHashes The array of giveaway root hashes to check.
          /// @return claimedGiveaways The array of bools confirming whether or not the giveaways relating to the root hashes provided have been claimed.
          function getClaimedStatus(address user, bytes32[] calldata rootHashes) external view returns (bool[] memory) {
              bool[] memory claimedGiveaways = new bool[](rootHashes.length);
              for (uint256 i = 0; i < rootHashes.length; i++) {
                  claimedGiveaways[i] = claimed[user][rootHashes[i]];
              }
              return claimedGiveaways;
          }
          /// @notice Function to permit the claiming of multiple tokens from multiple giveaways to a reserved address.
          /// @param claims The array of claim structs, each containing a destination address, the giveaway items to be claimed and an optional salt param.
          /// @param proofs The proofs submitted for verification.
          function claimMultipleTokensFromMultipleMerkleTree(
              bytes32[] calldata rootHashes,
              Claim[] memory claims,
              bytes32[][] calldata proofs
          ) external {
              require(claims.length == rootHashes.length, "INVALID_INPUT");
              require(claims.length == proofs.length, "INVALID_INPUT");
              for (uint256 i = 0; i < rootHashes.length; i++) {
                  claimMultipleTokens(rootHashes[i], claims[i], proofs[i]);
              }
          }
          /// @dev Public function used to perform validity checks and progress to claim multiple token types in one claim.
          /// @param merkleRoot The merkle root hash for the specific set of items being claimed.
          /// @param claim The claim struct containing the destination address, all items to be claimed and optional salt param.
          /// @param proof The proof provided by the user performing the claim function.
          function claimMultipleTokens(
              bytes32 merkleRoot,
              Claim memory claim,
              bytes32[] calldata proof
          ) public {
              uint256 giveawayExpiryTime = _expiryTime[merkleRoot];
              require(claim.to != address(0), "INVALID_TO_ZERO_ADDRESS");
              require(claim.to != address(this), "DESTINATION_MULTIGIVEAWAY_CONTRACT");
              require(giveawayExpiryTime != 0, "GIVEAWAY_DOES_NOT_EXIST");
              require(block.timestamp < giveawayExpiryTime, "CLAIM_PERIOD_IS_OVER");
              require(claimed[claim.to][merkleRoot] == false, "DESTINATION_ALREADY_CLAIMED");
              claimed[claim.to][merkleRoot] = true;
              _claimERC1155ERC721ERC20(merkleRoot, claim, proof);
          }
          function onERC721Received(
              address, /*operator*/
              address, /*from*/
              uint256, /*id*/
              bytes calldata /*data*/
          ) external pure returns (bytes4) {
              return ERC721_RECEIVED;
          }
          function onERC721BatchReceived(
              address, /*operator*/
              address, /*from*/
              uint256[] calldata, /*ids*/
              bytes calldata /*data*/
          ) external pure returns (bytes4) {
              return ERC721_BATCH_RECEIVED;
          }
          function onERC1155Received(
              address, /*operator*/
              address, /*from*/
              uint256, /*id*/
              uint256, /*value*/
              bytes calldata /*data*/
          ) external pure returns (bytes4) {
              return ERC1155_RECEIVED;
          }
          function onERC1155BatchReceived(
              address, /*operator*/
              address, /*from*/
              uint256[] calldata, /*ids*/
              uint256[] calldata, /*values*/
              bytes calldata /*data*/
          ) external pure returns (bytes4) {
              return ERC1155_BATCH_RECEIVED;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      /**
       * @title ERC721 token receiver interface
       * @dev Interface for any contract that wants to support safeTransfers
       * from ERC721 asset contracts.
       */
      interface IERC721Receiver {
          /**
           * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
           * by `operator` from `from`, this function is called.
           *
           * It must return its Solidity selector to confirm the token transfer.
           * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
           *
           * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
           */
          function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../../utils/introspection/IERC165.sol";
      /**
       * _Available since v3.1._
       */
      interface IERC1155Receiver is IERC165 {
          /**
              @dev Handles the receipt of a single ERC1155 token type. This function is
              called at the end of a `safeTransferFrom` after the balance has been updated.
              To accept the transfer, this must return
              `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
              (i.e. 0xf23a6e61, or its own function selector).
              @param operator The address which initiated the transfer (i.e. msg.sender)
              @param from The address which previously owned the token
              @param id The ID of the token being transferred
              @param value The amount of tokens being transferred
              @param data Additional data with no specified format
              @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
          */
          function onERC1155Received(
              address operator,
              address from,
              uint256 id,
              uint256 value,
              bytes calldata data
          )
              external
              returns(bytes4);
          /**
              @dev Handles the receipt of a multiple ERC1155 token types. This function
              is called at the end of a `safeBatchTransferFrom` after the balances have
              been updated. To accept the transfer(s), this must return
              `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
              (i.e. 0xbc197c81, or its own function selector).
              @param operator The address which initiated the batch transfer (i.e. msg.sender)
              @param from The address which previously owned the token
              @param ids An array containing ids of each token being transferred (order and length must match values array)
              @param values An array containing amounts of each token being transferred (order and length must match ids array)
              @param data Additional data with no specified format
              @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
          */
          function onERC1155BatchReceived(
              address operator,
              address from,
              uint256[] calldata ids,
              uint256[] calldata values,
              bytes calldata data
          )
              external
              returns(bytes4);
      }
      //SPDX-License-Identifier: MIT
      pragma solidity 0.8.2;
      import "@openzeppelin/contracts-0.8/token/ERC1155/IERC1155.sol";
      import "@openzeppelin/contracts-0.8/token/ERC20/IERC20.sol";
      import "@openzeppelin/contracts-0.8/token/ERC20/utils/SafeERC20.sol";
      import "../../common/interfaces/IERC721Extended.sol";
      import "../../common/Libraries/Verify.sol";
      contract ClaimERC1155ERC721ERC20 {
          ///////////////////////////////  Libs //////////////////////////////
          using SafeERC20 for IERC20;
          ///////////////////////////////  Data //////////////////////////////
          struct Claim {
              address to;
              ERC1155Claim[] erc1155;
              ERC721Claim[] erc721;
              ERC20Claim erc20;
              bytes32 salt;
          }
          struct ERC1155Claim {
              uint256[] ids;
              uint256[] values;
              address contractAddress;
          }
          struct ERC721Claim {
              uint256[] ids;
              address contractAddress;
          }
          struct ERC20Claim {
              uint256[] amounts;
              address[] contractAddresses;
          }
          ///////////////////////////////  Events //////////////////////////////
          /// @dev Emits when a successful claim occurs.
          /// @param to The destination address for the claimed ERC1155, ERC721 and ERC20 tokens.
          /// @param erc1155 The array of ERC1155Claim structs containing the ids, values and ERC1155 contract address.
          /// @param erc721 The array of ERC721Claim structs containing the ids and ERC721 contract address.
          /// @param erc20 The ERC20Claim struct containing the amounts and ERC20 contract addresses.
          event ClaimedMultipleTokens(address to, ERC1155Claim[] erc1155, ERC721Claim[] erc721, ERC20Claim erc20);
          ///////////////////////////////  Functions ///////////////////////////
          /// @dev Internal function used to claim multiple token types in one claim.
          /// @param merkleRoot The merkle root hash for the specific set of items being claimed.
          /// @param claim The claim struct containing the destination address, all items to be claimed and optional salt param.
          /// @param proof The proof provided by the user performing the claim function.
          function _claimERC1155ERC721ERC20(
              bytes32 merkleRoot,
              Claim memory claim,
              bytes32[] calldata proof
          ) internal {
              _checkValidity(merkleRoot, claim, proof);
              for (uint256 i = 0; i < claim.erc1155.length; i++) {
                  require(claim.erc1155[i].ids.length == claim.erc1155[i].values.length, "INVALID_INPUT");
                  _transferERC1155(claim.to, claim.erc1155[i].ids, claim.erc1155[i].values, claim.erc1155[i].contractAddress);
              }
              for (uint256 i = 0; i < claim.erc721.length; i++) {
                  _transferERC721(claim.to, claim.erc721[i].ids, claim.erc721[i].contractAddress);
              }
              if (claim.erc20.amounts.length != 0) {
                  require(claim.erc20.amounts.length == claim.erc20.contractAddresses.length, "INVALID_INPUT");
                  _transferERC20(claim.to, claim.erc20.amounts, claim.erc20.contractAddresses);
              }
              emit ClaimedMultipleTokens(claim.to, claim.erc1155, claim.erc721, claim.erc20);
          }
          /// @dev Private function used to check the validity of a specific claim.
          /// @param merkleRoot The merkle root hash for the specific set of items being claimed.
          /// @param claim The claim struct containing the destination address, all items to be claimed and optional salt param.
          /// @param proof The proof provided by the user performing the claim function.
          function _checkValidity(
              bytes32 merkleRoot,
              Claim memory claim,
              bytes32[] memory proof
          ) private pure {
              bytes32 leaf = _generateClaimHash(claim);
              require(Verify.doesComputedHashMatchMerkleRootHash(merkleRoot, proof, leaf), "INVALID_CLAIM");
          }
          /// @dev Private function used to generate a hash from an encoded claim.
          /// @param claim The claim struct.
          function _generateClaimHash(Claim memory claim) private pure returns (bytes32) {
              return keccak256(abi.encode(claim));
          }
          /// @dev Private function used to transfer the ERC1155 tokens specified in a specific claim.
          /// @param to The destination address for the claimed tokens.
          /// @param ids The array of ERC1155 ids.
          /// @param values The amount of ERC1155 tokens of each id to be transferred.
          /// @param contractAddress The ERC1155 token contract address.
          function _transferERC1155(
              address to,
              uint256[] memory ids,
              uint256[] memory values,
              address contractAddress
          ) private {
              require(contractAddress != address(0), "INVALID_CONTRACT_ZERO_ADDRESS");
              IERC1155(contractAddress).safeBatchTransferFrom(address(this), to, ids, values, "");
          }
          /// @dev Private function used to transfer the ERC721tokens specified in a specific claim.
          /// @param to The destination address for the claimed tokens.
          /// @param ids The array of ERC721 ids.
          /// @param contractAddress The ERC721 token contract address.
          function _transferERC721(
              address to,
              uint256[] memory ids,
              address contractAddress
          ) private {
              require(contractAddress != address(0), "INVALID_CONTRACT_ZERO_ADDRESS");
              IERC721Extended(contractAddress).safeBatchTransferFrom(address(this), to, ids, "");
          }
          /// @dev Private function used to transfer the ERC20 tokens specified in a specific claim.
          /// @param to The destination address for the claimed tokens.
          /// @param amounts The array of amounts of ERC20 tokens to be transferred.
          /// @param contractAddresses The array of ERC20 token contract addresses.
          function _transferERC20(
              address to,
              uint256[] memory amounts,
              address[] memory contractAddresses
          ) private {
              for (uint256 i = 0; i < amounts.length; i++) {
                  address erc20ContractAddress = contractAddresses[i];
                  uint256 erc20Amount = amounts[i];
                  require(erc20ContractAddress != address(0), "INVALID_CONTRACT_ZERO_ADDRESS");
                  IERC20(erc20ContractAddress).safeTransferFrom(address(this), to, erc20Amount);
              }
          }
      }
      //SPDX-License-Identifier: MIT
      // solhint-disable-next-line compiler-version
      pragma solidity 0.8.2;
      contract WithAdmin {
          address internal _admin;
          /// @dev Emits when the contract administrator is changed.
          /// @param oldAdmin The address of the previous administrator.
          /// @param newAdmin The address of the new administrator.
          event AdminChanged(address oldAdmin, address newAdmin);
          modifier onlyAdmin() {
              require(msg.sender == _admin, "ADMIN_ONLY");
              _;
          }
          /// @dev Get the current administrator of this contract.
          /// @return The current administrator of this contract.
          function getAdmin() external view returns (address) {
              return _admin;
          }
          /// @dev Change the administrator to be `newAdmin`.
          /// @param newAdmin The address of the new administrator.
          function changeAdmin(address newAdmin) external {
              require(msg.sender == _admin, "ADMIN_ACCESS_DENIED");
              emit AdminChanged(_admin, newAdmin);
              _admin = newAdmin;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      /**
       * @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);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../../utils/introspection/IERC165.sol";
      /**
       * @dev Required interface of an ERC1155 compliant contract, as defined in the
       * https://eips.ethereum.org/EIPS/eip-1155[EIP].
       *
       * _Available since v3.1._
       */
      interface IERC1155 is IERC165 {
          /**
           * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
           */
          event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
          /**
           * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
           * transfers.
           */
          event TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values);
          /**
           * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
           * `approved`.
           */
          event ApprovalForAll(address indexed account, address indexed operator, bool approved);
          /**
           * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
           *
           * If an {URI} event was emitted for `id`, the standard
           * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
           * returned by {IERC1155MetadataURI-uri}.
           */
          event URI(string value, uint256 indexed id);
          /**
           * @dev Returns the amount of tokens of token type `id` owned by `account`.
           *
           * Requirements:
           *
           * - `account` cannot be the zero address.
           */
          function balanceOf(address account, uint256 id) external view returns (uint256);
          /**
           * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
           *
           * Requirements:
           *
           * - `accounts` and `ids` must have the same length.
           */
          function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory);
          /**
           * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
           *
           * Emits an {ApprovalForAll} event.
           *
           * Requirements:
           *
           * - `operator` cannot be the caller.
           */
          function setApprovalForAll(address operator, bool approved) external;
          /**
           * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
           *
           * See {setApprovalForAll}.
           */
          function isApprovedForAll(address account, address operator) external view returns (bool);
          /**
           * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
           *
           * Emits a {TransferSingle} event.
           *
           * Requirements:
           *
           * - `to` cannot be the zero address.
           * - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}.
           * - `from` must have a balance of tokens of type `id` of at least `amount`.
           * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
           * acceptance magic value.
           */
          function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;
          /**
           * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
           *
           * Emits a {TransferBatch} event.
           *
           * Requirements:
           *
           * - `ids` and `amounts` must have the same length.
           * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
           * acceptance magic value.
           */
          function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      /**
       * @dev Interface of the ERC20 standard as defined in the EIP.
       */
      interface IERC20 {
          /**
           * @dev Returns the amount of tokens in existence.
           */
          function totalSupply() external view returns (uint256);
          /**
           * @dev Returns the amount of tokens owned by `account`.
           */
          function balanceOf(address account) external view returns (uint256);
          /**
           * @dev Moves `amount` tokens from the caller's account to `recipient`.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transfer(address recipient, uint256 amount) external returns (bool);
          /**
           * @dev Returns the remaining number of tokens that `spender` will be
           * allowed to spend on behalf of `owner` through {transferFrom}. This is
           * zero by default.
           *
           * This value changes when {approve} or {transferFrom} are called.
           */
          function allowance(address owner, address spender) external view returns (uint256);
          /**
           * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * IMPORTANT: Beware that changing an allowance with this method brings the risk
           * that someone may use both the old and the new allowance by unfortunate
           * transaction ordering. One possible solution to mitigate this race
           * condition is to first reduce the spender's allowance to 0 and set the
           * desired value afterwards:
           * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
           *
           * Emits an {Approval} event.
           */
          function approve(address spender, uint256 amount) external returns (bool);
          /**
           * @dev Moves `amount` tokens from `sender` to `recipient` using the
           * allowance mechanism. `amount` is then deducted from the caller's
           * allowance.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
          /**
           * @dev Emitted when `value` tokens are moved from one account (`from`) to
           * another (`to`).
           *
           * Note that `value` may be zero.
           */
          event Transfer(address indexed from, address indexed to, uint256 value);
          /**
           * @dev Emitted when the allowance of a `spender` for an `owner` is set by
           * a call to {approve}. `value` is the new allowance.
           */
          event Approval(address indexed owner, address indexed spender, uint256 value);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../IERC20.sol";
      import "../../../utils/Address.sol";
      /**
       * @title SafeERC20
       * @dev Wrappers around ERC20 operations that throw on failure (when the token
       * contract returns false). Tokens that return no value (and instead revert or
       * throw on failure) are also supported, non-reverting calls are assumed to be
       * successful.
       * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
       * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
       */
      library SafeERC20 {
          using Address for address;
          function safeTransfer(IERC20 token, address to, uint256 value) internal {
              _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
          }
          function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
              _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
          }
          /**
           * @dev Deprecated. This function has issues similar to the ones found in
           * {IERC20-approve}, and its usage is discouraged.
           *
           * Whenever possible, use {safeIncreaseAllowance} and
           * {safeDecreaseAllowance} instead.
           */
          function safeApprove(IERC20 token, address spender, uint256 value) internal {
              // safeApprove should only be called when setting an initial allowance,
              // or when resetting it to zero. To increase and decrease it, use
              // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
              // solhint-disable-next-line max-line-length
              require((value == 0) || (token.allowance(address(this), spender) == 0),
                  "SafeERC20: approve from non-zero to non-zero allowance"
              );
              _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
          }
          function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
              uint256 newAllowance = token.allowance(address(this), spender) + value;
              _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
          }
          function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
              unchecked {
                  uint256 oldAllowance = token.allowance(address(this), spender);
                  require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                  uint256 newAllowance = oldAllowance - value;
                  _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
              }
          }
          /**
           * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
           * on the return value: the return value is optional (but if data is returned, it must not be false).
           * @param token The token targeted by the call.
           * @param data The call data (encoded using abi.encode or one of its variants).
           */
          function _callOptionalReturn(IERC20 token, bytes memory data) private {
              // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
              // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
              // the target address contains contract code and also asserts for success in the low-level call.
              bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
              if (returndata.length > 0) { // Return data is optional
                  // solhint-disable-next-line max-line-length
                  require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
              }
          }
      }
      //SPDX-License-Identifier: MIT
      pragma solidity 0.8.2;
      import "@openzeppelin/contracts-0.8/token/ERC721/IERC721.sol";
      interface IERC721Extended is IERC721 {
          function approveFor(
              address sender,
              address operator,
              uint256 id
          ) external;
          function batchTransferFrom(
              address from,
              address to,
              uint256[] calldata ids,
              bytes calldata data
          ) external;
          function safeBatchTransferFrom(
              address from,
              address to,
              uint256[] calldata ids,
              bytes calldata data
          ) external;
          function setApprovalForAllFor(
              address sender,
              address operator,
              bool approved
          ) external;
          function burn(uint256 id) external;
          function burnFrom(address from, uint256 id) external;
      }
      //SPDX-License-Identifier: MIT
      pragma solidity 0.8.2;
      /**
       * @title Verify
       * @dev Merkle root comparison function.
       */
      library Verify {
          /// @dev Check if the computedHash == comparisonHash.
          /// @param comparisonHash The merkle root hash passed to the function.
          /// @param proof The proof provided by the user.
          /// @param leaf The generated hash.
          /// @return Whether the computedHash == comparisonHash.
          function doesComputedHashMatchMerkleRootHash(
              bytes32 comparisonHash,
              bytes32[] memory proof,
              bytes32 leaf
          ) internal pure returns (bool) {
              bytes32 computedHash = leaf;
              for (uint256 i = 0; i < proof.length; i++) {
                  bytes32 proofElement = proof[i];
                  if (computedHash < proofElement) {
                      computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
                  } else {
                      computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
                  }
              }
              return computedHash == comparisonHash;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      /**
       * @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) {
              // This method relies on 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;
          }
          /**
           * @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].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              require(address(this).balance >= amount, "Address: insufficient balance");
              // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
              (bool success, ) = recipient.call{ value: amount }("");
              require(success, "Address: unable to send value, recipient may have reverted");
          }
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain`call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason, it is bubbled up by this
           * function (like regular Solidity function calls).
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCall(target, data, "Address: low-level call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
           * `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
              return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
          }
          /**
           * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
           * with `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
              require(address(this).balance >= value, "Address: insufficient balance for call");
              require(isContract(target), "Address: call to non-contract");
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.call{ value: value }(data);
              return _verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
              return functionStaticCall(target, data, "Address: low-level static call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
              require(isContract(target), "Address: static call to non-contract");
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.staticcall(data);
              return _verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionDelegateCall(target, data, "Address: low-level delegate call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
              require(isContract(target), "Address: delegate call to non-contract");
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.delegatecall(data);
              return _verifyCallResult(success, returndata, errorMessage);
          }
          function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
              if (success) {
                  return returndata;
              } else {
                  // Look for revert reason and bubble it up if present
                  if (returndata.length > 0) {
                      // The easiest way to bubble the revert reason is using memory via assembly
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../../utils/introspection/IERC165.sol";
      /**
       * @dev Required interface of an ERC721 compliant contract.
       */
      interface IERC721 is IERC165 {
          /**
           * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
           */
          event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
          /**
           * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
           */
          event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
          /**
           * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
           */
          event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
          /**
           * @dev Returns the number of tokens in ``owner``'s account.
           */
          function balanceOf(address owner) external view returns (uint256 balance);
          /**
           * @dev Returns the owner of the `tokenId` token.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           */
          function ownerOf(uint256 tokenId) external view returns (address owner);
          /**
           * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
           * are aware of the ERC721 protocol to prevent tokens from being forever locked.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `tokenId` token must exist and be owned by `from`.
           * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
           * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
           *
           * Emits a {Transfer} event.
           */
          function safeTransferFrom(address from, address to, uint256 tokenId) external;
          /**
           * @dev Transfers `tokenId` token from `from` to `to`.
           *
           * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `tokenId` token must be owned by `from`.
           * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
           *
           * Emits a {Transfer} event.
           */
          function transferFrom(address from, address to, uint256 tokenId) external;
          /**
           * @dev Gives permission to `to` to transfer `tokenId` token to another account.
           * The approval is cleared when the token is transferred.
           *
           * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
           *
           * Requirements:
           *
           * - The caller must own the token or be an approved operator.
           * - `tokenId` must exist.
           *
           * Emits an {Approval} event.
           */
          function approve(address to, uint256 tokenId) external;
          /**
           * @dev Returns the account approved for `tokenId` token.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           */
          function getApproved(uint256 tokenId) external view returns (address operator);
          /**
           * @dev Approve or remove `operator` as an operator for the caller.
           * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
           *
           * Requirements:
           *
           * - The `operator` cannot be the caller.
           *
           * Emits an {ApprovalForAll} event.
           */
          function setApprovalForAll(address operator, bool _approved) external;
          /**
           * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
           *
           * See {setApprovalForAll}
           */
          function isApprovedForAll(address owner, address operator) external view returns (bool);
          /**
            * @dev Safely transfers `tokenId` token from `from` to `to`.
            *
            * Requirements:
            *
            * - `from` cannot be the zero address.
            * - `to` cannot be the zero address.
            * - `tokenId` token must exist and be owned by `from`.
            * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
            * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
            *
            * Emits a {Transfer} event.
            */
          function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
      }
      

      File 2 of 3: AdminUpgradeabilityProxy
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.6.0;
      import './UpgradeabilityProxy.sol';
      /**
       * @title AdminUpgradeabilityProxy
       * @dev This contract combines an upgradeability proxy with an authorization
       * mechanism for administrative tasks.
       * All external functions in this contract must be guarded by the
       * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity
       * feature proposal that would enable this to be done automatically.
       */
      contract AdminUpgradeabilityProxy is UpgradeabilityProxy {
        /**
         * Contract constructor.
         * @param _logic address of the initial implementation.
         * @param _admin Address of the proxy administrator.
         * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
         * It should include the signature and the parameters of the function to be called, as described in
         * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
         * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
         */
        constructor(address _logic, address _admin, bytes memory _data) UpgradeabilityProxy(_logic, _data) public payable {
          assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1));
          _setAdmin(_admin);
        }
        /**
         * @dev Emitted when the administration has been transferred.
         * @param previousAdmin Address of the previous admin.
         * @param newAdmin Address of the new admin.
         */
        event AdminChanged(address previousAdmin, address newAdmin);
        /**
         * @dev Storage slot with the admin of the contract.
         * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
         * validated in the constructor.
         */
        bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
        /**
         * @dev Modifier to check whether the `msg.sender` is the admin.
         * If it is, it will run the function. Otherwise, it will delegate the call
         * to the implementation.
         */
        modifier ifAdmin() {
          if (msg.sender == _admin()) {
            _;
          } else {
            _fallback();
          }
        }
        /**
         * @return The address of the proxy admin.
         */
        function admin() external ifAdmin returns (address) {
          return _admin();
        }
        /**
         * @return The address of the implementation.
         */
        function implementation() external ifAdmin returns (address) {
          return _implementation();
        }
        /**
         * @dev Changes the admin of the proxy.
         * Only the current admin can call this function.
         * @param newAdmin Address to transfer proxy administration to.
         */
        function changeAdmin(address newAdmin) external ifAdmin {
          require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address");
          emit AdminChanged(_admin(), newAdmin);
          _setAdmin(newAdmin);
        }
        /**
         * @dev Upgrade the backing implementation of the proxy.
         * Only the admin can call this function.
         * @param newImplementation Address of the new implementation.
         */
        function upgradeTo(address newImplementation) external ifAdmin {
          _upgradeTo(newImplementation);
        }
        /**
         * @dev Upgrade the backing implementation of the proxy and call a function
         * on the new implementation.
         * This is useful to initialize the proxied contract.
         * @param newImplementation Address of the new implementation.
         * @param data Data to send as msg.data in the low level call.
         * It should include the signature and the parameters of the function to be called, as described in
         * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
         */
        function upgradeToAndCall(address newImplementation, bytes calldata data) payable external ifAdmin {
          _upgradeTo(newImplementation);
          (bool success,) = newImplementation.delegatecall(data);
          require(success);
        }
        /**
         * @return adm The admin slot.
         */
        function _admin() internal view returns (address adm) {
          bytes32 slot = ADMIN_SLOT;
          assembly {
            adm := sload(slot)
          }
        }
        /**
         * @dev Sets the address of the proxy admin.
         * @param newAdmin Address of the new proxy admin.
         */
        function _setAdmin(address newAdmin) internal {
          bytes32 slot = ADMIN_SLOT;
          assembly {
            sstore(slot, newAdmin)
          }
        }
        /**
         * @dev Only fall back when the sender is not the admin.
         */
        function _willFallback() internal override virtual {
          require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin");
          super._willFallback();
        }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.6.0;
      import './Proxy.sol';
      import '@openzeppelin/contracts/utils/Address.sol';
      /**
       * @title UpgradeabilityProxy
       * @dev This contract implements a proxy that allows to change the
       * implementation address to which it will delegate.
       * Such a change is called an implementation upgrade.
       */
      contract UpgradeabilityProxy is Proxy {
        /**
         * @dev Contract constructor.
         * @param _logic Address of the initial implementation.
         * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
         * It should include the signature and the parameters of the function to be called, as described in
         * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
         * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
         */
        constructor(address _logic, bytes memory _data) public payable {
          assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));
          _setImplementation(_logic);
          if(_data.length > 0) {
            (bool success,) = _logic.delegatecall(_data);
            require(success);
          }
        }  
        /**
         * @dev Emitted when the implementation is upgraded.
         * @param implementation Address of the new implementation.
         */
        event Upgraded(address indexed implementation);
        /**
         * @dev Storage slot with the address of the current implementation.
         * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
         * validated in the constructor.
         */
        bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
        /**
         * @dev Returns the current implementation.
         * @return impl Address of the current implementation
         */
        function _implementation() internal override view returns (address impl) {
          bytes32 slot = IMPLEMENTATION_SLOT;
          assembly {
            impl := sload(slot)
          }
        }
        /**
         * @dev Upgrades the proxy to a new implementation.
         * @param newImplementation Address of the new implementation.
         */
        function _upgradeTo(address newImplementation) internal {
          _setImplementation(newImplementation);
          emit Upgraded(newImplementation);
        }
        /**
         * @dev Sets the implementation address of the proxy.
         * @param newImplementation Address of the new implementation.
         */
        function _setImplementation(address newImplementation) internal {
          require(Address.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address");
          bytes32 slot = IMPLEMENTATION_SLOT;
          assembly {
            sstore(slot, newImplementation)
          }
        }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.6.0;
      /**
       * @title Proxy
       * @dev Implements delegation of calls to other contracts, with proper
       * forwarding of return values and bubbling of failures.
       * It defines a fallback function that delegates all calls to the address
       * returned by the abstract _implementation() internal function.
       */
      abstract contract Proxy {
        /**
         * @dev Fallback function.
         * Implemented entirely in `_fallback`.
         */
        fallback () payable external {
          _fallback();
        }
        /**
         * @dev Receive function.
         * Implemented entirely in `_fallback`.
         */
        receive () payable external {
          _fallback();
        }
        /**
         * @return The Address of the implementation.
         */
        function _implementation() internal virtual view returns (address);
        /**
         * @dev Delegates execution to an implementation contract.
         * This is a low level function that doesn't return to its internal call site.
         * It will return to the external caller whatever the implementation returns.
         * @param implementation Address to delegate.
         */
        function _delegate(address implementation) internal {
          assembly {
            // Copy msg.data. We take full control of memory in this inline assembly
            // block because it will not return to Solidity code. We overwrite the
            // Solidity scratch pad at memory position 0.
            calldatacopy(0, 0, calldatasize())
            // Call the implementation.
            // out and outsize are 0 because we don't know the size yet.
            let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
            // Copy the returned data.
            returndatacopy(0, 0, returndatasize())
            switch result
            // delegatecall returns 0 on error.
            case 0 { revert(0, returndatasize()) }
            default { return(0, returndatasize()) }
          }
        }
        /**
         * @dev Function that is run as the first thing in the fallback function.
         * Can be redefined in derived contracts to add functionality.
         * Redefinitions must call super._willFallback().
         */
        function _willFallback() internal virtual {
        }
        /**
         * @dev fallback implementation.
         * Extracted to enable manual triggering.
         */
        function _fallback() internal {
          _willFallback();
          _delegate(_implementation());
        }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity >=0.6.2 <0.8.0;
      /**
       * @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) {
              // This method relies on 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;
          }
          /**
           * @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].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              require(address(this).balance >= amount, "Address: insufficient balance");
              // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
              (bool success, ) = recipient.call{ value: amount }("");
              require(success, "Address: unable to send value, recipient may have reverted");
          }
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain`call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason, it is bubbled up by this
           * function (like regular Solidity function calls).
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCall(target, data, "Address: low-level call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
           * `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
              return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
          }
          /**
           * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
           * with `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
              require(address(this).balance >= value, "Address: insufficient balance for call");
              require(isContract(target), "Address: call to non-contract");
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.call{ value: value }(data);
              return _verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
              return functionStaticCall(target, data, "Address: low-level static call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
              require(isContract(target), "Address: static call to non-contract");
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.staticcall(data);
              return _verifyCallResult(success, returndata, errorMessage);
          }
          function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
              if (success) {
                  return returndata;
              } else {
                  // Look for revert reason and bubble it up if present
                  if (returndata.length > 0) {
                      // The easiest way to bubble the revert reason is using memory via assembly
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
      }
      

      File 3 of 3: Asset
      pragma solidity 0.5.9;
      import "./Asset/ERC1155ERC721.sol";
      contract Asset is ERC1155ERC721 {}
      pragma solidity 0.5.9;
      import "../contracts_common/Interfaces/ERC1155.sol";
      import "../contracts_common/Interfaces/ERC1155TokenReceiver.sol";
      import "../contracts_common/Libraries/AddressUtils.sol";
      import "../contracts_common/Libraries/ObjectLib32.sol";
      import "../contracts_common/Interfaces/ERC721.sol";
      import "../contracts_common/Interfaces/ERC721TokenReceiver.sol";
      import "../contracts_common/BaseWithStorage/SuperOperators.sol";
      contract ERC1155ERC721 is SuperOperators, ERC1155, ERC721 {
          using AddressUtils for address;
          using ObjectLib32 for ObjectLib32.Operations;
          using ObjectLib32 for uint256;
          bytes4 private constant ERC1155_IS_RECEIVER = 0x4e2312e0;
          bytes4 private constant ERC1155_RECEIVED = 0xf23a6e61;
          bytes4 private constant ERC1155_BATCH_RECEIVED = 0xbc197c81;
          bytes4 private constant ERC721_RECEIVED = 0x150b7a02;
          uint256 private constant CREATOR_OFFSET_MULTIPLIER = uint256(2)**(256 - 160);
          uint256 private constant IS_NFT_OFFSET_MULTIPLIER = uint256(2)**(256 - 160 - 1);
          uint256 private constant PACK_ID_OFFSET_MULTIPLIER = uint256(2)**(256 - 160 - 1 - 32 - 40);
          uint256 private constant PACK_NUM_FT_TYPES_OFFSET_MULTIPLIER = uint256(2)**(256 - 160 - 1 - 32 - 40 - 12);
          uint256 private constant NFT_INDEX_OFFSET = 63;
          uint256 private constant IS_NFT =            0x0000000000000000000000000000000000000000800000000000000000000000;
          uint256 private constant NOT_IS_NFT =        0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFFFFFFFFFFF;
          uint256 private constant NFT_INDEX =         0x00000000000000000000000000000000000000007FFFFFFF8000000000000000;
          uint256 private constant NOT_NFT_INDEX =     0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF800000007FFFFFFFFFFFFFFF;
          uint256 private constant URI_ID =            0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000007FFFFFFFFFFFF800;
          uint256 private constant PACK_ID =           0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000007FFFFFFFFF800000;
          uint256 private constant PACK_INDEX =        0x00000000000000000000000000000000000000000000000000000000000007FF;
          uint256 private constant PACK_NUM_FT_TYPES = 0x00000000000000000000000000000000000000000000000000000000007FF800;
          uint256 private constant MAX_SUPPLY = uint256(2)**32 - 1;
          uint256 private constant MAX_PACK_SIZE = uint256(2)**11;
          event CreatorshipTransfer(
              address indexed original,
              address indexed from,
              address indexed to
          );
          mapping(address => uint256) private _numNFTPerAddress; // erc721
          mapping(uint256 => uint256) private _owners; // erc721
          mapping(address => mapping(uint256 => uint256)) private _packedTokenBalance; // erc1155
          mapping(address => mapping(address => bool)) private _operatorsForAll; // erc721 and erc1155
          mapping(uint256 => address) private _erc721operators; // erc721
          mapping(uint256 => bytes32) private _metadataHash; // erc721 and erc1155
          mapping(uint256 => bytes) private _rarityPacks; // rarity configuration per packs (2 bits per Asset)
          mapping(uint256 => uint32) private _nextCollectionIndex; // extraction
          mapping(address => address) private _creatorship; // creatorship transfer
          mapping(address => bool) private _bouncers; // the contracts allowed to mint
          mapping(address => bool) private _metaTransactionContracts; // native meta-transaction support
          address private _bouncerAdmin;
          bool internal _init;
          function init(
              address metaTransactionContract,
              address admin,
              address bouncerAdmin
          ) public {
              require(!_init, "ALREADY_INITIALISED");
              _init = true;
              _metaTransactionContracts[metaTransactionContract] = true;
              _admin = admin;
              _bouncerAdmin = bouncerAdmin;
              emit MetaTransactionProcessor(metaTransactionContract, true);
          }
          event BouncerAdminChanged(address oldBouncerAdmin, address newBouncerAdmin);
          /// @notice Returns the current administrator in charge of minting rights.
          /// @return the current minting administrator in charge of minting rights.
          function getBouncerAdmin() external view returns(address) {
              return _bouncerAdmin;
          }
          /// @notice Change the minting administrator to be `newBouncerAdmin`.
          /// @param newBouncerAdmin address of the new minting administrator.
          function changeBouncerAdmin(address newBouncerAdmin) external {
              require(
                  msg.sender == _bouncerAdmin,
                  "only bouncerAdmin can change itself"
              );
              emit BouncerAdminChanged(_bouncerAdmin, newBouncerAdmin);
              _bouncerAdmin = newBouncerAdmin;
          }
          event Bouncer(address bouncer, bool enabled);
          /// @notice Enable or disable the ability of `bouncer` to mint tokens (minting bouncer rights).
          /// @param bouncer address that will be given/removed minting bouncer rights.
          /// @param enabled set whether the address is enabled or disabled as a minting bouncer.
          function setBouncer(address bouncer, bool enabled) external {
              require(
                  msg.sender == _bouncerAdmin,
                  "only bouncerAdmin can setup bouncers"
              );
              _bouncers[bouncer] = enabled;
              emit Bouncer(bouncer, enabled);
          }
          /// @notice check whether address `who` is given minting bouncer rights.
          /// @param who The address to query.
          /// @return whether the address has minting rights.
          function isBouncer(address who) external view returns(bool) {
              return _bouncers[who];
          }
          event MetaTransactionProcessor(address metaTransactionProcessor, bool enabled);
          /// @notice Enable or disable the ability of `metaTransactionProcessor` to perform meta-tx (metaTransactionProcessor rights).
          /// @param metaTransactionProcessor address that will be given/removed metaTransactionProcessor rights.
          /// @param enabled set whether the metaTransactionProcessor is enabled or disabled.
          function setMetaTransactionProcessor(address metaTransactionProcessor, bool enabled) external {
              require(
                  msg.sender == _admin,
                  "only admin can setup metaTransactionProcessors"
              );
              _metaTransactionContracts[metaTransactionProcessor] = enabled;
              emit MetaTransactionProcessor(metaTransactionProcessor, enabled);
          }
          /// @notice check whether address `who` is given meta-transaction execution rights.
          /// @param who The address to query.
          /// @return whether the address has meta-transaction execution rights.
          function isMetaTransactionProcessor(address who) external view returns(bool) {
              return _metaTransactionContracts[who];
          }
          /// @notice Mint a token type for `creator` on slot `packId`.
          /// @param creator address of the creator of the token.
          /// @param packId unique packId for that token.
          /// @param hash hash of an IPFS cidv1 folder that contains the metadata of the token type in the file 0.json.
          /// @param supply number of tokens minted for that token type.
          /// @param rarity rarity power of the token.
          /// @param owner address that will receive the tokens.
          /// @param data extra data to accompany the minting call.
          /// @return the id of the newly minted token type.
          function mint(
              address creator,
              uint40 packId,
              bytes32 hash,
              uint256 supply,
              uint8 rarity,
              address owner,
              bytes calldata data
          ) external returns (uint256 id) {
              require(hash != 0, "hash is zero");
              require(_bouncers[msg.sender], "only bouncer allowed to mint");
              require(owner != address(0), "destination is zero address");
              id = generateTokenId(creator, supply, packId, supply == 1 ? 0 : 1, 0);
              _mint(
                  hash,
                  supply,
                  rarity,
                  msg.sender,
                  owner,
                  id,
                  data,
                  false
              );
          }
          function generateTokenId(
              address creator,
              uint256 supply,
              uint40 packId,
              uint16 numFTs,
              uint16 packIndex
          ) internal pure returns (uint256) {
              require(supply > 0 && supply <= MAX_SUPPLY, "invalid supply");
              return
                  uint256(creator) * CREATOR_OFFSET_MULTIPLIER + // CREATOR
                  (supply == 1 ? uint256(1) * IS_NFT_OFFSET_MULTIPLIER : 0) + // minted as NFT (1) or FT (0) // IS_NFT
                  uint256(packId) * PACK_ID_OFFSET_MULTIPLIER + // packId (unique pack) // PACk_ID
                  numFTs * PACK_NUM_FT_TYPES_OFFSET_MULTIPLIER + // number of fungible token in the pack // PACK_NUM_FT_TYPES
                  packIndex; // packIndex (position in the pack) // PACK_INDEX
          }
          function _mint(
              bytes32 hash,
              uint256 supply,
              uint8 rarity,
              address operator,
              address owner,
              uint256 id,
              bytes memory data,
              bool extraction
          ) internal {
              uint256 uriId = id & URI_ID;
              if (!extraction) {
                  require(uint256(_metadataHash[uriId]) == 0, "id already used");
                  _metadataHash[uriId] = hash;
                  require(rarity < 4, "rarity >= 4");
                  bytes memory pack = new bytes(1);
                  pack[0] = bytes1(rarity * 64);
                  _rarityPacks[uriId] = pack;
              }
              if (supply == 1) {
                  // ERC721
                  _numNFTPerAddress[owner]++;
                  _owners[id] = uint256(owner);
                  emit Transfer(address(0), owner, id);
              } else {
                  (uint256 bin, uint256 index) = id.getTokenBinIndex();
                  _packedTokenBalance[owner][bin] = _packedTokenBalance[owner][bin]
                      .updateTokenBalance(
                      index,
                      supply,
                      ObjectLib32.Operations.REPLACE
                  );
              }
              emit TransferSingle(operator, address(0), owner, id, supply);
              require(
                  _checkERC1155AndCallSafeTransfer(
                      operator,
                      address(0),
                      owner,
                      id,
                      supply,
                      data,
                      false,
                      false
                  ),
                  "transfer rejected"
              );
          }
          /// @notice Mint multiple token types for `creator` on slot `packId`.
          /// @param creator address of the creator of the tokens.
          /// @param packId unique packId for the tokens.
          /// @param hash hash of an IPFS cidv1 folder that contains the metadata of each token type in the files: 0.json, 1.json, 2.json, etc...
          /// @param supplies number of tokens minted for each token type.
          /// @param rarityPack rarity power of each token types packed into 2 bits each.
          /// @param owner address that will receive the tokens.
          /// @param data extra data to accompany the minting call.
          /// @return the ids of each newly minted token types.
          function mintMultiple(
              address creator,
              uint40 packId,
              bytes32 hash,
              uint256[] calldata supplies,
              bytes calldata rarityPack,
              address owner,
              bytes calldata data
          ) external returns (uint256[] memory ids) {
              require(hash != 0, "hash is zero");
              require(_bouncers[msg.sender], "only bouncer allowed to mint");
              require(owner != address(0), "destination is zero address");
              uint16 numNFTs;
              (ids, numNFTs) = allocateIds(
                  creator,
                  supplies,
                  rarityPack,
                  packId,
                  hash
              );
              _mintBatches(supplies, owner, ids, numNFTs);
              completeMultiMint(msg.sender, owner, ids, supplies, data);
          }
          function allocateIds(
              address creator,
              uint256[] memory supplies,
              bytes memory rarityPack,
              uint40 packId,
              bytes32 hash
          ) internal returns (uint256[] memory ids, uint16 numNFTs) {
              require(supplies.length > 0, "supplies.length == 0");
              require(supplies.length <= MAX_PACK_SIZE, "too big batch");
              (ids, numNFTs) = generateTokenIds(creator, supplies, packId);
              uint256 uriId = ids[0] & URI_ID;
              require(uint256(_metadataHash[uriId]) == 0, "id already used");
              _metadataHash[uriId] = hash;
              _rarityPacks[uriId] = rarityPack;
          }
          function generateTokenIds(
              address creator,
              uint256[] memory supplies,
              uint40 packId
          ) internal pure returns (uint256[] memory, uint16) {
              uint16 numTokenTypes = uint16(supplies.length);
              uint256[] memory ids = new uint256[](numTokenTypes);
              uint16 numNFTs = 0;
              for (uint16 i = 0; i < numTokenTypes; i++) {
                  if (numNFTs == 0) {
                      if (supplies[i] == 1) {
                          numNFTs = uint16(numTokenTypes - i);
                      }
                  } else {
                      require(supplies[i] == 1, "NFTs need to be put at the end");
                  }
              }
              uint16 numFTs = numTokenTypes - numNFTs;
              for (uint16 i = 0; i < numTokenTypes; i++) {
                  ids[i] = generateTokenId(creator, supplies[i], packId, numFTs, i);
              }
              return (ids, numNFTs);
          }
          function completeMultiMint(
              address operator,
              address owner,
              uint256[] memory ids,
              uint256[] memory supplies,
              bytes memory data
          ) internal {
              emit TransferBatch(operator, address(0), owner, ids, supplies);
              require(
                  _checkERC1155AndCallSafeBatchTransfer(
                      operator,
                      address(0),
                      owner,
                      ids,
                      supplies,
                      data
                  ),
                  "transfer rejected"
              );
          }
          function _mintBatches(
              uint256[] memory supplies,
              address owner,
              uint256[] memory ids,
              uint16 numNFTs
          ) internal {
              uint16 offset = 0;
              while (offset < supplies.length - numNFTs) {
                  _mintBatch(offset, supplies, owner, ids);
                  offset += 8;
              }
              // deal with NFT last. they do not care of balance packing
              if (numNFTs > 0) {
                  _mintNFTs(
                      uint16(supplies.length - numNFTs),
                      numNFTs,
                      owner,
                      ids
                  );
              }
          }
          function _mintNFTs(
              uint16 offset,
              uint32 numNFTs,
              address owner,
              uint256[] memory ids
          ) internal {
              for (uint16 i = 0; i < numNFTs; i++) {
                  uint256 id = ids[i + offset];
                  _owners[id] = uint256(owner);
                  emit Transfer(address(0), owner, id);
              }
              _numNFTPerAddress[owner] += numNFTs;
          }
          function _mintBatch(
              uint16 offset,
              uint256[] memory supplies,
              address owner,
              uint256[] memory ids
          ) internal {
              uint256 firstId = ids[offset];
              (uint256 bin, uint256 index) = firstId.getTokenBinIndex();
              uint256 balances = _packedTokenBalance[owner][bin];
              for (uint256 i = 0; i < 8 && offset + i < supplies.length; i++) {
                  uint256 j = offset + i;
                  if (supplies[j] > 1) {
                      balances = balances.updateTokenBalance(
                          index + i,
                          supplies[j],
                          ObjectLib32.Operations.REPLACE
                      );
                  } else {
                      break;
                  }
              }
              _packedTokenBalance[owner][bin] = balances;
          }
          function _transferFrom(
              address from,
              address to,
              uint256 id,
              uint256 value
          ) internal returns (bool metaTx) {
              require(to != address(0), "destination is zero address");
              require(from != address(0), "from is zero address");
              metaTx = _metaTransactionContracts[msg.sender];
              bool authorized = from == msg.sender ||
                  metaTx ||
                  _superOperators[msg.sender] ||
                  _operatorsForAll[from][msg.sender];
              if (id & IS_NFT > 0) {
                  require(
                      authorized || _erc721operators[id] == msg.sender,
                      "Operator not approved"
                  );
                  if(value > 0) {
                      require(value == 1, "cannot transfer nft if amount not 1");
                      _numNFTPerAddress[from]--;
                      _numNFTPerAddress[to]++;
                      _owners[id] = uint256(to);
                      if (_erc721operators[id] != address(0)) { // TODO operatorEnabled flag optimization (like in ERC721BaseToken)
                          _erc721operators[id] = address(0);
                      }
                      emit Transfer(from, to, id);
                  }
              } else {
                  require(authorized, "Operator not approved");
                  if(value > 0) {
                      // if different owners it will fails
                      (uint256 bin, uint256 index) = id.getTokenBinIndex();
                      _packedTokenBalance[from][bin] = _packedTokenBalance[from][bin]
                          .updateTokenBalance(index, value, ObjectLib32.Operations.SUB);
                      _packedTokenBalance[to][bin] = _packedTokenBalance[to][bin]
                          .updateTokenBalance(index, value, ObjectLib32.Operations.ADD);
                  }
              }
              emit TransferSingle(
                  metaTx ? from : msg.sender,
                  from,
                  to,
                  id,
                  value
              );
          }
          /// @notice Transfers `value` tokens of type `id` from  `from` to `to`  (with safety call).
          /// @param from address from which tokens are transfered.
          /// @param to address to which the token will be transfered.
          /// @param id the token type transfered.
          /// @param value amount of token transfered.
          /// @param data aditional data accompanying the transfer.
          function safeTransferFrom(
              address from,
              address to,
              uint256 id,
              uint256 value,
              bytes calldata data
          ) external {
              if (id & IS_NFT > 0) {
                  require(_ownerOf(id) == from, "not owner");
              }
              bool metaTx = _transferFrom(from, to, id, value);
              require(
                  _checkERC1155AndCallSafeTransfer(
                      metaTx ? from : msg.sender,
                      from,
                      to,
                      id,
                      value,
                      data,
                      false,
                      false
                  ),
                  "erc1155 transfer rejected"
              );
          }
          /// @notice Transfers `values` tokens of type `ids` from  `from` to `to` (with safety call).
          /// @dev call data should be optimized to order ids so packedBalance can be used efficiently.
          /// @param from address from which tokens are transfered.
          /// @param to address to which the token will be transfered.
          /// @param ids ids of each token type transfered.
          /// @param values amount of each token type transfered.
          /// @param data aditional data accompanying the transfer.
          function safeBatchTransferFrom(
              address from,
              address to,
              uint256[] calldata ids,
              uint256[] calldata values,
              bytes calldata data
          ) external {
              require(
                  ids.length == values.length,
                  "Inconsistent array length between args"
              );
              require(to != address(0), "destination is zero address");
              require(from != address(0), "from is zero address");
              bool metaTx = _metaTransactionContracts[msg.sender];
              bool authorized = from == msg.sender ||
                  metaTx ||
                  _superOperators[msg.sender] ||
                  _operatorsForAll[from][msg.sender]; // solium-disable-line max-len
              _batchTransferFrom(from, to, ids, values, authorized);
              emit TransferBatch(
                  metaTx ? from : msg.sender,
                  from,
                  to,
                  ids,
                  values
              );
              require(
                  _checkERC1155AndCallSafeBatchTransfer(
                      metaTx ? from : msg.sender,
                      from,
                      to,
                      ids,
                      values,
                      data
                  ),
                  "erc1155 transfer rejected"
              );
          }
          function _batchTransferFrom(
              address from,
              address to,
              uint256[] memory ids,
              uint256[] memory values,
              bool authorized
          ) internal {
              uint256 numItems = ids.length;
              uint256 bin;
              uint256 index;
              uint256 balFrom;
              uint256 balTo;
              uint256 lastBin;
              uint256 numNFTs = 0;
              for (uint256 i = 0; i < numItems; i++) {
                  if (ids[i] & IS_NFT > 0) {
                      require(
                          authorized || _erc721operators[ids[i]] == msg.sender,
                          "Operator not approved"
                      );
                      if(values[i] > 0) {
                          require(values[i] == 1, "cannot transfer nft if amount not 1");
                          require(_ownerOf(ids[i]) == from, "not owner");
                          numNFTs++;
                          _owners[ids[i]] = uint256(to);
                          if (_erc721operators[ids[i]] != address(0)) { // TODO operatorEnabled flag optimization (like in ERC721BaseToken)
                              _erc721operators[ids[i]] = address(0);
                          }
                          emit Transfer(from, to, ids[i]);
                      }
                  } else {
                      require(authorized, "Operator not approved");
                      if (from == to) {
                          _checkEnoughBalance(from, ids[i], values[i]);
                      } else if(values[i] > 0) {
                          (bin, index) = ids[i].getTokenBinIndex();
                          if (lastBin == 0) {
                              lastBin = bin;
                              balFrom = ObjectLib32.updateTokenBalance(
                                  _packedTokenBalance[from][bin],
                                  index,
                                  values[i],
                                  ObjectLib32.Operations.SUB
                              );
                              balTo = ObjectLib32.updateTokenBalance(
                                  _packedTokenBalance[to][bin],
                                  index,
                                  values[i],
                                  ObjectLib32.Operations.ADD
                              );
                          } else {
                              if (bin != lastBin) {
                                  _packedTokenBalance[from][lastBin] = balFrom;
                                  _packedTokenBalance[to][lastBin] = balTo;
                                  balFrom = _packedTokenBalance[from][bin];
                                  balTo = _packedTokenBalance[to][bin];
                                  lastBin = bin;
                              }
                              balFrom = balFrom.updateTokenBalance(
                                  index,
                                  values[i],
                                  ObjectLib32.Operations.SUB
                              );
                              balTo = balTo.updateTokenBalance(
                                  index,
                                  values[i],
                                  ObjectLib32.Operations.ADD
                              );
                          }
                      }
                  }
              }
              if (numNFTs > 0 && from != to) {
                  _numNFTPerAddress[from] -= numNFTs;
                  _numNFTPerAddress[to] += numNFTs;
              }
              if (bin != 0 && from != to) {
                  _packedTokenBalance[from][bin] = balFrom;
                  _packedTokenBalance[to][bin] = balTo;
              }
          }
          function _checkEnoughBalance(address from, uint256 id, uint256 value) internal {
              (uint256 bin, uint256 index) = id.getTokenBinIndex();
              require(_packedTokenBalance[from][bin].getValueInBin(index) >= value, "can't substract more than there is");
          }
          /// @notice Get the balance of `owner` for the token type `id`.
          /// @param owner The address of the token holder.
          /// @param id the token type of which to get the balance of.
          /// @return the balance of `owner` for the token type `id`.
          function balanceOf(address owner, uint256 id)
              public
              view
              returns (uint256)
          {
              // do not check for existence, balance is zero if never minted
              // require(wasEverMinted(id), "token was never minted");
              if (id & IS_NFT > 0) {
                  if (_ownerOf(id) == owner) {
                      return 1;
                  } else {
                      return 0;
                  }
              }
              (uint256 bin, uint256 index) = id.getTokenBinIndex();
              return _packedTokenBalance[owner][bin].getValueInBin(index);
          }
          /// @notice Get the balance of `owners` for each token type `ids`.
          /// @param owners the addresses of the token holders queried.
          /// @param ids ids of each token type to query.
          /// @return the balance of each `owners` for each token type `ids`.
          function balanceOfBatch(
              address[] calldata owners,
              uint256[] calldata ids
          ) external view returns (uint256[] memory) {
              require(
                  owners.length == ids.length,
                  "Inconsistent array length between args"
              );
              uint256[] memory balances = new uint256[](ids.length);
              for (uint256 i = 0; i < ids.length; i++) {
                  balances[i] = balanceOf(owners[i], ids[i]);
              }
              return balances;
          }
          /// @notice Get the creator of the token type `id`.
          /// @param id the id of the token to get the creator of.
          /// @return the creator of the token type `id`.
          function creatorOf(uint256 id) external view returns (address) {
              require(wasEverMinted(id), "token was never minted");
              address originalCreator = address(id / CREATOR_OFFSET_MULTIPLIER);
              address newCreator = _creatorship[originalCreator];
              if (newCreator != address(0)) {
                  return newCreator;
              }
              return originalCreator;
          }
          /// @notice Transfers creatorship of `original` from `sender` to `to`.
          /// @param sender address of current registered creator.
          /// @param original address of the original creator whose creation are saved in the ids themselves.
          /// @param to address which will be given creatorship for all tokens originally minted by `original`.
          function transferCreatorship(
              address sender,
              address original,
              address to
          ) external {
              require(
                  msg.sender == sender ||
                  _metaTransactionContracts[msg.sender] ||
                  _superOperators[msg.sender],
                  "require meta approval"
              );
              require(sender != address(0), "sender is zero address");
              require(to != address(0), "destination is zero address");
              address current = _creatorship[original];
              if (current == address(0)) {
                  current = original;
              }
              require(current != to, "current == to");
              require(current == sender, "current != sender");
              if (to == original) {
                  _creatorship[original] = address(0);
              } else {
                  _creatorship[original] = to;
              }
              emit CreatorshipTransfer(original, current, to);
          }
          /// @notice Enable or disable approval for `operator` to manage all `sender`'s tokens.
          /// @dev used for Meta Transaction (from metaTransactionContract).
          /// @param sender address which grant approval.
          /// @param operator address which will be granted rights to transfer all token owned by `sender`.
          /// @param approved whether to approve or revoke.
          function setApprovalForAllFor(
              address sender,
              address operator,
              bool approved
          ) external {
              require(
                  msg.sender == sender ||
                  _metaTransactionContracts[msg.sender] ||
                  _superOperators[msg.sender],
                  "require meta approval"
              );
              _setApprovalForAll(sender, operator, approved);
          }
          /// @notice Enable or disable approval for `operator` to manage all of the caller's tokens.
          /// @param operator address which will be granted rights to transfer all tokens of the caller.
          /// @param approved whether to approve or revoke
          function setApprovalForAll(address operator, bool approved) external {
              _setApprovalForAll(msg.sender, operator, approved);
          }
          function _setApprovalForAll(
              address sender,
              address operator,
              bool approved
          ) internal {
              require(sender != address(0), "sender is zero address");
              require(sender != operator, "sender = operator");
              require(operator != address(0), "operator is zero address");
              require(
                  !_superOperators[operator],
                  "super operator can't have their approvalForAll changed"
              );
              _operatorsForAll[sender][operator] = approved;
              emit ApprovalForAll(sender, operator, approved);
          }
          /// @notice Queries the approval status of `operator` for owner `owner`.
          /// @param owner the owner of the tokens.
          /// @param operator address of authorized operator.
          /// @return true if the operator is approved, false if not.
          function isApprovedForAll(address owner, address operator)
              external
              view
              returns (bool isOperator)
          {
              require(owner != address(0), "owner is zero address");
              require(operator != address(0), "operator is zero address");
              return _operatorsForAll[owner][operator] || _superOperators[operator];
          }
          /// @notice Count all NFTs assigned to `owner`.
          /// @param owner address for whom to query the balance.
          /// @return the number of NFTs owned by `owner`, possibly zero.
          function balanceOf(address owner)
              external
              view
              returns (uint256 balance)
          {
              require(owner != address(0), "owner is zero address");
              return _numNFTPerAddress[owner];
          }
          /// @notice Find the owner of an NFT.
          /// @param id the identifier for an NFT.
          /// @return the address of the owner of the NFT.
          function ownerOf(uint256 id) external view returns (address owner) {
              owner = _ownerOf(id);
              require(owner != address(0), "NFT does not exist");
          }
          function _ownerOf(uint256 id) internal view returns (address) {
              return address(_owners[id]);
          }
          /// @notice Change or reaffirm the approved address for an NFT for `sender`.
          /// @dev used for Meta Transaction (from metaTransactionContract).
          /// @param sender the sender granting control.
          /// @param operator the address to approve as NFT controller.
          /// @param id the NFT to approve.
          function approveFor(address sender, address operator, uint256 id)
              external
          {
              address owner = _ownerOf(id);
              require(sender != address(0), "sender is zero address");
              require(
                  msg.sender == sender ||
                  _metaTransactionContracts[msg.sender] ||
                  _superOperators[msg.sender] ||
                  _operatorsForAll[sender][msg.sender],
                  "require operators"
              ); // solium-disable-line max-len
              require(owner == sender, "not owner");
              _erc721operators[id] = operator;
              emit Approval(owner, operator, id);
          }
          /// @notice Change or reaffirm the approved address for an NFT.
          /// @param operator the address to approve as NFT controller.
          /// @param id the id of the NFT to approve.
          function approve(address operator, uint256 id) external {
              address owner = _ownerOf(id);
              require(owner != address(0), "NFT does not exist");
              require(
                  owner == msg.sender ||
                  _superOperators[msg.sender] ||
                  _operatorsForAll[owner][msg.sender],
                  "not authorized"
              );
              _erc721operators[id] = operator;
              emit Approval(owner, operator, id);
          }
          /// @notice Get the approved address for a single NFT.
          /// @param id the NFT to find the approved address for.
          /// @return the approved address for this NFT, or the zero address if there is none.
          function getApproved(uint256 id)
              external
              view
              returns (address operator)
          {
              require(_ownerOf(id) != address(0), "NFT does not exist");
              return _erc721operators[id];
          }
          /// @notice Transfers ownership of an NFT.
          /// @param from the current owner of the NFT.
          /// @param to the new owner.
          /// @param id the NFT to transfer.
          function transferFrom(address from, address to, uint256 id) external {
              require(_ownerOf(id) == from, "not owner");
              bool metaTx = _transferFrom(from, to, id, 1);
              require(
                  _checkERC1155AndCallSafeTransfer(
                      metaTx ? from : msg.sender,
                      from,
                      to,
                      id,
                      1,
                      "",
                      true,
                      false
                  ),
                  "erc1155 transfer rejected"
              );
          }
          /// @notice Transfers the ownership of an NFT from one address to another address.
          /// @param from the current owner of the NFT.
          /// @param to the new owner.
          /// @param id the NFT to transfer.
          function safeTransferFrom(address from, address to, uint256 id)
              external
          {
              safeTransferFrom(from, to, id, "");
          }
          /// @notice Transfers the ownership of an NFT from one address to another address.
          /// @param from the current owner of the NFT.
          /// @param to the new owner.
          /// @param id the NFT to transfer.
          /// @param data additional data with no specified format, sent in call to `to`.
          function safeTransferFrom(
              address from,
              address to,
              uint256 id,
              bytes memory data
          ) public {
              require(_ownerOf(id) == from, "not owner");
              bool metaTx = _transferFrom(from, to, id, 1);
              require(
                  _checkERC1155AndCallSafeTransfer(
                      metaTx ? from : msg.sender,
                      from,
                      to,
                      id,
                      1,
                      data,
                      true,
                      true
                  ),
                  "erc721/erc1155 transfer rejected"
              );
          }
          /// @notice A descriptive name for the collection of tokens in this contract.
          /// @return the name of the tokens.
          function name() external pure returns (string memory _name) {
              return "Sandbox's ASSETs";
          }
          /// @notice An abbreviated name for the collection of tokens in this contract.
          /// @return the symbol of the tokens.
          function symbol() external pure returns (string memory _symbol) {
              return "ASSET";
          }
          /// @notice Gives the rarity power of a particular token type.
          /// @param id the token type to get the rarity of.
          /// @return the rarity power(between 0 and 3).
          function rarity(uint256 id) public view returns (uint256) {
              require(wasEverMinted(id), "token was never minted");
              bytes storage rarityPack = _rarityPacks[id & URI_ID];
              uint256 packIndex = id & PACK_INDEX;
              if (packIndex / 4 >= rarityPack.length) {
                  return 0;
              } else {
                  uint8 pack = uint8(rarityPack[packIndex / 4]);
                  uint8 i = (3 - uint8(packIndex % 4)) * 2;
                  return (pack / (uint8(2)**i)) % 4;
              }
          }
          /// @notice Gives the collection a specific token belongs to.
          /// @param id the token to get the collection of.
          /// @return the collection the NFT is part of.
          function collectionOf(uint256 id) public view returns (uint256) {
              require(_ownerOf(id) != address(0), "NFT does not exist");
              uint256 collectionId = id & NOT_NFT_INDEX & NOT_IS_NFT;
              require(wasEverMinted(collectionId), "no collection ever minted for that token");
              return collectionId;
          }
          /// @notice Return wether the id is a collection
          /// @param id collectionId to check.
          /// @return whether the id is a collection.
          function isCollection(uint256 id) public view returns (bool) {
              uint256 collectionId = id & NOT_NFT_INDEX & NOT_IS_NFT;
              return wasEverMinted(collectionId);
          }
          /// @notice Gives the index at which an NFT was minted in a collection : first of a collection get the zero index.
          /// @param id the token to get the index of.
          /// @return the index/order at which the token `id` was minted in a collection.
          function collectionIndexOf(uint256 id) public view returns (uint256) {
              collectionOf(id); // this check if id and collection indeed was ever minted
              return uint32((id & NFT_INDEX) >> NFT_INDEX_OFFSET);
          }
          function toFullURI(bytes32 hash, uint256 id)
              internal
              pure
              returns (string memory)
          {
              return
                  string(
                      abi.encodePacked(
                          "ipfs://bafybei",
                          hash2base32(hash),
                          "/",
                          uint2str(id & PACK_INDEX),
                          ".json"
                      )
                  );
          }
          function wasEverMinted(uint256 id) public view returns(bool) {
              if ((id & IS_NFT) > 0) {
                  return _owners[id] != 0;
              } else {
                  return
                      ((id & PACK_INDEX) < ((id & PACK_NUM_FT_TYPES) / PACK_NUM_FT_TYPES_OFFSET_MULTIPLIER)) &&
                      _metadataHash[id & URI_ID] != 0;
              }
          }
          /// @notice check whether a packId/numFT tupple has been used
          /// @param creator for which creator
          /// @param packId the packId to check
          /// @param numFTs number of Fungible Token in that pack (can reuse packId if different)
          /// @return whether the pack has already been used
          function isPackIdUsed(address creator, uint40 packId, uint16 numFTs) external returns(bool) {
              uint256 uriId = uint256(creator) * CREATOR_OFFSET_MULTIPLIER + // CREATOR
                  uint256(packId) * PACK_ID_OFFSET_MULTIPLIER + // packId (unique pack) // PACk_ID
                  numFTs * PACK_NUM_FT_TYPES_OFFSET_MULTIPLIER; // number of fungible token in the pack // PACK_NUM_FT_TYPES
              return _metadataHash[uriId] != 0;
          }
          /// @notice A distinct Uniform Resource Identifier (URI) for a given token.
          /// @param id token to get the uri of.
          /// @return URI string
          function uri(uint256 id) public view returns (string memory) {
              require(wasEverMinted(id), "token was never minted"); // prevent returning invalid uri
              return toFullURI(_metadataHash[id & URI_ID], id);
          }
          /// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
          /// @param id token to get the uri of.
          /// @return URI string
          function tokenURI(uint256 id) public view returns (string memory) {
              require(_ownerOf(id) != address(0), "NFT does not exist");
              return toFullURI(_metadataHash[id & URI_ID], id);
          }
          bytes32 private constant base32Alphabet = 0x6162636465666768696A6B6C6D6E6F707172737475767778797A323334353637;
          // solium-disable-next-line security/no-assign-params
          function hash2base32(bytes32 hash)
              private
              pure
              returns (string memory _uintAsString)
          {
              uint256 _i = uint256(hash);
              uint256 k = 52;
              bytes memory bstr = new bytes(k);
              bstr[--k] = base32Alphabet[uint8((_i % 8) << 2)]; // uint8 s = uint8((256 - skip) % 5);  // (_i % (2**s)) << (5-s)
              _i /= 8;
              while (k > 0) {
                  bstr[--k] = base32Alphabet[_i % 32];
                  _i /= 32;
              }
              return string(bstr);
          }
          // solium-disable-next-line security/no-assign-params
          function uint2str(uint256 _i)
              private
              pure
              returns (string memory _uintAsString)
          {
              if (_i == 0) {
                  return "0";
              }
              uint256 j = _i;
              uint256 len;
              while (j != 0) {
                  len++;
                  j /= 10;
              }
              bytes memory bstr = new bytes(len);
              uint256 k = len - 1;
              while (_i != 0) {
                  bstr[k--] = bytes1(uint8(48 + (_i % 10)));
                  _i /= 10;
              }
              return string(bstr);
          }
          /// @notice Query if a contract implements interface `id`.
          /// @param id the interface identifier, as specified in ERC-165.
          /// @return `true` if the contract implements `id`.
          function supportsInterface(bytes4 id) external view returns (bool) {
              return
                  id == 0x01ffc9a7 || //ERC165
                  id == 0xd9b67a26 || // ERC1155
                  id == 0x80ac58cd || // ERC721
                  id == 0x5b5e139f || // ERC721 metadata
                  id == 0x0e89341c; // ERC1155 metadata
          }
          bytes4 constant ERC165ID = 0x01ffc9a7;
          function checkIsERC1155Receiver(address _contract)
              internal
              view
              returns (bool)
          {
              bool success;
              bool result;
              bytes memory call_data = abi.encodeWithSelector(
                  ERC165ID,
                  ERC1155_IS_RECEIVER
              );
              // solium-disable-next-line security/no-inline-assembly
              assembly {
                  let call_ptr := add(0x20, call_data)
                  let call_size := mload(call_data)
                  let output := mload(0x40) // Find empty storage location using "free memory pointer"
                  mstore(output, 0x0)
                  success := staticcall(
                      10000,
                      _contract,
                      call_ptr,
                      call_size,
                      output,
                      0x20
                  ) // 32 bytes
                  result := mload(output)
              }
              // (10000 / 63) "not enough for supportsInterface(...)" // consume all gas, so caller can potentially know that there was not enough gas
              assert(gasleft() > 158);
              return success && result;
          }
          function _checkERC1155AndCallSafeTransfer(
              address operator,
              address from,
              address to,
              uint256 id,
              uint256 value,
              bytes memory data,
              bool erc721,
              bool erc721Safe
          ) internal returns (bool) {
              if (!to.isContract()) {
                  return true;
              }
              if (erc721) {
                  if (!checkIsERC1155Receiver(to)) {
                      if (erc721Safe) {
                          return
                              _checkERC721AndCallSafeTransfer(
                                  operator,
                                  from,
                                  to,
                                  id,
                                  data
                              );
                      } else {
                          return true;
                      }
                  }
              }
              return
                  ERC1155TokenReceiver(to).onERC1155Received(
                          operator,
                          from,
                          id,
                          value,
                          data
                  ) == ERC1155_RECEIVED;
          }
          function _checkERC1155AndCallSafeBatchTransfer(
              address operator,
              address from,
              address to,
              uint256[] memory ids,
              uint256[] memory values,
              bytes memory data
          ) internal returns (bool) {
              if (!to.isContract()) {
                  return true;
              }
              bytes4 retval = ERC1155TokenReceiver(to).onERC1155BatchReceived(
                  operator,
                  from,
                  ids,
                  values,
                  data
              );
              return (retval == ERC1155_BATCH_RECEIVED);
          }
          function _checkERC721AndCallSafeTransfer(
              address operator,
              address from,
              address to,
              uint256 id,
              bytes memory data
          ) internal returns (bool) {
              // following not required as this function is always called as part of ERC1155 checks that include such check already
              // if (!to.isContract()) {
              //     return true;
              // }
              return (ERC721TokenReceiver(to).onERC721Received(
                      operator,
                      from,
                      id,
                      data
                  ) ==
                  ERC721_RECEIVED);
          }
          event Extraction(uint256 indexed fromId, uint256 toId);
          event AssetUpdate(uint256 indexed fromId, uint256 toId);
          function _burnERC1155(
              address operator,
              address from,
              uint256 id,
              uint32 amount
          ) internal {
              (uint256 bin, uint256 index) = (id).getTokenBinIndex();
              _packedTokenBalance[from][bin] = _packedTokenBalance[from][bin]
                  .updateTokenBalance(index, amount, ObjectLib32.Operations.SUB);
              emit TransferSingle(operator, from, address(0), id, amount);
          }
          function _burnERC721(address operator, address from, uint256 id)
              internal
          {
              require(from == _ownerOf(id), "not owner");
              _owners[id] = 2**160; // equivalent to zero address when casted but ensure we track minted status
              _numNFTPerAddress[from]--;
              emit Transfer(from, address(0), id);
              emit TransferSingle(operator, from, address(0), id, 1);
          }
          /// @notice Burns `amount` tokens of type `id`.
          /// @param id token type which will be burnt.
          /// @param amount amount of token to burn.
          function burn(uint256 id, uint256 amount) external {
              _burn(msg.sender, id, amount);
          }
          /// @notice Burns `amount` tokens of type `id` from `from`.
          /// @param from address whose token is to be burnt.
          /// @param id token type which will be burnt.
          /// @param amount amount of token to burn.
          function burnFrom(address from, uint256 id, uint256 amount) external {
              require(from != address(0), "from is zero address");
              require(
                  msg.sender == from ||
                      _metaTransactionContracts[msg.sender] ||
                      _superOperators[msg.sender] ||
                      _operatorsForAll[from][msg.sender],
                  "require meta approval"
              );
              _burn(from, id, amount);
          }
          function _burn(address from, uint256 id, uint256 amount) internal {
              if ((id & IS_NFT) > 0) {
                  require(amount == 1, "can only burn one NFT");
                  _burnERC721(
                      _metaTransactionContracts[msg.sender] ? from : msg.sender,
                      from,
                      id
                  );
              } else {
                  require(amount > 0 && amount <= MAX_SUPPLY, "invalid amount");
                  _burnERC1155(
                      _metaTransactionContracts[msg.sender] ? from : msg.sender,
                      from,
                      id,
                      uint32(amount)
                  );
              }
          }
          /// @notice Upgrades an NFT with new metadata and rarity.
          /// @param from address which own the NFT to be upgraded.
          /// @param id the NFT that will be burnt to be upgraded.
          /// @param packId unqiue packId for the token.
          /// @param hash hash of an IPFS cidv1 folder that contains the metadata of the new token type in the file 0.json.
          /// @param newRarity rarity power of the new NFT.
          /// @param to address which will receive the NFT.
          /// @param data bytes to be transmitted as part of the minted token.
          /// @return the id of the newly minted NFT.
          function updateERC721(
              address from,
              uint256 id,
              uint40 packId,
              bytes32 hash,
              uint8 newRarity,
              address to,
              bytes calldata data
          ) external returns(uint256) {
              require(hash != 0, "hash is zero");
              require(
                  _bouncers[msg.sender],
                  "only bouncer allowed to mint via update"
              );
              require(to != address(0), "destination is zero address");
              require(from != address(0), "from is zero address");
              _burnERC721(msg.sender, from, id);
              uint256 newId = generateTokenId(from, 1, packId, 0, 0);
              _mint(hash, 1, newRarity, msg.sender, to, newId, data, false);
              emit AssetUpdate(id, newId);
              return newId;
          }
          /// @notice Extracts an EIP-721 NFT from an EIP-1155 token.
          /// @param id the token type to extract from.
          /// @param to address which will receive the token.
          /// @return the id of the newly minted NFT.
          function extractERC721(uint256 id, address to)
              external
              returns (uint256 newId)
          {
              return _extractERC721From(msg.sender, msg.sender, id, to);
          }
          /// @notice Extracts an EIP-721 NFT from an EIP-1155 token.
          /// @param sender address which own the token to be extracted.
          /// @param id the token type to extract from.
          /// @param to address which will receive the token.
          /// @return the id of the newly minted NFT.
          function extractERC721From(address sender, uint256 id, address to)
              external
              returns (uint256 newId)
          {
              bool metaTx = _metaTransactionContracts[msg.sender];
              require(
                  msg.sender == sender ||
                      metaTx ||
                      _superOperators[msg.sender] ||
                      _operatorsForAll[sender][msg.sender],
                  "require meta approval"
              );
              return _extractERC721From(metaTx ? sender : msg.sender, sender, id, to);
          }
          function _extractERC721From(address operator, address sender, uint256 id, address to)
              internal
              returns (uint256 newId)
          {
              require(to != address(0), "destination is zero address");
              require(id & IS_NFT == 0, "Not an ERC1155 Token");
              uint32 tokenCollectionIndex = _nextCollectionIndex[id];
              newId = id +
                  IS_NFT +
                  (tokenCollectionIndex) *
                  2**NFT_INDEX_OFFSET;
              _nextCollectionIndex[id] = tokenCollectionIndex + 1;
              _burnERC1155(operator, sender, id, 1);
              _mint(
                  _metadataHash[id & URI_ID],
                  1,
                  0,
                  operator,
                  to,
                  newId,
                  "",
                  true
              );
              emit Extraction(id, newId);
          }
      }
      pragma solidity ^0.5.2;
      contract Admin {
          address internal _admin;
          event AdminChanged(address oldAdmin, address newAdmin);
          /// @notice gives the current administrator of this contract.
          /// @return the current administrator of this contract.
          function getAdmin() external view returns (address) {
              return _admin;
          }
          /// @notice change the administrator to be `newAdmin`.
          /// @param newAdmin address of the new administrator.
          function changeAdmin(address newAdmin) external {
              require(msg.sender == _admin, "only admin can change admin");
              emit AdminChanged(_admin, newAdmin);
              _admin = newAdmin;
          }
          modifier onlyAdmin() {
              require (msg.sender == _admin, "only admin allowed");
              _;
          }
      }
      pragma solidity ^0.5.2;
      import "./Admin.sol";
      contract SuperOperators is Admin {
          mapping(address => bool) internal _superOperators;
          event SuperOperator(address superOperator, bool enabled);
          /// @notice Enable or disable the ability of `superOperator` to transfer tokens of all (superOperator rights).
          /// @param superOperator address that will be given/removed superOperator right.
          /// @param enabled set whether the superOperator is enabled or disabled.
          function setSuperOperator(address superOperator, bool enabled) external {
              require(
                  msg.sender == _admin,
                  "only admin is allowed to add super operators"
              );
              _superOperators[superOperator] = enabled;
              emit SuperOperator(superOperator, enabled);
          }
          /// @notice check whether address `who` is given superOperator rights.
          /// @param who The address to query.
          /// @return whether the address has superOperator rights.
          function isSuperOperator(address who) public view returns (bool) {
              return _superOperators[who];
          }
      }
      pragma solidity ^0.5.2;
      /**
          @title ERC-1155 Multi Token Standard
          @dev See https://eips.ethereum.org/EIPS/eip-1155
          Note: The ERC-165 identifier for this interface is 0xd9b67a26.
       */
      interface ERC1155 {
          event TransferSingle(
              address indexed operator,
              address indexed from,
              address indexed to,
              uint256 id,
              uint256 value
          );
          event TransferBatch(
              address indexed operator,
              address indexed from,
              address indexed to,
              uint256[] ids,
              uint256[] values
          );
          event ApprovalForAll(
              address indexed owner,
              address indexed operator,
              bool approved
          );
          event URI(string value, uint256 indexed id);
          /**
              @notice Transfers `value` amount of an `id` from  `from` to `to`  (with safety call).
              @dev Caller must be approved to manage the tokens being transferred out of the `from` account (see "Approval" section of the standard).
              MUST revert if `to` is the zero address.
              MUST revert if balance of holder for token `id` is lower than the `value` sent.
              MUST revert on any other error.
              MUST emit the `TransferSingle` event to reflect the balance change (see "Safe Transfer Rules" section of the standard).
              After the above conditions are met, this function MUST check if `to` is a smart contract (e.g. code size > 0). If so, it MUST call `onERC1155Received` on `to` and act appropriately (see "Safe Transfer Rules" section of the standard).
              @param from    Source address
              @param to      Target address
              @param id      ID of the token type
              @param value   Transfer amount
              @param data    Additional data with no specified format, MUST be sent unaltered in call to `onERC1155Received` on `to`
          */
          function safeTransferFrom(
              address from,
              address to,
              uint256 id,
              uint256 value,
              bytes calldata data
          ) external;
          /**
              @notice Transfers `values` amount(s) of `ids` from the `from` address to the `to` address specified (with safety call).
              @dev Caller must be approved to manage the tokens being transferred out of the `from` account (see "Approval" section of the standard).
              MUST revert if `to` is the zero address.
              MUST revert if length of `ids` is not the same as length of `values`.
              MUST revert if any of the balance(s) of the holder(s) for token(s) in `ids` is lower than the respective amount(s) in `values` sent to the recipient.
              MUST revert on any other error.
              MUST emit `TransferSingle` or `TransferBatch` event(s) such that all the balance changes are reflected (see "Safe Transfer Rules" section of the standard).
              Balance changes and events MUST follow the ordering of the arrays (_ids[0]/_values[0] before _ids[1]/_values[1], etc).
              After the above conditions for the transfer(s) in the batch are met, this function MUST check if `to` is a smart contract (e.g. code size > 0). If so, it MUST call the relevant `ERC1155TokenReceiver` hook(s) on `to` and act appropriately (see "Safe Transfer Rules" section of the standard).
              @param from    Source address
              @param to      Target address
              @param ids     IDs of each token type (order and length must match _values array)
              @param values  Transfer amounts per token type (order and length must match _ids array)
              @param data    Additional data with no specified format, MUST be sent unaltered in call to the `ERC1155TokenReceiver` hook(s) on `to`
          */
          function safeBatchTransferFrom(
              address from,
              address to,
              uint256[] calldata ids,
              uint256[] calldata values,
              bytes calldata data
          ) external;
          /**
              @notice Get the balance of an account's tokens.
              @param owner  The address of the token holder
              @param id     ID of the token
              @return        The _owner's balance of the token type requested
           */
          function balanceOf(address owner, uint256 id)
              external
              view
              returns (uint256);
          /**
              @notice Get the balance of multiple account/token pairs
              @param owners The addresses of the token holders
              @param ids    ID of the tokens
              @return        The _owner's balance of the token types requested (i.e. balance for each (owner, id) pair)
           */
          function balanceOfBatch(address[] calldata owners, uint256[] calldata ids)
              external
              view
              returns (uint256[] memory);
          /**
              @notice Enable or disable approval for a third party ("operator") to manage all of the caller's tokens.
              @dev MUST emit the ApprovalForAll event on success.
              @param operator  Address to add to the set of authorized operators
              @param approved  True if the operator is approved, false to revoke approval
          */
          function setApprovalForAll(address operator, bool approved) external;
          /**
              @notice Queries the approval status of an operator for a given owner.
              @param owner     The owner of the tokens
              @param operator  Address of authorized operator
              @return           True if the operator is approved, false if not
          */
          function isApprovedForAll(address owner, address operator)
              external
              view
              returns (bool);
      }
      pragma solidity ^0.5.2;
      /**
          Note: The ERC-165 identifier for this interface is 0x4e2312e0.
      */
      interface ERC1155TokenReceiver {
          /**
              @notice Handle the receipt of a single ERC1155 token type.
              @dev An ERC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeTransferFrom` after the balance has been updated.
              This function MUST return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` (i.e. 0xf23a6e61) if it accepts the transfer.
              This function MUST revert if it rejects the transfer.
              Return of any other value than the prescribed keccak256 generated value MUST result in the transaction being reverted by the caller.
              @param operator  The address which initiated the transfer (i.e. msg.sender)
              @param from      The address which previously owned the token
              @param id        The ID of the token being transferred
              @param value     The amount of tokens being transferred
              @param data      Additional data with no specified format
              @return           `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
          */
          function onERC1155Received(
              address operator,
              address from,
              uint256 id,
              uint256 value,
              bytes calldata data
          ) external returns (bytes4);
          /**
              @notice Handle the receipt of multiple ERC1155 token types.
              @dev An ERC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeBatchTransferFrom` after the balances have been updated.
              This function MUST return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` (i.e. 0xbc197c81) if it accepts the transfer(s).
              This function MUST revert if it rejects the transfer(s).
              Return of any other value than the prescribed keccak256 generated value MUST result in the transaction being reverted by the caller.
              @param operator  The address which initiated the batch transfer (i.e. msg.sender)
              @param from      The address which previously owned the token
              @param ids       An array containing ids of each token being transferred (order and length must match _values array)
              @param values    An array containing amounts of each token being transferred (order and length must match _ids array)
              @param data      Additional data with no specified format
              @return           `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
          */
          function onERC1155BatchReceived(
              address operator,
              address from,
              uint256[] calldata ids,
              uint256[] calldata values,
              bytes calldata data
          ) external returns (bytes4);
      }
      pragma solidity ^0.5.2;
      /**
       * @title ERC165
       * @dev https://eips.ethereum.org/EIPS/eip-165
       */
      interface ERC165 {
          /**
         * @notice Query if a contract implements interface `interfaceId`
         * @param interfaceId The interface identifier, as specified in ERC-165
         * @dev Interface identification is specified in ERC-165. This function
         * uses less than 30,000 gas.
         */
          function supportsInterface(bytes4 interfaceId)
              external
              view
              returns (bool);
      }
      pragma solidity ^0.5.2;
      import "./ERC165.sol";
      import "./ERC721Events.sol";
      /**
       * @title ERC721 Non-Fungible Token Standard basic interface
       * @dev see https://eips.ethereum.org/EIPS/eip-721
       */
      /*interface*/
      contract ERC721 is ERC165, ERC721Events {
          function balanceOf(address owner) external view returns (uint256 balance);
          function ownerOf(uint256 tokenId) external view returns (address owner);
          //   function exists(uint256 tokenId) external view returns (bool exists);
          function approve(address to, uint256 tokenId) external;
          function getApproved(uint256 tokenId)
              external
              view
              returns (address operator);
          function setApprovalForAll(address operator, bool approved) external;
          function isApprovedForAll(address owner, address operator)
              external
              view
              returns (bool);
          function transferFrom(address from, address to, uint256 tokenId)
              external;
          function safeTransferFrom(address from, address to, uint256 tokenId)
              external;
          function safeTransferFrom(
              address from,
              address to,
              uint256 tokenId,
              bytes calldata data
          ) external;
      }
      pragma solidity ^0.5.2;
      /**
       * @title ERC721 Non-Fungible Token Standard basic interface
       * @dev see https://eips.ethereum.org/EIPS/eip-721
       */
      interface ERC721Events {
          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
          );
      }
      /* This Source Code Form is subject to the terms of the Mozilla Public
       * License, v. 2.0. If a copy of the MPL was not distributed with this
       * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       *
       * This code has not been reviewed.
       * Do not use or deploy this code before reviewing it personally first.
       */
      // solhint-disable-next-line compiler-fixed
      pragma solidity ^0.5.2;
      interface ERC721TokenReceiver {
          function onERC721Received(
              address operator,
              address from,
              uint256 tokenId,
              bytes calldata data
          ) external returns (bytes4);
      }
      pragma solidity ^0.5.2;
      library AddressUtils {
          function toPayable(address _address) internal pure returns (address payable _payable) {
              return address(uint160(_address));
          }
          function isContract(address addr) internal view returns (bool) {
              // for accounts without code, i.e. `keccak256('')`:
              bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
              bytes32 codehash;
              // solium-disable-next-line security/no-inline-assembly
              assembly {
                  codehash := extcodehash(addr)
              }
              return (codehash != 0x0 && codehash != accountHash);
          }
      }
      pragma solidity ^0.5.2;
      import "./SafeMathWithRequire.sol";
      library ObjectLib32 {
          using SafeMathWithRequire for uint256;
          enum Operations {ADD, SUB, REPLACE}
          // Constants regarding bin or chunk sizes for balance packing
          uint256 constant TYPES_BITS_SIZE = 32; // Max size of each object
          uint256 constant TYPES_PER_UINT256 = 256 / TYPES_BITS_SIZE; // Number of types per uint256
          //
          // Objects and Tokens Functions
          //
          /**
        * @dev Return the bin number and index within that bin where ID is
        * @param tokenId Object type
        * @return (Bin number, ID's index within that bin)
        */
          function getTokenBinIndex(uint256 tokenId)
              internal
              pure
              returns (uint256 bin, uint256 index)
          {
              bin = (tokenId * TYPES_BITS_SIZE) / 256;
              index = tokenId % TYPES_PER_UINT256;
              return (bin, index);
          }
          /**
        * @dev update the balance of a type provided in binBalances
        * @param binBalances Uint256 containing the balances of objects
        * @param index Index of the object in the provided bin
        * @param amount Value to update the type balance
        * @param operation Which operation to conduct :
        *     Operations.REPLACE : Replace type balance with amount
        *     Operations.ADD     : ADD amount to type balance
        *     Operations.SUB     : Substract amount from type balance
        */
          function updateTokenBalance(
              uint256 binBalances,
              uint256 index,
              uint256 amount,
              Operations operation
          ) internal pure returns (uint256 newBinBalance) {
              uint256 objectBalance = 0;
              if (operation == Operations.ADD) {
                  objectBalance = getValueInBin(binBalances, index);
                  newBinBalance = writeValueInBin(
                      binBalances,
                      index,
                      objectBalance.add(amount)
                  );
              } else if (operation == Operations.SUB) {
                  objectBalance = getValueInBin(binBalances, index);
                  require(objectBalance >= amount, "can't substract more than there is");
                  newBinBalance = writeValueInBin(
                      binBalances,
                      index,
                      objectBalance.sub(amount)
                  );
              } else if (operation == Operations.REPLACE) {
                  newBinBalance = writeValueInBin(binBalances, index, amount);
              } else {
                  revert("Invalid operation"); // Bad operation
              }
              return newBinBalance;
          }
          /*
        * @dev return value in binValue at position index
        * @param binValue uint256 containing the balances of TYPES_PER_UINT256 types
        * @param index index at which to retrieve value
        * @return Value at given index in bin
        */
          function getValueInBin(uint256 binValue, uint256 index)
              internal
              pure
              returns (uint256)
          {
              // Mask to retrieve data for a given binData
              uint256 mask = (uint256(1) << TYPES_BITS_SIZE) - 1;
              // Shift amount
              uint256 rightShift = 256 - TYPES_BITS_SIZE * (index + 1);
              return (binValue >> rightShift) & mask;
          }
          /**
        * @dev return the updated binValue after writing amount at index
        * @param binValue uint256 containing the balances of TYPES_PER_UINT256 types
        * @param index Index at which to retrieve value
        * @param amount Value to store at index in bin
        * @return Value at given index in bin
        */
          function writeValueInBin(uint256 binValue, uint256 index, uint256 amount)
              internal
              pure
              returns (uint256)
          {
              require(
                  amount < 2**TYPES_BITS_SIZE,
                  "Amount to write in bin is too large"
              );
              // Mask to retrieve data for a given binData
              uint256 mask = (uint256(1) << TYPES_BITS_SIZE) - 1;
              // Shift amount
              uint256 leftShift = 256 - TYPES_BITS_SIZE * (index + 1);
              return (binValue & ~(mask << leftShift)) | (amount << leftShift);
          }
      }
      pragma solidity ^0.5.2;
      /**
       * @title SafeMath
       * @dev Math operations with safety checks that revert
       */
      library SafeMathWithRequire {
          /**
          * @dev Multiplies two numbers, throws on overflow.
          */
          function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
              // Gas optimization: this is cheaper than asserting 'a' not being zero, but the
              // benefit is lost if 'b' is also tested.
              // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
              if (a == 0) {
                  return 0;
              }
              c = a * b;
              require(c / a == b, "overflow");
              return c;
          }
          /**
          * @dev Integer division of two numbers, truncating the quotient.
          */
          function div(uint256 a, uint256 b) internal pure 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 a / b;
          }
          /**
          * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
          */
          function sub(uint256 a, uint256 b) internal pure returns (uint256) {
              require(b <= a, "undeflow");
              return a - b;
          }
          /**
          * @dev Adds two numbers, throws on overflow.
          */
          function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
              c = a + b;
              require(c >= a, "overflow");
              return c;
          }
      }