ETH Price: $3,583.39 (-3.18%)

Transaction Decoder

Block:
23060646 at Aug-03-2025 12:50:11 PM +UTC
Transaction Fee:
0.000070317421770825 ETH $0.25
Gas Used:
253,325 Gas / 0.277577901 Gwei

Emitted Events:

391 BloodCrystal.Transfer( from=ERC20PredicateProxy, to=[Sender] 0x7b8c97c6a2fe7cb533d47b4544aeaa8fb5b8e8a2, value=10900000000000000000000 )
392 ERC20PredicateProxy.0xbb61bd1b26b3684c7c028ff1a8f6dabcac2fac8ac57b66fa6b1efb6edeab03c4( 0xbb61bd1b26b3684c7c028ff1a8f6dabcac2fac8ac57b66fa6b1efb6edeab03c4, 0x0000000000000000000000007b8c97c6a2fe7cb533d47b4544aeaa8fb5b8e8a2, 0x0000000000000000000000004b6d036d0bc62a633acca6d10956e9dbbb16748f, 00000000000000000000000000000000000000000000024ee3e319532dd00000 )

Account State Difference:

  Address   Before After State Difference Code
(Titan Builder)
16.325781444068019046 Eth16.325786223514821896 Eth0.00000477944680285
0x4b6d036d...bBB16748f
0x7B8C97C6...FB5B8E8a2
0.002763802031241761 Eth
Nonce: 4
0.002693484609470936 Eth
Nonce: 5
0.000070317421770825
0xA0c68C63...1bFc77C77
(Polygon (Matic): Bridge)

Execution Trace

RootChainManagerProxy.3805550f( )
  • RootChainManager.exit( inputData=0xF90A878433328970B90180139CF79C88625B5F376AD4A691F770C2375D562A628D9BFFD61513B960C2BD3865422683B0DEBB5ECE883709E7398BA4613BD6120DB03A8853B8FFA0B55830841BCE3CC084EA3BF81593AA6ADA107F83EAEF621A44E05D8BDA91865157EE142F86F2BE587280FF4FF58FC22AF8161992FC8BF5458752B31F5BB589BCC6D311F29AA1F84A1E4597A46159E1D3E68C1856BF8B63013239F7D3C9C358A81D75C856974EA989A1E47F0183C3AFC9874A13ACB4ACF46AB4D0F58700D224980EA8BF4DA7E2C098CED9EB2EFB7AAC1744CF3B17DB69466FA7E8257407C54C3B088A06C9596101455BA7EE1714C29124B8ED086860B4A65A94AB3AE4750393A637F3B6109064A8073728C1696673C30C67008993BE2DE13005E9CBDBF94AA1CE5DBE949B26FAB9495A746B64D675D7F2FD71545B091385695766C96069DE278988B3C9586B1EBD3983E76CDE35DD55B5492A021D52E0E28A2F766DEB54254BDA96CE9FDDFF8AC26E76E0257B991A483E8341D98440CB1D4F58DE7B8605D22FE7339760D0840474872E84688F3EB7A03F2148B4F897025E2F6FD3452E5A3918E535B3571E251C8DA9FFCD81DEB4470BA076164CAD69CD13B363890A137FFBAC2F2A493D03376EC748409B92DC9ED3CC2DB902EC02F902E801833DECAAB9010000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000001008400000000000000000000000000000000000000000000000008000000800000000000000000000100000000000000000000020000000000000000000800000000000000000080000010000000000000000800000000000000000000000000800000000000000080000000000000200000000000000000000000000800000000000000000000000000000000004000000002000000000001000000000000000000000000040000100000000020000000000000000000000800000000000000000000000000000000000000100000F901DDF89B94FE049F59963545BF5469F968E04C9646D6E2C2C5F863A0DDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EFA00000000000000000000000007B8C97C6A2FE7CB533D47B4544AEAA8FB5B8E8A2A00000000000000000000000000000000000000000000000000000000000000000A000000000000000000000000000000000000000000000024EE3E319532DD00000F9013D940000000000000000000000000000000000001010F884A04DFE1BBBCF077DDC3E01291EEA2D5C70C2B422B415D95645B9ADCFD678CB1D63A00000000000000000000000000000000000000000000000000000000000001010A00000000000000000000000007B8C97C6A2FE7CB533D47B4544AEAA8FB5B8E8A2A00000000000000000000000000E94B9B3FABD95338B8B23C36CAAE1D640E1339FB8A00000000000000000000000000000000000000000000000000003F09CEF021F0800000000000000000000000000000000000000000000000002E0EBB87F01AFB50000000000000000000000000000000000000000000006AB15E47439E3F2C12C00000000000000000000000000000000000000000000000002DCFB1B8FFF90AD0000000000000000000000000000000000000000000006AB15E864D6D2F4E034B905BDF905BAF8B1A083901D600919993E8D63503824DF176FCFF9E6EACE1D1C4A4960A9173547FCC3A091CAA86B17C173AA57845AE6CD142C8F85A38226BD3CFC710FC707A3CA4CEDC8A0D2ED425CA8974250D920D48A099F734A0D3CBF9BA031B030D67AE50D2CA241DAA0BFF58DF513981FFBDA85014AD46DD5A4273E19FA110003982028D2ED003373CF80808080A0AF33850EC95D229A1394251CD137BE764ED7FAF1D6EA282B736C5E74B1FFCBA98080808080808080F90211A07D0D922CE931CCDCFC835B72AD65D294B3AF9DB3A3DAAA1092FC756FAAED7B80A00B8CE76EE3A56FE56DF9392C942502DDC72D0F7F5A550DD7FEC4025624BE010AA0EFBE8817671D5E10FC057E35FD22DD58C9EA05D36DCBFD4FF1C4661603087893A0F43B0924872A17D01B4D8605C430ACF91C6EC71AB6123C2A1FEC179FBB7C7B0CA04BD25B5552AAD451128A9379FFB0F4DBB8CB7556A83F252738C89BDEF0811C70A0773C2EEFB9519EA6AE7666033CF5544A70C66AE3D2C550E32008E662B7B3F344A06EDC1B1E0F196A349543E27D8F9A5407ABD5CA6B3B00AD2BA572582651F8D249A0015CF24B57C61A0A4136CECFDA2622D867742E5534E571F8283FAE33172972C7A0DEAD6EDE368E6F1DE3A9AA6EE530B08A69A3942A417A5B502F737ADBB3D17D94A0168F571B5220E0E9A89A2F84FE18AE4C9837D79A2CFDBF6A465D9EAA4354F468A03C6F498CD9C2EE341A3F413F8A55829A70DEDFE5427706485B325BA6C80F8DF4A0459DB59FD1679CD689D09007703CB8962081BCF679524BEE7A45408AF07C279BA01AAEE22326DCEB5DD57C7C7728B6BE9B0DBE29C16DEB8F7A93B29712BA3C7A57A0212AA5722F851892BABC3458D0226191D8A5B0EFC8A24EF442F209DE055F21D2A04D4A9557E380DCB818F2970064D628D8533B44C15CCFE6BFD25B08EDFD75F514A06B8B0D0B0AD0CEBD46FFFDD880792BB6725A67EB611E277C854C4371AFB8EB2480F902F020B902EC02F902E801833DECAAB9010000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000001008400000000000000000000000000000000000000000000000008000000800000000000000000000100000000000000000000020000000000000000000800000000000000000080000010000000000000000800000000000000000000000000800000000000000080000000000000200000000000000000000000000800000000000000000000000000000000004000000002000000000001000000000000000000000000040000100000000020000000000000000000000800000000000000000000000000000000000000100000F901DDF89B94FE049F59963545BF5469F968E04C9646D6E2C2C5F863A0DDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EFA00000000000000000000000007B8C97C6A2FE7CB533D47B4544AEAA8FB5B8E8A2A00000000000000000000000000000000000000000000000000000000000000000A000000000000000000000000000000000000000000000024EE3E319532DD00000F9013D940000000000000000000000000000000000001010F884A04DFE1BBBCF077DDC3E01291EEA2D5C70C2B422B415D95645B9ADCFD678CB1D63A00000000000000000000000000000000000000000000000000000000000001010A00000000000000000000000007B8C97C6A2FE7CB533D47B4544AEAA8FB5B8E8A2A00000000000000000000000000E94B9B3FABD95338B8B23C36CAAE1D640E1339FB8A00000000000000000000000000000000000000000000000000003F09CEF021F0800000000000000000000000000000000000000000000000002E0EBB87F01AFB50000000000000000000000000000000000000000000006AB15E47439E3F2C12C00000000000000000000000000000000000000000000000002DCFB1B8FFF90AD0000000000000000000000000000000000000000000006AB15E864D6D2F4E03482002080 )
    • RootChainProxy.headerBlocks( 858950000 ) => ( root=C16E1CE392EDA16D8410F2D2D79FD2A449B6D23265977A28431010AB534D7A12, start=74744998, end=74747813, createdAt=1754224151, proposer=0x794e44D1334A56Fea7f4df12633b88820D0c5888 )
    • ERC20PredicateProxy.8274664f( )
      • ERC20Predicate.exitTokens( 0x7B8C97C6A2FE7Cb533D47B4544AeAa8FB5B8E8a2, rootToken=0x4b6d036d0BC62A633aCca6d10956E9dbBB16748f, log=0xF89B94FE049F59963545BF5469F968E04C9646D6E2C2C5F863A0DDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EFA00000000000000000000000007B8C97C6A2FE7CB533D47B4544AEAA8FB5B8E8A2A00000000000000000000000000000000000000000000000000000000000000000A000000000000000000000000000000000000000000000024EE3E319532DD00000 )
        • BloodCrystal.transfer( to=0x7B8C97C6A2FE7Cb533D47B4544AeAa8FB5B8E8a2, amount=10900000000000000000000 ) => ( True )
          exit[RootChainManager (ln:313)]
          File 1 of 6: RootChainManagerProxy
          // File: contracts/common/Proxy/IERCProxy.sol
          
          pragma solidity 0.6.6;
          
          interface IERCProxy {
              function proxyType() external pure returns (uint256 proxyTypeId);
          
              function implementation() external view returns (address codeAddr);
          }
          
          // File: contracts/common/Proxy/Proxy.sol
          
          pragma solidity 0.6.6;
          
          
          abstract contract Proxy is IERCProxy {
              function delegatedFwd(address _dst, bytes memory _calldata) internal {
                  // solium-disable-next-line security/no-inline-assembly
                  assembly {
                      let result := delegatecall(
                          sub(gas(), 10000),
                          _dst,
                          add(_calldata, 0x20),
                          mload(_calldata),
                          0,
                          0
                      )
                      let size := returndatasize()
          
                      let ptr := mload(0x40)
                      returndatacopy(ptr, 0, size)
          
                      // revert instead of invalid() bc if the underlying call failed with invalid() it already wasted gas.
                      // if the call returned error data, forward it
                      switch result
                          case 0 {
                              revert(ptr, size)
                          }
                          default {
                              return(ptr, size)
                          }
                  }
              }
          
              function proxyType() external virtual override pure returns (uint256 proxyTypeId) {
                  // Upgradeable proxy
                  proxyTypeId = 2;
              }
          
              function implementation() external virtual override view returns (address);
          }
          
          // File: contracts/common/Proxy/UpgradableProxy.sol
          
          pragma solidity 0.6.6;
          
          
          contract UpgradableProxy is Proxy {
              event ProxyUpdated(address indexed _new, address indexed _old);
              event ProxyOwnerUpdate(address _new, address _old);
          
              bytes32 constant IMPLEMENTATION_SLOT = keccak256("matic.network.proxy.implementation");
              bytes32 constant OWNER_SLOT = keccak256("matic.network.proxy.owner");
          
              constructor(address _proxyTo) public {
                  setProxyOwner(msg.sender);
                  setImplementation(_proxyTo);
              }
          
              fallback() external payable {
                  delegatedFwd(loadImplementation(), msg.data);
              }
          
              receive() external payable {
                  delegatedFwd(loadImplementation(), msg.data);
              }
          
              modifier onlyProxyOwner() {
                  require(loadProxyOwner() == msg.sender, "NOT_OWNER");
                  _;
              }
          
              function proxyOwner() external view returns(address) {
                  return loadProxyOwner();
              }
          
              function loadProxyOwner() internal view returns(address) {
                  address _owner;
                  bytes32 position = OWNER_SLOT;
                  assembly {
                      _owner := sload(position)
                  }
                  return _owner;
              }
          
              function implementation() external override view returns (address) {
                  return loadImplementation();
              }
          
              function loadImplementation() internal view returns(address) {
                  address _impl;
                  bytes32 position = IMPLEMENTATION_SLOT;
                  assembly {
                      _impl := sload(position)
                  }
                  return _impl;
              }
          
              function transferProxyOwnership(address newOwner) public onlyProxyOwner {
                  require(newOwner != address(0), "ZERO_ADDRESS");
                  emit ProxyOwnerUpdate(newOwner, loadProxyOwner());
                  setProxyOwner(newOwner);
              }
          
              function setProxyOwner(address newOwner) private {
                  bytes32 position = OWNER_SLOT;
                  assembly {
                      sstore(position, newOwner)
                  }
              }
          
              function updateImplementation(address _newProxyTo) public onlyProxyOwner {
                  require(_newProxyTo != address(0x0), "INVALID_PROXY_ADDRESS");
                  require(isContract(_newProxyTo), "DESTINATION_ADDRESS_IS_NOT_A_CONTRACT");
          
                  emit ProxyUpdated(_newProxyTo, loadImplementation());
                  
                  setImplementation(_newProxyTo);
              }
          
              function updateAndCall(address _newProxyTo, bytes memory data) payable public onlyProxyOwner {
                  updateImplementation(_newProxyTo);
          
                  (bool success, bytes memory returnData) = address(this).call{value: msg.value}(data);
                  require(success, string(returnData));
              }
          
              function setImplementation(address _newProxyTo) private {
                  bytes32 position = IMPLEMENTATION_SLOT;
                  assembly {
                      sstore(position, _newProxyTo)
                  }
              }
              
              function isContract(address _target) internal view returns (bool) {
                  if (_target == address(0)) {
                      return false;
                  }
          
                  uint256 size;
                  assembly {
                      size := extcodesize(_target)
                  }
                  return size > 0;
              }
          }
          
          // File: contracts/root/RootChainManager/RootChainManagerProxy.sol
          
          pragma solidity 0.6.6;
          
          
          contract RootChainManagerProxy is UpgradableProxy {
              constructor(address _proxyTo)
                  public
                  UpgradableProxy(_proxyTo)
              {}
          }

          File 2 of 6: ERC20PredicateProxy
          // File: contracts/common/Proxy/IERCProxy.sol
          
          pragma solidity 0.6.6;
          
          interface IERCProxy {
              function proxyType() external pure returns (uint256 proxyTypeId);
          
              function implementation() external view returns (address codeAddr);
          }
          
          // File: contracts/common/Proxy/Proxy.sol
          
          pragma solidity 0.6.6;
          
          
          abstract contract Proxy is IERCProxy {
              function delegatedFwd(address _dst, bytes memory _calldata) internal {
                  // solium-disable-next-line security/no-inline-assembly
                  assembly {
                      let result := delegatecall(
                          sub(gas(), 10000),
                          _dst,
                          add(_calldata, 0x20),
                          mload(_calldata),
                          0,
                          0
                      )
                      let size := returndatasize()
          
                      let ptr := mload(0x40)
                      returndatacopy(ptr, 0, size)
          
                      // revert instead of invalid() bc if the underlying call failed with invalid() it already wasted gas.
                      // if the call returned error data, forward it
                      switch result
                          case 0 {
                              revert(ptr, size)
                          }
                          default {
                              return(ptr, size)
                          }
                  }
              }
          
              function proxyType() external virtual override pure returns (uint256 proxyTypeId) {
                  // Upgradeable proxy
                  proxyTypeId = 2;
              }
          
              function implementation() external virtual override view returns (address);
          }
          
          // File: contracts/common/Proxy/UpgradableProxy.sol
          
          pragma solidity 0.6.6;
          
          
          contract UpgradableProxy is Proxy {
              event ProxyUpdated(address indexed _new, address indexed _old);
              event ProxyOwnerUpdate(address _new, address _old);
          
              bytes32 constant IMPLEMENTATION_SLOT = keccak256("matic.network.proxy.implementation");
              bytes32 constant OWNER_SLOT = keccak256("matic.network.proxy.owner");
          
              constructor(address _proxyTo) public {
                  setProxyOwner(msg.sender);
                  setImplementation(_proxyTo);
              }
          
              fallback() external payable {
                  delegatedFwd(loadImplementation(), msg.data);
              }
          
              receive() external payable {
                  delegatedFwd(loadImplementation(), msg.data);
              }
          
              modifier onlyProxyOwner() {
                  require(loadProxyOwner() == msg.sender, "NOT_OWNER");
                  _;
              }
          
              function proxyOwner() external view returns(address) {
                  return loadProxyOwner();
              }
          
              function loadProxyOwner() internal view returns(address) {
                  address _owner;
                  bytes32 position = OWNER_SLOT;
                  assembly {
                      _owner := sload(position)
                  }
                  return _owner;
              }
          
              function implementation() external override view returns (address) {
                  return loadImplementation();
              }
          
              function loadImplementation() internal view returns(address) {
                  address _impl;
                  bytes32 position = IMPLEMENTATION_SLOT;
                  assembly {
                      _impl := sload(position)
                  }
                  return _impl;
              }
          
              function transferProxyOwnership(address newOwner) public onlyProxyOwner {
                  require(newOwner != address(0), "ZERO_ADDRESS");
                  emit ProxyOwnerUpdate(newOwner, loadProxyOwner());
                  setProxyOwner(newOwner);
              }
          
              function setProxyOwner(address newOwner) private {
                  bytes32 position = OWNER_SLOT;
                  assembly {
                      sstore(position, newOwner)
                  }
              }
          
              function updateImplementation(address _newProxyTo) public onlyProxyOwner {
                  require(_newProxyTo != address(0x0), "INVALID_PROXY_ADDRESS");
                  require(isContract(_newProxyTo), "DESTINATION_ADDRESS_IS_NOT_A_CONTRACT");
          
                  emit ProxyUpdated(_newProxyTo, loadImplementation());
                  
                  setImplementation(_newProxyTo);
              }
          
              function updateAndCall(address _newProxyTo, bytes memory data) payable public onlyProxyOwner {
                  updateImplementation(_newProxyTo);
          
                  (bool success, bytes memory returnData) = address(this).call{value: msg.value}(data);
                  require(success, string(returnData));
              }
          
              function setImplementation(address _newProxyTo) private {
                  bytes32 position = IMPLEMENTATION_SLOT;
                  assembly {
                      sstore(position, _newProxyTo)
                  }
              }
              
              function isContract(address _target) internal view returns (bool) {
                  if (_target == address(0)) {
                      return false;
                  }
          
                  uint256 size;
                  assembly {
                      size := extcodesize(_target)
                  }
                  return size > 0;
              }
          }
          
          // File: contracts/root/TokenPredicates/ERC20PredicateProxy.sol
          
          pragma solidity 0.6.6;
          
          
          contract ERC20PredicateProxy is UpgradableProxy {
              constructor(address _proxyTo)
                  public
                  UpgradableProxy(_proxyTo)
              {}
          }

          File 3 of 6: BloodCrystal
          // Sources flattened with hardhat v2.17.1 https://hardhat.org
          
          // SPDX-License-Identifier: MIT
          
          // File @openzeppelin/contracts-upgradeable/access/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
          
          pragma solidity ^0.8.0;
          
          /**
           * @dev External interface of AccessControl declared to support ERC165 detection.
           */
          interface IAccessControlUpgradeable {
              /**
               * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
               *
               * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
               * {RoleAdminChanged} not being emitted signaling this.
               *
               * _Available since v3.1._
               */
              event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
          
              /**
               * @dev Emitted when `account` is granted `role`.
               *
               * `sender` is the account that originated the contract call, an admin role
               * bearer except when using {AccessControl-_setupRole}.
               */
              event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
          
              /**
               * @dev Emitted when `account` is revoked `role`.
               *
               * `sender` is the account that originated the contract call:
               *   - if using `revokeRole`, it is the admin role bearer
               *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
               */
              event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
          
              /**
               * @dev Returns `true` if `account` has been granted `role`.
               */
              function hasRole(bytes32 role, address account) external view returns (bool);
          
              /**
               * @dev Returns the admin role that controls `role`. See {grantRole} and
               * {revokeRole}.
               *
               * To change a role's admin, use {AccessControl-_setRoleAdmin}.
               */
              function getRoleAdmin(bytes32 role) external view returns (bytes32);
          
              /**
               * @dev Grants `role` to `account`.
               *
               * If `account` had not been already granted `role`, emits a {RoleGranted}
               * event.
               *
               * Requirements:
               *
               * - the caller must have ``role``'s admin role.
               */
              function grantRole(bytes32 role, address account) external;
          
              /**
               * @dev Revokes `role` from `account`.
               *
               * If `account` had been granted `role`, emits a {RoleRevoked} event.
               *
               * Requirements:
               *
               * - the caller must have ``role``'s admin role.
               */
              function revokeRole(bytes32 role, address account) external;
          
              /**
               * @dev Revokes `role` from the calling account.
               *
               * Roles are often managed via {grantRole} and {revokeRole}: this function's
               * purpose is to provide a mechanism for accounts to lose their privileges
               * if they are compromised (such as when a trusted device is misplaced).
               *
               * If the calling account had been granted `role`, emits a {RoleRevoked}
               * event.
               *
               * Requirements:
               *
               * - the caller must be `account`.
               */
              function renounceRole(bytes32 role, address account) external;
          }
          
          
          // File @openzeppelin/contracts-upgradeable/utils/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
          
          pragma solidity ^0.8.1;
          
          /**
           * @dev Collection of functions related to the address type
           */
          library AddressUpgradeable {
              /**
               * @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
               *
               * Furthermore, `isContract` will also return true if the target contract within
               * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
               * which only has an effect at the end of a transaction.
               * ====
               *
               * [IMPORTANT]
               * ====
               * You shouldn't rely on `isContract` to protect against flash loan attacks!
               *
               * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
               * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
               * constructor.
               * ====
               */
              function isContract(address account) internal view returns (bool) {
                  // This method relies on extcodesize/address.code.length, which returns 0
                  // for contracts in construction, since the code is only stored at the end
                  // of the constructor execution.
          
                  return account.code.length > 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://consensys.net/diligence/blog/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.8.0/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");
          
                  (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 functionCallWithValue(target, data, 0, "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");
                  (bool success, bytes memory returndata) = target.call{value: value}(data);
                  return verifyCallResultFromTarget(target, 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) {
                  (bool success, bytes memory returndata) = target.staticcall(data);
                  return verifyCallResultFromTarget(target, 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) {
                  (bool success, bytes memory returndata) = target.delegatecall(data);
                  return verifyCallResultFromTarget(target, success, returndata, errorMessage);
              }
          
              /**
               * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
               * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
               *
               * _Available since v4.8._
               */
              function verifyCallResultFromTarget(
                  address target,
                  bool success,
                  bytes memory returndata,
                  string memory errorMessage
              ) internal view returns (bytes memory) {
                  if (success) {
                      if (returndata.length == 0) {
                          // only check isContract if the call was successful and the return data is empty
                          // otherwise we already know that it was a contract
                          require(isContract(target), "Address: call to non-contract");
                      }
                      return returndata;
                  } else {
                      _revert(returndata, errorMessage);
                  }
              }
          
              /**
               * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
               * revert reason or using the provided one.
               *
               * _Available since v4.3._
               */
              function verifyCallResult(
                  bool success,
                  bytes memory returndata,
                  string memory errorMessage
              ) internal pure returns (bytes memory) {
                  if (success) {
                      return returndata;
                  } else {
                      _revert(returndata, errorMessage);
                  }
              }
          
              function _revert(bytes memory returndata, string memory errorMessage) private pure {
                  // 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
                      /// @solidity memory-safe-assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
          
          
          // File @openzeppelin/contracts-upgradeable/proxy/utils/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
          
          pragma solidity ^0.8.2;
          
          /**
           * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
           * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
           * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
           * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
           *
           * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
           * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
           * case an upgrade adds a module that needs to be initialized.
           *
           * For example:
           *
           * [.hljs-theme-light.nopadding]
           * ```solidity
           * contract MyToken is ERC20Upgradeable {
           *     function initialize() initializer public {
           *         __ERC20_init("MyToken", "MTK");
           *     }
           * }
           *
           * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
           *     function initializeV2() reinitializer(2) public {
           *         __ERC20Permit_init("MyToken");
           *     }
           * }
           * ```
           *
           * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
           * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
           *
           * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
           * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
           *
           * [CAUTION]
           * ====
           * Avoid leaving a contract uninitialized.
           *
           * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
           * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
           * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
           *
           * [.hljs-theme-light.nopadding]
           * ```
           * /// @custom:oz-upgrades-unsafe-allow constructor
           * constructor() {
           *     _disableInitializers();
           * }
           * ```
           * ====
           */
          abstract contract Initializable {
              /**
               * @dev Indicates that the contract has been initialized.
               * @custom:oz-retyped-from bool
               */
              uint8 private _initialized;
          
              /**
               * @dev Indicates that the contract is in the process of being initialized.
               */
              bool private _initializing;
          
              /**
               * @dev Triggered when the contract has been initialized or reinitialized.
               */
              event Initialized(uint8 version);
          
              /**
               * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
               * `onlyInitializing` functions can be used to initialize parent contracts.
               *
               * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
               * constructor.
               *
               * Emits an {Initialized} event.
               */
              modifier initializer() {
                  bool isTopLevelCall = !_initializing;
                  require(
                      (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
                      "Initializable: contract is already initialized"
                  );
                  _initialized = 1;
                  if (isTopLevelCall) {
                      _initializing = true;
                  }
                  _;
                  if (isTopLevelCall) {
                      _initializing = false;
                      emit Initialized(1);
                  }
              }
          
              /**
               * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
               * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
               * used to initialize parent contracts.
               *
               * A reinitializer may be used after the original initialization step. This is essential to configure modules that
               * are added through upgrades and that require initialization.
               *
               * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
               * cannot be nested. If one is invoked in the context of another, execution will revert.
               *
               * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
               * a contract, executing them in the right order is up to the developer or operator.
               *
               * WARNING: setting the version to 255 will prevent any future reinitialization.
               *
               * Emits an {Initialized} event.
               */
              modifier reinitializer(uint8 version) {
                  require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
                  _initialized = version;
                  _initializing = true;
                  _;
                  _initializing = false;
                  emit Initialized(version);
              }
          
              /**
               * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
               * {initializer} and {reinitializer} modifiers, directly or indirectly.
               */
              modifier onlyInitializing() {
                  require(_initializing, "Initializable: contract is not initializing");
                  _;
              }
          
              /**
               * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
               * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
               * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
               * through proxies.
               *
               * Emits an {Initialized} event the first time it is successfully executed.
               */
              function _disableInitializers() internal virtual {
                  require(!_initializing, "Initializable: contract is initializing");
                  if (_initialized != type(uint8).max) {
                      _initialized = type(uint8).max;
                      emit Initialized(type(uint8).max);
                  }
              }
          
              /**
               * @dev Returns the highest version that has been initialized. See {reinitializer}.
               */
              function _getInitializedVersion() internal view returns (uint8) {
                  return _initialized;
              }
          
              /**
               * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
               */
              function _isInitializing() internal view returns (bool) {
                  return _initializing;
              }
          }
          
          
          // File @openzeppelin/contracts-upgradeable/utils/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
          
          pragma solidity ^0.8.0;
          
          /**
           * @dev Provides information about the current execution context, including the
           * sender of the transaction and its data. While these are generally available
           * via msg.sender and msg.data, they should not be accessed in such a direct
           * manner, since when dealing with meta-transactions the account sending and
           * paying for execution may not be the actual sender (as far as an application
           * is concerned).
           *
           * This contract is only required for intermediate, library-like contracts.
           */
          abstract contract ContextUpgradeable is Initializable {
              function __Context_init() internal onlyInitializing {
              }
          
              function __Context_init_unchained() internal onlyInitializing {
              }
              function _msgSender() internal view virtual returns (address) {
                  return msg.sender;
              }
          
              function _msgData() internal view virtual returns (bytes calldata) {
                  return msg.data;
              }
          
              /**
               * @dev This empty reserved space is put in place to allow future versions to add new
               * variables without shifting down storage in the inheritance chain.
               * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
               */
              uint256[50] private __gap;
          }
          
          
          // File @openzeppelin/contracts-upgradeable/utils/introspection/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
          
          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 IERC165Upgradeable {
              /**
               * @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);
          }
          
          
          // File @openzeppelin/contracts-upgradeable/utils/introspection/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
          
          pragma solidity ^0.8.0;
          
          
          /**
           * @dev Implementation of the {IERC165} interface.
           *
           * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
           * for the additional interface id that will be supported. For example:
           *
           * ```solidity
           * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
           *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
           * }
           * ```
           *
           * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
           */
          abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
              function __ERC165_init() internal onlyInitializing {
              }
          
              function __ERC165_init_unchained() internal onlyInitializing {
              }
              /**
               * @dev See {IERC165-supportsInterface}.
               */
              function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                  return interfaceId == type(IERC165Upgradeable).interfaceId;
              }
          
              /**
               * @dev This empty reserved space is put in place to allow future versions to add new
               * variables without shifting down storage in the inheritance chain.
               * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
               */
              uint256[50] private __gap;
          }
          
          
          // File @openzeppelin/contracts-upgradeable/utils/math/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
          
          pragma solidity ^0.8.0;
          
          /**
           * @dev Standard math utilities missing in the Solidity language.
           */
          library MathUpgradeable {
              enum Rounding {
                  Down, // Toward negative infinity
                  Up, // Toward infinity
                  Zero // Toward zero
              }
          
              /**
               * @dev Returns the largest of two numbers.
               */
              function max(uint256 a, uint256 b) internal pure returns (uint256) {
                  return a > b ? a : b;
              }
          
              /**
               * @dev Returns the smallest of two numbers.
               */
              function min(uint256 a, uint256 b) internal pure returns (uint256) {
                  return a < b ? a : b;
              }
          
              /**
               * @dev Returns the average of two numbers. The result is rounded towards
               * zero.
               */
              function average(uint256 a, uint256 b) internal pure returns (uint256) {
                  // (a + b) / 2 can overflow.
                  return (a & b) + (a ^ b) / 2;
              }
          
              /**
               * @dev Returns the ceiling of the division of two numbers.
               *
               * This differs from standard division with `/` in that it rounds up instead
               * of rounding down.
               */
              function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
                  // (a + b - 1) / b can overflow on addition, so we distribute.
                  return a == 0 ? 0 : (a - 1) / b + 1;
              }
          
              /**
               * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
               * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
               * with further edits by Uniswap Labs also under MIT license.
               */
              function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
                  unchecked {
                      // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                      // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                      // variables such that product = prod1 * 2^256 + prod0.
                      uint256 prod0; // Least significant 256 bits of the product
                      uint256 prod1; // Most significant 256 bits of the product
                      assembly {
                          let mm := mulmod(x, y, not(0))
                          prod0 := mul(x, y)
                          prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                      }
          
                      // Handle non-overflow cases, 256 by 256 division.
                      if (prod1 == 0) {
                          // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                          // The surrounding unchecked block does not change this fact.
                          // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                          return prod0 / denominator;
                      }
          
                      // Make sure the result is less than 2^256. Also prevents denominator == 0.
                      require(denominator > prod1, "Math: mulDiv overflow");
          
                      ///////////////////////////////////////////////
                      // 512 by 256 division.
                      ///////////////////////////////////////////////
          
                      // Make division exact by subtracting the remainder from [prod1 prod0].
                      uint256 remainder;
                      assembly {
                          // Compute remainder using mulmod.
                          remainder := mulmod(x, y, denominator)
          
                          // Subtract 256 bit number from 512 bit number.
                          prod1 := sub(prod1, gt(remainder, prod0))
                          prod0 := sub(prod0, remainder)
                      }
          
                      // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
                      // See https://cs.stackexchange.com/q/138556/92363.
          
                      // Does not overflow because the denominator cannot be zero at this stage in the function.
                      uint256 twos = denominator & (~denominator + 1);
                      assembly {
                          // Divide denominator by twos.
                          denominator := div(denominator, twos)
          
                          // Divide [prod1 prod0] by twos.
                          prod0 := div(prod0, twos)
          
                          // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                          twos := add(div(sub(0, twos), twos), 1)
                      }
          
                      // Shift in bits from prod1 into prod0.
                      prod0 |= prod1 * twos;
          
                      // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                      // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                      // four bits. That is, denominator * inv = 1 mod 2^4.
                      uint256 inverse = (3 * denominator) ^ 2;
          
                      // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                      // in modular arithmetic, doubling the correct bits in each step.
                      inverse *= 2 - denominator * inverse; // inverse mod 2^8
                      inverse *= 2 - denominator * inverse; // inverse mod 2^16
                      inverse *= 2 - denominator * inverse; // inverse mod 2^32
                      inverse *= 2 - denominator * inverse; // inverse mod 2^64
                      inverse *= 2 - denominator * inverse; // inverse mod 2^128
                      inverse *= 2 - denominator * inverse; // inverse mod 2^256
          
                      // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                      // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                      // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                      // is no longer required.
                      result = prod0 * inverse;
                      return result;
                  }
              }
          
              /**
               * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
               */
              function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
                  uint256 result = mulDiv(x, y, denominator);
                  if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
                      result += 1;
                  }
                  return result;
              }
          
              /**
               * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
               *
               * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
               */
              function sqrt(uint256 a) internal pure returns (uint256) {
                  if (a == 0) {
                      return 0;
                  }
          
                  // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
                  //
                  // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
                  // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
                  //
                  // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
                  // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
                  // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
                  //
                  // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
                  uint256 result = 1 << (log2(a) >> 1);
          
                  // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
                  // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
                  // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
                  // into the expected uint128 result.
                  unchecked {
                      result = (result + a / result) >> 1;
                      result = (result + a / result) >> 1;
                      result = (result + a / result) >> 1;
                      result = (result + a / result) >> 1;
                      result = (result + a / result) >> 1;
                      result = (result + a / result) >> 1;
                      result = (result + a / result) >> 1;
                      return min(result, a / result);
                  }
              }
          
              /**
               * @notice Calculates sqrt(a), following the selected rounding direction.
               */
              function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
                  unchecked {
                      uint256 result = sqrt(a);
                      return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
                  }
              }
          
              /**
               * @dev Return the log in base 2, rounded down, of a positive value.
               * Returns 0 if given 0.
               */
              function log2(uint256 value) internal pure returns (uint256) {
                  uint256 result = 0;
                  unchecked {
                      if (value >> 128 > 0) {
                          value >>= 128;
                          result += 128;
                      }
                      if (value >> 64 > 0) {
                          value >>= 64;
                          result += 64;
                      }
                      if (value >> 32 > 0) {
                          value >>= 32;
                          result += 32;
                      }
                      if (value >> 16 > 0) {
                          value >>= 16;
                          result += 16;
                      }
                      if (value >> 8 > 0) {
                          value >>= 8;
                          result += 8;
                      }
                      if (value >> 4 > 0) {
                          value >>= 4;
                          result += 4;
                      }
                      if (value >> 2 > 0) {
                          value >>= 2;
                          result += 2;
                      }
                      if (value >> 1 > 0) {
                          result += 1;
                      }
                  }
                  return result;
              }
          
              /**
               * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
               * Returns 0 if given 0.
               */
              function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
                  unchecked {
                      uint256 result = log2(value);
                      return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
                  }
              }
          
              /**
               * @dev Return the log in base 10, rounded down, of a positive value.
               * Returns 0 if given 0.
               */
              function log10(uint256 value) internal pure returns (uint256) {
                  uint256 result = 0;
                  unchecked {
                      if (value >= 10 ** 64) {
                          value /= 10 ** 64;
                          result += 64;
                      }
                      if (value >= 10 ** 32) {
                          value /= 10 ** 32;
                          result += 32;
                      }
                      if (value >= 10 ** 16) {
                          value /= 10 ** 16;
                          result += 16;
                      }
                      if (value >= 10 ** 8) {
                          value /= 10 ** 8;
                          result += 8;
                      }
                      if (value >= 10 ** 4) {
                          value /= 10 ** 4;
                          result += 4;
                      }
                      if (value >= 10 ** 2) {
                          value /= 10 ** 2;
                          result += 2;
                      }
                      if (value >= 10 ** 1) {
                          result += 1;
                      }
                  }
                  return result;
              }
          
              /**
               * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
               * Returns 0 if given 0.
               */
              function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
                  unchecked {
                      uint256 result = log10(value);
                      return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
                  }
              }
          
              /**
               * @dev Return the log in base 256, rounded down, of a positive value.
               * Returns 0 if given 0.
               *
               * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
               */
              function log256(uint256 value) internal pure returns (uint256) {
                  uint256 result = 0;
                  unchecked {
                      if (value >> 128 > 0) {
                          value >>= 128;
                          result += 16;
                      }
                      if (value >> 64 > 0) {
                          value >>= 64;
                          result += 8;
                      }
                      if (value >> 32 > 0) {
                          value >>= 32;
                          result += 4;
                      }
                      if (value >> 16 > 0) {
                          value >>= 16;
                          result += 2;
                      }
                      if (value >> 8 > 0) {
                          result += 1;
                      }
                  }
                  return result;
              }
          
              /**
               * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
               * Returns 0 if given 0.
               */
              function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
                  unchecked {
                      uint256 result = log256(value);
                      return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
                  }
              }
          }
          
          
          // File @openzeppelin/contracts-upgradeable/utils/math/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
          
          pragma solidity ^0.8.0;
          
          /**
           * @dev Standard signed math utilities missing in the Solidity language.
           */
          library SignedMathUpgradeable {
              /**
               * @dev Returns the largest of two signed numbers.
               */
              function max(int256 a, int256 b) internal pure returns (int256) {
                  return a > b ? a : b;
              }
          
              /**
               * @dev Returns the smallest of two signed numbers.
               */
              function min(int256 a, int256 b) internal pure returns (int256) {
                  return a < b ? a : b;
              }
          
              /**
               * @dev Returns the average of two signed numbers without overflow.
               * The result is rounded towards zero.
               */
              function average(int256 a, int256 b) internal pure returns (int256) {
                  // Formula from the book "Hacker's Delight"
                  int256 x = (a & b) + ((a ^ b) >> 1);
                  return x + (int256(uint256(x) >> 255) & (a ^ b));
              }
          
              /**
               * @dev Returns the absolute unsigned value of a signed value.
               */
              function abs(int256 n) internal pure returns (uint256) {
                  unchecked {
                      // must be unchecked in order to support `n = type(int256).min`
                      return uint256(n >= 0 ? n : -n);
                  }
              }
          }
          
          
          // File @openzeppelin/contracts-upgradeable/utils/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
          
          pragma solidity ^0.8.0;
          
          
          /**
           * @dev String operations.
           */
          library StringsUpgradeable {
              bytes16 private constant _SYMBOLS = "0123456789abcdef";
              uint8 private constant _ADDRESS_LENGTH = 20;
          
              /**
               * @dev Converts a `uint256` to its ASCII `string` decimal representation.
               */
              function toString(uint256 value) internal pure returns (string memory) {
                  unchecked {
                      uint256 length = MathUpgradeable.log10(value) + 1;
                      string memory buffer = new string(length);
                      uint256 ptr;
                      /// @solidity memory-safe-assembly
                      assembly {
                          ptr := add(buffer, add(32, length))
                      }
                      while (true) {
                          ptr--;
                          /// @solidity memory-safe-assembly
                          assembly {
                              mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                          }
                          value /= 10;
                          if (value == 0) break;
                      }
                      return buffer;
                  }
              }
          
              /**
               * @dev Converts a `int256` to its ASCII `string` decimal representation.
               */
              function toString(int256 value) internal pure returns (string memory) {
                  return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMathUpgradeable.abs(value))));
              }
          
              /**
               * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
               */
              function toHexString(uint256 value) internal pure returns (string memory) {
                  unchecked {
                      return toHexString(value, MathUpgradeable.log256(value) + 1);
                  }
              }
          
              /**
               * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
               */
              function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                  bytes memory buffer = new bytes(2 * length + 2);
                  buffer[0] = "0";
                  buffer[1] = "x";
                  for (uint256 i = 2 * length + 1; i > 1; --i) {
                      buffer[i] = _SYMBOLS[value & 0xf];
                      value >>= 4;
                  }
                  require(value == 0, "Strings: hex length insufficient");
                  return string(buffer);
              }
          
              /**
               * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
               */
              function toHexString(address addr) internal pure returns (string memory) {
                  return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
              }
          
              /**
               * @dev Returns true if the two strings are equal.
               */
              function equal(string memory a, string memory b) internal pure returns (bool) {
                  return keccak256(bytes(a)) == keccak256(bytes(b));
              }
          }
          
          
          // File @openzeppelin/contracts-upgradeable/access/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)
          
          pragma solidity ^0.8.0;
          
          
          
          
          
          /**
           * @dev Contract module that allows children to implement role-based access
           * control mechanisms. This is a lightweight version that doesn't allow enumerating role
           * members except through off-chain means by accessing the contract event logs. Some
           * applications may benefit from on-chain enumerability, for those cases see
           * {AccessControlEnumerable}.
           *
           * Roles are referred to by their `bytes32` identifier. These should be exposed
           * in the external API and be unique. The best way to achieve this is by
           * using `public constant` hash digests:
           *
           * ```solidity
           * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
           * ```
           *
           * Roles can be used to represent a set of permissions. To restrict access to a
           * function call, use {hasRole}:
           *
           * ```solidity
           * function foo() public {
           *     require(hasRole(MY_ROLE, msg.sender));
           *     ...
           * }
           * ```
           *
           * Roles can be granted and revoked dynamically via the {grantRole} and
           * {revokeRole} functions. Each role has an associated admin role, and only
           * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
           *
           * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
           * that only accounts with this role will be able to grant or revoke other
           * roles. More complex role relationships can be created by using
           * {_setRoleAdmin}.
           *
           * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
           * grant and revoke this role. Extra precautions should be taken to secure
           * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
           * to enforce additional security measures for this role.
           */
          abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControlUpgradeable, ERC165Upgradeable {
              function __AccessControl_init() internal onlyInitializing {
              }
          
              function __AccessControl_init_unchained() internal onlyInitializing {
              }
              struct RoleData {
                  mapping(address => bool) members;
                  bytes32 adminRole;
              }
          
              mapping(bytes32 => RoleData) private _roles;
          
              bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
          
              /**
               * @dev Modifier that checks that an account has a specific role. Reverts
               * with a standardized message including the required role.
               *
               * The format of the revert reason is given by the following regular expression:
               *
               *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
               *
               * _Available since v4.1._
               */
              modifier onlyRole(bytes32 role) {
                  _checkRole(role);
                  _;
              }
          
              /**
               * @dev See {IERC165-supportsInterface}.
               */
              function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                  return interfaceId == type(IAccessControlUpgradeable).interfaceId || super.supportsInterface(interfaceId);
              }
          
              /**
               * @dev Returns `true` if `account` has been granted `role`.
               */
              function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
                  return _roles[role].members[account];
              }
          
              /**
               * @dev Revert with a standard message if `_msgSender()` is missing `role`.
               * Overriding this function changes the behavior of the {onlyRole} modifier.
               *
               * Format of the revert message is described in {_checkRole}.
               *
               * _Available since v4.6._
               */
              function _checkRole(bytes32 role) internal view virtual {
                  _checkRole(role, _msgSender());
              }
          
              /**
               * @dev Revert with a standard message if `account` is missing `role`.
               *
               * The format of the revert reason is given by the following regular expression:
               *
               *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
               */
              function _checkRole(bytes32 role, address account) internal view virtual {
                  if (!hasRole(role, account)) {
                      revert(
                          string(
                              abi.encodePacked(
                                  "AccessControl: account ",
                                  StringsUpgradeable.toHexString(account),
                                  " is missing role ",
                                  StringsUpgradeable.toHexString(uint256(role), 32)
                              )
                          )
                      );
                  }
              }
          
              /**
               * @dev Returns the admin role that controls `role`. See {grantRole} and
               * {revokeRole}.
               *
               * To change a role's admin, use {_setRoleAdmin}.
               */
              function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
                  return _roles[role].adminRole;
              }
          
              /**
               * @dev Grants `role` to `account`.
               *
               * If `account` had not been already granted `role`, emits a {RoleGranted}
               * event.
               *
               * Requirements:
               *
               * - the caller must have ``role``'s admin role.
               *
               * May emit a {RoleGranted} event.
               */
              function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
                  _grantRole(role, account);
              }
          
              /**
               * @dev Revokes `role` from `account`.
               *
               * If `account` had been granted `role`, emits a {RoleRevoked} event.
               *
               * Requirements:
               *
               * - the caller must have ``role``'s admin role.
               *
               * May emit a {RoleRevoked} event.
               */
              function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
                  _revokeRole(role, account);
              }
          
              /**
               * @dev Revokes `role` from the calling account.
               *
               * Roles are often managed via {grantRole} and {revokeRole}: this function's
               * purpose is to provide a mechanism for accounts to lose their privileges
               * if they are compromised (such as when a trusted device is misplaced).
               *
               * If the calling account had been revoked `role`, emits a {RoleRevoked}
               * event.
               *
               * Requirements:
               *
               * - the caller must be `account`.
               *
               * May emit a {RoleRevoked} event.
               */
              function renounceRole(bytes32 role, address account) public virtual override {
                  require(account == _msgSender(), "AccessControl: can only renounce roles for self");
          
                  _revokeRole(role, account);
              }
          
              /**
               * @dev Grants `role` to `account`.
               *
               * If `account` had not been already granted `role`, emits a {RoleGranted}
               * event. Note that unlike {grantRole}, this function doesn't perform any
               * checks on the calling account.
               *
               * May emit a {RoleGranted} event.
               *
               * [WARNING]
               * ====
               * This function should only be called from the constructor when setting
               * up the initial roles for the system.
               *
               * Using this function in any other way is effectively circumventing the admin
               * system imposed by {AccessControl}.
               * ====
               *
               * NOTE: This function is deprecated in favor of {_grantRole}.
               */
              function _setupRole(bytes32 role, address account) internal virtual {
                  _grantRole(role, account);
              }
          
              /**
               * @dev Sets `adminRole` as ``role``'s admin role.
               *
               * Emits a {RoleAdminChanged} event.
               */
              function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
                  bytes32 previousAdminRole = getRoleAdmin(role);
                  _roles[role].adminRole = adminRole;
                  emit RoleAdminChanged(role, previousAdminRole, adminRole);
              }
          
              /**
               * @dev Grants `role` to `account`.
               *
               * Internal function without access restriction.
               *
               * May emit a {RoleGranted} event.
               */
              function _grantRole(bytes32 role, address account) internal virtual {
                  if (!hasRole(role, account)) {
                      _roles[role].members[account] = true;
                      emit RoleGranted(role, account, _msgSender());
                  }
              }
          
              /**
               * @dev Revokes `role` from `account`.
               *
               * Internal function without access restriction.
               *
               * May emit a {RoleRevoked} event.
               */
              function _revokeRole(bytes32 role, address account) internal virtual {
                  if (hasRole(role, account)) {
                      _roles[role].members[account] = false;
                      emit RoleRevoked(role, account, _msgSender());
                  }
              }
          
              /**
               * @dev This empty reserved space is put in place to allow future versions to add new
               * variables without shifting down storage in the inheritance chain.
               * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
               */
              uint256[49] private __gap;
          }
          
          
          // File @openzeppelin/contracts-upgradeable/interfaces/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
          
          pragma solidity ^0.8.0;
          
          /**
           * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
           * proxy whose upgrades are fully controlled by the current implementation.
           */
          interface IERC1822ProxiableUpgradeable {
              /**
               * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
               * address.
               *
               * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
               * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
               * function revert if invoked through a proxy.
               */
              function proxiableUUID() external view returns (bytes32);
          }
          
          
          // File @openzeppelin/contracts-upgradeable/interfaces/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC1967.sol)
          
          pragma solidity ^0.8.0;
          
          /**
           * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
           *
           * _Available since v4.8.3._
           */
          interface IERC1967Upgradeable {
              /**
               * @dev Emitted when the implementation is upgraded.
               */
              event Upgraded(address indexed implementation);
          
              /**
               * @dev Emitted when the admin account has changed.
               */
              event AdminChanged(address previousAdmin, address newAdmin);
          
              /**
               * @dev Emitted when the beacon is changed.
               */
              event BeaconUpgraded(address indexed beacon);
          }
          
          
          // File @openzeppelin/contracts-upgradeable/proxy/beacon/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
          
          pragma solidity ^0.8.0;
          
          /**
           * @dev This is the interface that {BeaconProxy} expects of its beacon.
           */
          interface IBeaconUpgradeable {
              /**
               * @dev Must return an address that can be used as a delegate call target.
               *
               * {BeaconProxy} will check that this address is a contract.
               */
              function implementation() external view returns (address);
          }
          
          
          // File @openzeppelin/contracts-upgradeable/utils/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
          // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
          
          pragma solidity ^0.8.0;
          
          /**
           * @dev Library for reading and writing primitive types to specific storage slots.
           *
           * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
           * This library helps with reading and writing to such slots without the need for inline assembly.
           *
           * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
           *
           * Example usage to set ERC1967 implementation slot:
           * ```solidity
           * contract ERC1967 {
           *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
           *
           *     function _getImplementation() internal view returns (address) {
           *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
           *     }
           *
           *     function _setImplementation(address newImplementation) internal {
           *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
           *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
           *     }
           * }
           * ```
           *
           * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._
           * _Available since v4.9 for `string`, `bytes`._
           */
          library StorageSlotUpgradeable {
              struct AddressSlot {
                  address value;
              }
          
              struct BooleanSlot {
                  bool value;
              }
          
              struct Bytes32Slot {
                  bytes32 value;
              }
          
              struct Uint256Slot {
                  uint256 value;
              }
          
              struct StringSlot {
                  string value;
              }
          
              struct BytesSlot {
                  bytes value;
              }
          
              /**
               * @dev Returns an `AddressSlot` with member `value` located at `slot`.
               */
              function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      r.slot := slot
                  }
              }
          
              /**
               * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
               */
              function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      r.slot := slot
                  }
              }
          
              /**
               * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
               */
              function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      r.slot := slot
                  }
              }
          
              /**
               * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
               */
              function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      r.slot := slot
                  }
              }
          
              /**
               * @dev Returns an `StringSlot` with member `value` located at `slot`.
               */
              function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      r.slot := slot
                  }
              }
          
              /**
               * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
               */
              function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      r.slot := store.slot
                  }
              }
          
              /**
               * @dev Returns an `BytesSlot` with member `value` located at `slot`.
               */
              function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      r.slot := slot
                  }
              }
          
              /**
               * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
               */
              function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      r.slot := store.slot
                  }
              }
          }
          
          
          // File @openzeppelin/contracts-upgradeable/proxy/ERC1967/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.9.0) (proxy/ERC1967/ERC1967Upgrade.sol)
          
          pragma solidity ^0.8.2;
          
          
          
          
          
          
          /**
           * @dev This abstract contract provides getters and event emitting update functions for
           * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
           *
           * _Available since v4.1._
           */
          abstract contract ERC1967UpgradeUpgradeable is Initializable, IERC1967Upgradeable {
              function __ERC1967Upgrade_init() internal onlyInitializing {
              }
          
              function __ERC1967Upgrade_init_unchained() internal onlyInitializing {
              }
              // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
              bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
          
              /**
               * @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 address.
               */
              function _getImplementation() internal view returns (address) {
                  return StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value;
              }
          
              /**
               * @dev Stores a new address in the EIP1967 implementation slot.
               */
              function _setImplementation(address newImplementation) private {
                  require(AddressUpgradeable.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                  StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
              }
          
              /**
               * @dev Perform implementation upgrade
               *
               * Emits an {Upgraded} event.
               */
              function _upgradeTo(address newImplementation) internal {
                  _setImplementation(newImplementation);
                  emit Upgraded(newImplementation);
              }
          
              /**
               * @dev Perform implementation upgrade with additional setup call.
               *
               * Emits an {Upgraded} event.
               */
              function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {
                  _upgradeTo(newImplementation);
                  if (data.length > 0 || forceCall) {
                      AddressUpgradeable.functionDelegateCall(newImplementation, data);
                  }
              }
          
              /**
               * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
               *
               * Emits an {Upgraded} event.
               */
              function _upgradeToAndCallUUPS(address newImplementation, bytes memory data, bool forceCall) internal {
                  // Upgrades from old implementations will perform a rollback test. This test requires the new
                  // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
                  // this special case will break upgrade paths from old UUPS implementation to new ones.
                  if (StorageSlotUpgradeable.getBooleanSlot(_ROLLBACK_SLOT).value) {
                      _setImplementation(newImplementation);
                  } else {
                      try IERC1822ProxiableUpgradeable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                          require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
                      } catch {
                          revert("ERC1967Upgrade: new implementation is not UUPS");
                      }
                      _upgradeToAndCall(newImplementation, data, forceCall);
                  }
              }
          
              /**
               * @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 Returns the current admin.
               */
              function _getAdmin() internal view returns (address) {
                  return StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value;
              }
          
              /**
               * @dev Stores a new address in the EIP1967 admin slot.
               */
              function _setAdmin(address newAdmin) private {
                  require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                  StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
              }
          
              /**
               * @dev Changes the admin of the proxy.
               *
               * Emits an {AdminChanged} event.
               */
              function _changeAdmin(address newAdmin) internal {
                  emit AdminChanged(_getAdmin(), newAdmin);
                  _setAdmin(newAdmin);
              }
          
              /**
               * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
               * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
               */
              bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
          
              /**
               * @dev Returns the current beacon.
               */
              function _getBeacon() internal view returns (address) {
                  return StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value;
              }
          
              /**
               * @dev Stores a new beacon in the EIP1967 beacon slot.
               */
              function _setBeacon(address newBeacon) private {
                  require(AddressUpgradeable.isContract(newBeacon), "ERC1967: new beacon is not a contract");
                  require(
                      AddressUpgradeable.isContract(IBeaconUpgradeable(newBeacon).implementation()),
                      "ERC1967: beacon implementation is not a contract"
                  );
                  StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value = newBeacon;
              }
          
              /**
               * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
               * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
               *
               * Emits a {BeaconUpgraded} event.
               */
              function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {
                  _setBeacon(newBeacon);
                  emit BeaconUpgraded(newBeacon);
                  if (data.length > 0 || forceCall) {
                      AddressUpgradeable.functionDelegateCall(IBeaconUpgradeable(newBeacon).implementation(), data);
                  }
              }
          
              /**
               * @dev This empty reserved space is put in place to allow future versions to add new
               * variables without shifting down storage in the inheritance chain.
               * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
               */
              uint256[50] private __gap;
          }
          
          
          // File @openzeppelin/contracts-upgradeable/proxy/utils/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/UUPSUpgradeable.sol)
          
          pragma solidity ^0.8.0;
          
          
          
          /**
           * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
           * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
           *
           * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
           * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
           * `UUPSUpgradeable` with a custom implementation of upgrades.
           *
           * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
           *
           * _Available since v4.1._
           */
          abstract contract UUPSUpgradeable is Initializable, IERC1822ProxiableUpgradeable, ERC1967UpgradeUpgradeable {
              function __UUPSUpgradeable_init() internal onlyInitializing {
              }
          
              function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
              }
              /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
              address private immutable __self = address(this);
          
              /**
               * @dev Check that the execution is being performed through a delegatecall call and that the execution context is
               * a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
               * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
               * function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
               * fail.
               */
              modifier onlyProxy() {
                  require(address(this) != __self, "Function must be called through delegatecall");
                  require(_getImplementation() == __self, "Function must be called through active proxy");
                  _;
              }
          
              /**
               * @dev Check that the execution is not being performed through a delegate call. This allows a function to be
               * callable on the implementing contract but not through proxies.
               */
              modifier notDelegated() {
                  require(address(this) == __self, "UUPSUpgradeable: must not be called through delegatecall");
                  _;
              }
          
              /**
               * @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
               * implementation. It is used to validate the implementation's compatibility when performing an upgrade.
               *
               * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
               * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
               * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
               */
              function proxiableUUID() external view virtual override notDelegated returns (bytes32) {
                  return _IMPLEMENTATION_SLOT;
              }
          
              /**
               * @dev Upgrade the implementation of the proxy to `newImplementation`.
               *
               * Calls {_authorizeUpgrade}.
               *
               * Emits an {Upgraded} event.
               *
               * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
               */
              function upgradeTo(address newImplementation) public virtual onlyProxy {
                  _authorizeUpgrade(newImplementation);
                  _upgradeToAndCallUUPS(newImplementation, new bytes(0), false);
              }
          
              /**
               * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
               * encoded in `data`.
               *
               * Calls {_authorizeUpgrade}.
               *
               * Emits an {Upgraded} event.
               *
               * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
               */
              function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
                  _authorizeUpgrade(newImplementation);
                  _upgradeToAndCallUUPS(newImplementation, data, true);
              }
          
              /**
               * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
               * {upgradeTo} and {upgradeToAndCall}.
               *
               * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
               *
               * ```solidity
               * function _authorizeUpgrade(address) internal override onlyOwner {}
               * ```
               */
              function _authorizeUpgrade(address newImplementation) internal virtual;
          
              /**
               * @dev This empty reserved space is put in place to allow future versions to add new
               * variables without shifting down storage in the inheritance chain.
               * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
               */
              uint256[50] private __gap;
          }
          
          
          // File @openzeppelin/contracts-upgradeable/security/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
          
          pragma solidity ^0.8.0;
          
          /**
           * @dev Contract module that helps prevent reentrant calls to a function.
           *
           * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
           * available, which can be applied to functions to make sure there are no nested
           * (reentrant) calls to them.
           *
           * Note that because there is a single `nonReentrant` guard, functions marked as
           * `nonReentrant` may not call one another. This can be worked around by making
           * those functions `private`, and then adding `external` `nonReentrant` entry
           * points to them.
           *
           * TIP: If you would like to learn more about reentrancy and alternative ways
           * to protect against it, check out our blog post
           * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
           */
          abstract contract ReentrancyGuardUpgradeable is Initializable {
              // Booleans are more expensive than uint256 or any type that takes up a full
              // word because each write operation emits an extra SLOAD to first read the
              // slot's contents, replace the bits taken up by the boolean, and then write
              // back. This is the compiler's defense against contract upgrades and
              // pointer aliasing, and it cannot be disabled.
          
              // The values being non-zero value makes deployment a bit more expensive,
              // but in exchange the refund on every call to nonReentrant will be lower in
              // amount. Since refunds are capped to a percentage of the total
              // transaction's gas, it is best to keep them low in cases like this one, to
              // increase the likelihood of the full refund coming into effect.
              uint256 private constant _NOT_ENTERED = 1;
              uint256 private constant _ENTERED = 2;
          
              uint256 private _status;
          
              function __ReentrancyGuard_init() internal onlyInitializing {
                  __ReentrancyGuard_init_unchained();
              }
          
              function __ReentrancyGuard_init_unchained() internal onlyInitializing {
                  _status = _NOT_ENTERED;
              }
          
              /**
               * @dev Prevents a contract from calling itself, directly or indirectly.
               * Calling a `nonReentrant` function from another `nonReentrant`
               * function is not supported. It is possible to prevent this from happening
               * by making the `nonReentrant` function external, and making it call a
               * `private` function that does the actual work.
               */
              modifier nonReentrant() {
                  _nonReentrantBefore();
                  _;
                  _nonReentrantAfter();
              }
          
              function _nonReentrantBefore() private {
                  // On the first call to nonReentrant, _status will be _NOT_ENTERED
                  require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
          
                  // Any calls to nonReentrant after this point will fail
                  _status = _ENTERED;
              }
          
              function _nonReentrantAfter() private {
                  // By storing the original value once again, a refund is triggered (see
                  // https://eips.ethereum.org/EIPS/eip-2200)
                  _status = _NOT_ENTERED;
              }
          
              /**
               * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
               * `nonReentrant` function in the call stack.
               */
              function _reentrancyGuardEntered() internal view returns (bool) {
                  return _status == _ENTERED;
              }
          
              /**
               * @dev This empty reserved space is put in place to allow future versions to add new
               * variables without shifting down storage in the inheritance chain.
               * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
               */
              uint256[49] private __gap;
          }
          
          
          // File @openzeppelin/contracts/token/ERC20/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
          
          pragma solidity ^0.8.0;
          
          /**
           * @dev Interface of the ERC20 standard as defined in the EIP.
           */
          interface IERC20 {
              /**
               * @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);
          
              /**
               * @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 `to`.
               *
               * Returns a boolean value indicating whether the operation succeeded.
               *
               * Emits a {Transfer} event.
               */
              function transfer(address to, 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 `from` to `to` 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 from, address to, uint256 amount) external returns (bool);
          }
          
          
          // File @openzeppelin/contracts/token/ERC20/extensions/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
          
          pragma solidity ^0.8.0;
          
          /**
           * @dev Interface for the optional metadata functions from the ERC20 standard.
           *
           * _Available since v4.1._
           */
          interface IERC20Metadata is IERC20 {
              /**
               * @dev Returns the name of the token.
               */
              function name() external view returns (string memory);
          
              /**
               * @dev Returns the symbol of the token.
               */
              function symbol() external view returns (string memory);
          
              /**
               * @dev Returns the decimals places of the token.
               */
              function decimals() external view returns (uint8);
          }
          
          
          // File @openzeppelin/contracts/utils/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
          
          pragma solidity ^0.8.0;
          
          /**
           * @dev Provides information about the current execution context, including the
           * sender of the transaction and its data. While these are generally available
           * via msg.sender and msg.data, they should not be accessed in such a direct
           * manner, since when dealing with meta-transactions the account sending and
           * paying for execution may not be the actual sender (as far as an application
           * is concerned).
           *
           * This contract is only required for intermediate, library-like contracts.
           */
          abstract contract Context {
              function _msgSender() internal view virtual returns (address) {
                  return msg.sender;
              }
          
              function _msgData() internal view virtual returns (bytes calldata) {
                  return msg.data;
              }
          }
          
          
          // File @openzeppelin/contracts/token/ERC20/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)
          
          pragma solidity ^0.8.0;
          
          
          
          /**
           * @dev Implementation of the {IERC20} interface.
           *
           * This implementation is agnostic to the way tokens are created. This means
           * that a supply mechanism has to be added in a derived contract using {_mint}.
           * For a generic mechanism see {ERC20PresetMinterPauser}.
           *
           * TIP: For a detailed writeup see our guide
           * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
           * to implement supply mechanisms].
           *
           * The default value of {decimals} is 18. To change this, you should override
           * this function so it returns a different value.
           *
           * We have followed general OpenZeppelin Contracts guidelines: functions revert
           * instead returning `false` on failure. This behavior is nonetheless
           * conventional and does not conflict with the expectations of ERC20
           * applications.
           *
           * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
           * This allows applications to reconstruct the allowance for all accounts just
           * by listening to said events. Other implementations of the EIP may not emit
           * these events, as it isn't required by the specification.
           *
           * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
           * functions have been added to mitigate the well-known issues around setting
           * allowances. See {IERC20-approve}.
           */
          contract ERC20 is Context, IERC20, IERC20Metadata {
              mapping(address => uint256) private _balances;
          
              mapping(address => mapping(address => uint256)) private _allowances;
          
              uint256 private _totalSupply;
          
              string private _name;
              string private _symbol;
          
              /**
               * @dev Sets the values for {name} and {symbol}.
               *
               * All two of these values are immutable: they can only be set once during
               * construction.
               */
              constructor(string memory name_, string memory symbol_) {
                  _name = name_;
                  _symbol = symbol_;
              }
          
              /**
               * @dev Returns the name of the token.
               */
              function name() public view virtual override returns (string memory) {
                  return _name;
              }
          
              /**
               * @dev Returns the symbol of the token, usually a shorter version of the
               * name.
               */
              function symbol() public view virtual override returns (string memory) {
                  return _symbol;
              }
          
              /**
               * @dev Returns the number of decimals used to get its user representation.
               * For example, if `decimals` equals `2`, a balance of `505` tokens should
               * be displayed to a user as `5.05` (`505 / 10 ** 2`).
               *
               * Tokens usually opt for a value of 18, imitating the relationship between
               * Ether and Wei. This is the default value returned by this function, unless
               * it's overridden.
               *
               * NOTE: This information is only used for _display_ purposes: it in
               * no way affects any of the arithmetic of the contract, including
               * {IERC20-balanceOf} and {IERC20-transfer}.
               */
              function decimals() public view virtual override returns (uint8) {
                  return 18;
              }
          
              /**
               * @dev See {IERC20-totalSupply}.
               */
              function totalSupply() public view virtual override returns (uint256) {
                  return _totalSupply;
              }
          
              /**
               * @dev See {IERC20-balanceOf}.
               */
              function balanceOf(address account) public view virtual override returns (uint256) {
                  return _balances[account];
              }
          
              /**
               * @dev See {IERC20-transfer}.
               *
               * Requirements:
               *
               * - `to` cannot be the zero address.
               * - the caller must have a balance of at least `amount`.
               */
              function transfer(address to, uint256 amount) public virtual override returns (bool) {
                  address owner = _msgSender();
                  _transfer(owner, to, amount);
                  return true;
              }
          
              /**
               * @dev See {IERC20-allowance}.
               */
              function allowance(address owner, address spender) public view virtual override returns (uint256) {
                  return _allowances[owner][spender];
              }
          
              /**
               * @dev See {IERC20-approve}.
               *
               * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
               * `transferFrom`. This is semantically equivalent to an infinite approval.
               *
               * Requirements:
               *
               * - `spender` cannot be the zero address.
               */
              function approve(address spender, uint256 amount) public virtual override returns (bool) {
                  address owner = _msgSender();
                  _approve(owner, spender, amount);
                  return true;
              }
          
              /**
               * @dev See {IERC20-transferFrom}.
               *
               * Emits an {Approval} event indicating the updated allowance. This is not
               * required by the EIP. See the note at the beginning of {ERC20}.
               *
               * NOTE: Does not update the allowance if the current allowance
               * is the maximum `uint256`.
               *
               * Requirements:
               *
               * - `from` and `to` cannot be the zero address.
               * - `from` must have a balance of at least `amount`.
               * - the caller must have allowance for ``from``'s tokens of at least
               * `amount`.
               */
              function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
                  address spender = _msgSender();
                  _spendAllowance(from, spender, amount);
                  _transfer(from, to, amount);
                  return true;
              }
          
              /**
               * @dev Atomically increases the allowance granted to `spender` by the caller.
               *
               * This is an alternative to {approve} that can be used as a mitigation for
               * problems described in {IERC20-approve}.
               *
               * Emits an {Approval} event indicating the updated allowance.
               *
               * Requirements:
               *
               * - `spender` cannot be the zero address.
               */
              function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
                  address owner = _msgSender();
                  _approve(owner, spender, allowance(owner, spender) + addedValue);
                  return true;
              }
          
              /**
               * @dev Atomically decreases the allowance granted to `spender` by the caller.
               *
               * This is an alternative to {approve} that can be used as a mitigation for
               * problems described in {IERC20-approve}.
               *
               * Emits an {Approval} event indicating the updated allowance.
               *
               * Requirements:
               *
               * - `spender` cannot be the zero address.
               * - `spender` must have allowance for the caller of at least
               * `subtractedValue`.
               */
              function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
                  address owner = _msgSender();
                  uint256 currentAllowance = allowance(owner, spender);
                  require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
                  unchecked {
                      _approve(owner, spender, currentAllowance - subtractedValue);
                  }
          
                  return true;
              }
          
              /**
               * @dev Moves `amount` of tokens from `from` to `to`.
               *
               * This internal function is equivalent to {transfer}, and can be used to
               * e.g. implement automatic token fees, slashing mechanisms, etc.
               *
               * Emits a {Transfer} event.
               *
               * Requirements:
               *
               * - `from` cannot be the zero address.
               * - `to` cannot be the zero address.
               * - `from` must have a balance of at least `amount`.
               */
              function _transfer(address from, address to, uint256 amount) internal virtual {
                  require(from != address(0), "ERC20: transfer from the zero address");
                  require(to != address(0), "ERC20: transfer to the zero address");
          
                  _beforeTokenTransfer(from, to, amount);
          
                  uint256 fromBalance = _balances[from];
                  require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
                  unchecked {
                      _balances[from] = fromBalance - amount;
                      // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
                      // decrementing then incrementing.
                      _balances[to] += amount;
                  }
          
                  emit Transfer(from, to, amount);
          
                  _afterTokenTransfer(from, to, amount);
              }
          
              /** @dev Creates `amount` tokens and assigns them to `account`, increasing
               * the total supply.
               *
               * Emits a {Transfer} event with `from` set to the zero address.
               *
               * Requirements:
               *
               * - `account` cannot be the zero address.
               */
              function _mint(address account, uint256 amount) internal virtual {
                  require(account != address(0), "ERC20: mint to the zero address");
          
                  _beforeTokenTransfer(address(0), account, amount);
          
                  _totalSupply += amount;
                  unchecked {
                      // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
                      _balances[account] += amount;
                  }
                  emit Transfer(address(0), account, amount);
          
                  _afterTokenTransfer(address(0), account, amount);
              }
          
              /**
               * @dev Destroys `amount` tokens from `account`, reducing the
               * total supply.
               *
               * Emits a {Transfer} event with `to` set to the zero address.
               *
               * Requirements:
               *
               * - `account` cannot be the zero address.
               * - `account` must have at least `amount` tokens.
               */
              function _burn(address account, uint256 amount) internal virtual {
                  require(account != address(0), "ERC20: burn from the zero address");
          
                  _beforeTokenTransfer(account, address(0), amount);
          
                  uint256 accountBalance = _balances[account];
                  require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
                  unchecked {
                      _balances[account] = accountBalance - amount;
                      // Overflow not possible: amount <= accountBalance <= totalSupply.
                      _totalSupply -= amount;
                  }
          
                  emit Transfer(account, address(0), amount);
          
                  _afterTokenTransfer(account, address(0), amount);
              }
          
              /**
               * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
               *
               * This internal function is equivalent to `approve`, and can be used to
               * e.g. set automatic allowances for certain subsystems, etc.
               *
               * Emits an {Approval} event.
               *
               * Requirements:
               *
               * - `owner` cannot be the zero address.
               * - `spender` cannot be the zero address.
               */
              function _approve(address owner, address spender, uint256 amount) internal virtual {
                  require(owner != address(0), "ERC20: approve from the zero address");
                  require(spender != address(0), "ERC20: approve to the zero address");
          
                  _allowances[owner][spender] = amount;
                  emit Approval(owner, spender, amount);
              }
          
              /**
               * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
               *
               * Does not update the allowance amount in case of infinite allowance.
               * Revert if not enough allowance is available.
               *
               * Might emit an {Approval} event.
               */
              function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
                  uint256 currentAllowance = allowance(owner, spender);
                  if (currentAllowance != type(uint256).max) {
                      require(currentAllowance >= amount, "ERC20: insufficient allowance");
                      unchecked {
                          _approve(owner, spender, currentAllowance - amount);
                      }
                  }
              }
          
              /**
               * @dev Hook that is called before any transfer of tokens. This includes
               * minting and burning.
               *
               * Calling conditions:
               *
               * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
               * will be transferred to `to`.
               * - when `from` is zero, `amount` tokens will be minted for `to`.
               * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
               * - `from` and `to` are never both zero.
               *
               * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
               */
              function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
          
              /**
               * @dev Hook that is called after any transfer of tokens. This includes
               * minting and burning.
               *
               * Calling conditions:
               *
               * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
               * has been transferred to `to`.
               * - when `from` is zero, `amount` tokens have been minted for `to`.
               * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
               * - `from` and `to` are never both zero.
               *
               * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
               */
              function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
          }
          
          
          // File @openzeppelin/contracts/token/ERC20/extensions/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Burnable.sol)
          
          pragma solidity ^0.8.0;
          
          
          /**
           * @dev Extension of {ERC20} that allows token holders to destroy both their own
           * tokens and those that they have an allowance for, in a way that can be
           * recognized off-chain (via event analysis).
           */
          abstract contract ERC20Burnable is Context, ERC20 {
              /**
               * @dev Destroys `amount` tokens from the caller.
               *
               * See {ERC20-_burn}.
               */
              function burn(uint256 amount) public virtual {
                  _burn(_msgSender(), amount);
              }
          
              /**
               * @dev Destroys `amount` tokens from `account`, deducting from the caller's
               * allowance.
               *
               * See {ERC20-_burn} and {ERC20-allowance}.
               *
               * Requirements:
               *
               * - the caller must have allowance for ``accounts``'s tokens of at least
               * `amount`.
               */
              function burnFrom(address account, uint256 amount) public virtual {
                  _spendAllowance(account, _msgSender(), amount);
                  _burn(account, amount);
              }
          }
          
          
          // File @openzeppelin/contracts/utils/introspection/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
          
          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);
          }
          
          
          // File @openzeppelin/contracts/token/ERC721/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)
          
          pragma solidity ^0.8.0;
          
          /**
           * @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`.
               *
               * 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;
          
              /**
               * @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 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: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
               * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
               * understand this adds an external call which potentially creates a reentrancy vulnerability.
               *
               * 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 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 the account approved for `tokenId` token.
               *
               * Requirements:
               *
               * - `tokenId` must exist.
               */
              function getApproved(uint256 tokenId) external view returns (address operator);
          
              /**
               * @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);
          }
          
          
          // File @openzeppelin/contracts/token/ERC721/extensions/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)
          
          pragma solidity ^0.8.0;
          
          /**
           * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
           * @dev See https://eips.ethereum.org/EIPS/eip-721
           */
          interface IERC721Metadata is IERC721 {
              /**
               * @dev Returns the token collection name.
               */
              function name() external view returns (string memory);
          
              /**
               * @dev Returns the token collection symbol.
               */
              function symbol() external view returns (string memory);
          
              /**
               * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
               */
              function tokenURI(uint256 tokenId) external view returns (string memory);
          }
          
          
          // File @openzeppelin/contracts/token/ERC721/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)
          
          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 `IERC721Receiver.onERC721Received.selector`.
               */
              function onERC721Received(
                  address operator,
                  address from,
                  uint256 tokenId,
                  bytes calldata data
              ) external returns (bytes4);
          }
          
          
          // File @openzeppelin/contracts/utils/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
          
          pragma solidity ^0.8.1;
          
          /**
           * @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
               *
               * Furthermore, `isContract` will also return true if the target contract within
               * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
               * which only has an effect at the end of a transaction.
               * ====
               *
               * [IMPORTANT]
               * ====
               * You shouldn't rely on `isContract` to protect against flash loan attacks!
               *
               * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
               * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
               * constructor.
               * ====
               */
              function isContract(address account) internal view returns (bool) {
                  // This method relies on extcodesize/address.code.length, which returns 0
                  // for contracts in construction, since the code is only stored at the end
                  // of the constructor execution.
          
                  return account.code.length > 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://consensys.net/diligence/blog/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.8.0/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");
          
                  (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 functionCallWithValue(target, data, 0, "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");
                  (bool success, bytes memory returndata) = target.call{value: value}(data);
                  return verifyCallResultFromTarget(target, 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) {
                  (bool success, bytes memory returndata) = target.staticcall(data);
                  return verifyCallResultFromTarget(target, 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) {
                  (bool success, bytes memory returndata) = target.delegatecall(data);
                  return verifyCallResultFromTarget(target, success, returndata, errorMessage);
              }
          
              /**
               * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
               * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
               *
               * _Available since v4.8._
               */
              function verifyCallResultFromTarget(
                  address target,
                  bool success,
                  bytes memory returndata,
                  string memory errorMessage
              ) internal view returns (bytes memory) {
                  if (success) {
                      if (returndata.length == 0) {
                          // only check isContract if the call was successful and the return data is empty
                          // otherwise we already know that it was a contract
                          require(isContract(target), "Address: call to non-contract");
                      }
                      return returndata;
                  } else {
                      _revert(returndata, errorMessage);
                  }
              }
          
              /**
               * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
               * revert reason or using the provided one.
               *
               * _Available since v4.3._
               */
              function verifyCallResult(
                  bool success,
                  bytes memory returndata,
                  string memory errorMessage
              ) internal pure returns (bytes memory) {
                  if (success) {
                      return returndata;
                  } else {
                      _revert(returndata, errorMessage);
                  }
              }
          
              function _revert(bytes memory returndata, string memory errorMessage) private pure {
                  // 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
                      /// @solidity memory-safe-assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
          
          
          // File @openzeppelin/contracts/utils/introspection/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
          
          pragma solidity ^0.8.0;
          
          /**
           * @dev Implementation of the {IERC165} interface.
           *
           * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
           * for the additional interface id that will be supported. For example:
           *
           * ```solidity
           * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
           *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
           * }
           * ```
           *
           * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
           */
          abstract contract ERC165 is IERC165 {
              /**
               * @dev See {IERC165-supportsInterface}.
               */
              function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                  return interfaceId == type(IERC165).interfaceId;
              }
          }
          
          
          // File @openzeppelin/contracts/utils/math/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
          
          pragma solidity ^0.8.0;
          
          /**
           * @dev Standard math utilities missing in the Solidity language.
           */
          library Math {
              enum Rounding {
                  Down, // Toward negative infinity
                  Up, // Toward infinity
                  Zero // Toward zero
              }
          
              /**
               * @dev Returns the largest of two numbers.
               */
              function max(uint256 a, uint256 b) internal pure returns (uint256) {
                  return a > b ? a : b;
              }
          
              /**
               * @dev Returns the smallest of two numbers.
               */
              function min(uint256 a, uint256 b) internal pure returns (uint256) {
                  return a < b ? a : b;
              }
          
              /**
               * @dev Returns the average of two numbers. The result is rounded towards
               * zero.
               */
              function average(uint256 a, uint256 b) internal pure returns (uint256) {
                  // (a + b) / 2 can overflow.
                  return (a & b) + (a ^ b) / 2;
              }
          
              /**
               * @dev Returns the ceiling of the division of two numbers.
               *
               * This differs from standard division with `/` in that it rounds up instead
               * of rounding down.
               */
              function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
                  // (a + b - 1) / b can overflow on addition, so we distribute.
                  return a == 0 ? 0 : (a - 1) / b + 1;
              }
          
              /**
               * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
               * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
               * with further edits by Uniswap Labs also under MIT license.
               */
              function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
                  unchecked {
                      // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                      // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                      // variables such that product = prod1 * 2^256 + prod0.
                      uint256 prod0; // Least significant 256 bits of the product
                      uint256 prod1; // Most significant 256 bits of the product
                      assembly {
                          let mm := mulmod(x, y, not(0))
                          prod0 := mul(x, y)
                          prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                      }
          
                      // Handle non-overflow cases, 256 by 256 division.
                      if (prod1 == 0) {
                          // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                          // The surrounding unchecked block does not change this fact.
                          // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                          return prod0 / denominator;
                      }
          
                      // Make sure the result is less than 2^256. Also prevents denominator == 0.
                      require(denominator > prod1, "Math: mulDiv overflow");
          
                      ///////////////////////////////////////////////
                      // 512 by 256 division.
                      ///////////////////////////////////////////////
          
                      // Make division exact by subtracting the remainder from [prod1 prod0].
                      uint256 remainder;
                      assembly {
                          // Compute remainder using mulmod.
                          remainder := mulmod(x, y, denominator)
          
                          // Subtract 256 bit number from 512 bit number.
                          prod1 := sub(prod1, gt(remainder, prod0))
                          prod0 := sub(prod0, remainder)
                      }
          
                      // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
                      // See https://cs.stackexchange.com/q/138556/92363.
          
                      // Does not overflow because the denominator cannot be zero at this stage in the function.
                      uint256 twos = denominator & (~denominator + 1);
                      assembly {
                          // Divide denominator by twos.
                          denominator := div(denominator, twos)
          
                          // Divide [prod1 prod0] by twos.
                          prod0 := div(prod0, twos)
          
                          // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                          twos := add(div(sub(0, twos), twos), 1)
                      }
          
                      // Shift in bits from prod1 into prod0.
                      prod0 |= prod1 * twos;
          
                      // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                      // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                      // four bits. That is, denominator * inv = 1 mod 2^4.
                      uint256 inverse = (3 * denominator) ^ 2;
          
                      // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                      // in modular arithmetic, doubling the correct bits in each step.
                      inverse *= 2 - denominator * inverse; // inverse mod 2^8
                      inverse *= 2 - denominator * inverse; // inverse mod 2^16
                      inverse *= 2 - denominator * inverse; // inverse mod 2^32
                      inverse *= 2 - denominator * inverse; // inverse mod 2^64
                      inverse *= 2 - denominator * inverse; // inverse mod 2^128
                      inverse *= 2 - denominator * inverse; // inverse mod 2^256
          
                      // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                      // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                      // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                      // is no longer required.
                      result = prod0 * inverse;
                      return result;
                  }
              }
          
              /**
               * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
               */
              function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
                  uint256 result = mulDiv(x, y, denominator);
                  if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
                      result += 1;
                  }
                  return result;
              }
          
              /**
               * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
               *
               * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
               */
              function sqrt(uint256 a) internal pure returns (uint256) {
                  if (a == 0) {
                      return 0;
                  }
          
                  // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
                  //
                  // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
                  // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
                  //
                  // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
                  // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
                  // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
                  //
                  // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
                  uint256 result = 1 << (log2(a) >> 1);
          
                  // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
                  // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
                  // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
                  // into the expected uint128 result.
                  unchecked {
                      result = (result + a / result) >> 1;
                      result = (result + a / result) >> 1;
                      result = (result + a / result) >> 1;
                      result = (result + a / result) >> 1;
                      result = (result + a / result) >> 1;
                      result = (result + a / result) >> 1;
                      result = (result + a / result) >> 1;
                      return min(result, a / result);
                  }
              }
          
              /**
               * @notice Calculates sqrt(a), following the selected rounding direction.
               */
              function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
                  unchecked {
                      uint256 result = sqrt(a);
                      return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
                  }
              }
          
              /**
               * @dev Return the log in base 2, rounded down, of a positive value.
               * Returns 0 if given 0.
               */
              function log2(uint256 value) internal pure returns (uint256) {
                  uint256 result = 0;
                  unchecked {
                      if (value >> 128 > 0) {
                          value >>= 128;
                          result += 128;
                      }
                      if (value >> 64 > 0) {
                          value >>= 64;
                          result += 64;
                      }
                      if (value >> 32 > 0) {
                          value >>= 32;
                          result += 32;
                      }
                      if (value >> 16 > 0) {
                          value >>= 16;
                          result += 16;
                      }
                      if (value >> 8 > 0) {
                          value >>= 8;
                          result += 8;
                      }
                      if (value >> 4 > 0) {
                          value >>= 4;
                          result += 4;
                      }
                      if (value >> 2 > 0) {
                          value >>= 2;
                          result += 2;
                      }
                      if (value >> 1 > 0) {
                          result += 1;
                      }
                  }
                  return result;
              }
          
              /**
               * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
               * Returns 0 if given 0.
               */
              function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
                  unchecked {
                      uint256 result = log2(value);
                      return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
                  }
              }
          
              /**
               * @dev Return the log in base 10, rounded down, of a positive value.
               * Returns 0 if given 0.
               */
              function log10(uint256 value) internal pure returns (uint256) {
                  uint256 result = 0;
                  unchecked {
                      if (value >= 10 ** 64) {
                          value /= 10 ** 64;
                          result += 64;
                      }
                      if (value >= 10 ** 32) {
                          value /= 10 ** 32;
                          result += 32;
                      }
                      if (value >= 10 ** 16) {
                          value /= 10 ** 16;
                          result += 16;
                      }
                      if (value >= 10 ** 8) {
                          value /= 10 ** 8;
                          result += 8;
                      }
                      if (value >= 10 ** 4) {
                          value /= 10 ** 4;
                          result += 4;
                      }
                      if (value >= 10 ** 2) {
                          value /= 10 ** 2;
                          result += 2;
                      }
                      if (value >= 10 ** 1) {
                          result += 1;
                      }
                  }
                  return result;
              }
          
              /**
               * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
               * Returns 0 if given 0.
               */
              function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
                  unchecked {
                      uint256 result = log10(value);
                      return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
                  }
              }
          
              /**
               * @dev Return the log in base 256, rounded down, of a positive value.
               * Returns 0 if given 0.
               *
               * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
               */
              function log256(uint256 value) internal pure returns (uint256) {
                  uint256 result = 0;
                  unchecked {
                      if (value >> 128 > 0) {
                          value >>= 128;
                          result += 16;
                      }
                      if (value >> 64 > 0) {
                          value >>= 64;
                          result += 8;
                      }
                      if (value >> 32 > 0) {
                          value >>= 32;
                          result += 4;
                      }
                      if (value >> 16 > 0) {
                          value >>= 16;
                          result += 2;
                      }
                      if (value >> 8 > 0) {
                          result += 1;
                      }
                  }
                  return result;
              }
          
              /**
               * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
               * Returns 0 if given 0.
               */
              function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
                  unchecked {
                      uint256 result = log256(value);
                      return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
                  }
              }
          }
          
          
          // File @openzeppelin/contracts/utils/math/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
          
          pragma solidity ^0.8.0;
          
          /**
           * @dev Standard signed math utilities missing in the Solidity language.
           */
          library SignedMath {
              /**
               * @dev Returns the largest of two signed numbers.
               */
              function max(int256 a, int256 b) internal pure returns (int256) {
                  return a > b ? a : b;
              }
          
              /**
               * @dev Returns the smallest of two signed numbers.
               */
              function min(int256 a, int256 b) internal pure returns (int256) {
                  return a < b ? a : b;
              }
          
              /**
               * @dev Returns the average of two signed numbers without overflow.
               * The result is rounded towards zero.
               */
              function average(int256 a, int256 b) internal pure returns (int256) {
                  // Formula from the book "Hacker's Delight"
                  int256 x = (a & b) + ((a ^ b) >> 1);
                  return x + (int256(uint256(x) >> 255) & (a ^ b));
              }
          
              /**
               * @dev Returns the absolute unsigned value of a signed value.
               */
              function abs(int256 n) internal pure returns (uint256) {
                  unchecked {
                      // must be unchecked in order to support `n = type(int256).min`
                      return uint256(n >= 0 ? n : -n);
                  }
              }
          }
          
          
          // File @openzeppelin/contracts/utils/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
          
          pragma solidity ^0.8.0;
          
          
          /**
           * @dev String operations.
           */
          library Strings {
              bytes16 private constant _SYMBOLS = "0123456789abcdef";
              uint8 private constant _ADDRESS_LENGTH = 20;
          
              /**
               * @dev Converts a `uint256` to its ASCII `string` decimal representation.
               */
              function toString(uint256 value) internal pure returns (string memory) {
                  unchecked {
                      uint256 length = Math.log10(value) + 1;
                      string memory buffer = new string(length);
                      uint256 ptr;
                      /// @solidity memory-safe-assembly
                      assembly {
                          ptr := add(buffer, add(32, length))
                      }
                      while (true) {
                          ptr--;
                          /// @solidity memory-safe-assembly
                          assembly {
                              mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                          }
                          value /= 10;
                          if (value == 0) break;
                      }
                      return buffer;
                  }
              }
          
              /**
               * @dev Converts a `int256` to its ASCII `string` decimal representation.
               */
              function toString(int256 value) internal pure returns (string memory) {
                  return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
              }
          
              /**
               * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
               */
              function toHexString(uint256 value) internal pure returns (string memory) {
                  unchecked {
                      return toHexString(value, Math.log256(value) + 1);
                  }
              }
          
              /**
               * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
               */
              function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                  bytes memory buffer = new bytes(2 * length + 2);
                  buffer[0] = "0";
                  buffer[1] = "x";
                  for (uint256 i = 2 * length + 1; i > 1; --i) {
                      buffer[i] = _SYMBOLS[value & 0xf];
                      value >>= 4;
                  }
                  require(value == 0, "Strings: hex length insufficient");
                  return string(buffer);
              }
          
              /**
               * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
               */
              function toHexString(address addr) internal pure returns (string memory) {
                  return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
              }
          
              /**
               * @dev Returns true if the two strings are equal.
               */
              function equal(string memory a, string memory b) internal pure returns (bool) {
                  return keccak256(bytes(a)) == keccak256(bytes(b));
              }
          }
          
          
          // File @openzeppelin/contracts/token/ERC721/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/ERC721.sol)
          
          pragma solidity ^0.8.0;
          
          
          
          
          
          
          
          /**
           * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
           * the Metadata extension, but not including the Enumerable extension, which is available separately as
           * {ERC721Enumerable}.
           */
          contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
              using Address for address;
              using Strings for uint256;
          
              // Token name
              string private _name;
          
              // Token symbol
              string private _symbol;
          
              // Mapping from token ID to owner address
              mapping(uint256 => address) private _owners;
          
              // Mapping owner address to token count
              mapping(address => uint256) private _balances;
          
              // Mapping from token ID to approved address
              mapping(uint256 => address) private _tokenApprovals;
          
              // Mapping from owner to operator approvals
              mapping(address => mapping(address => bool)) private _operatorApprovals;
          
              /**
               * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
               */
              constructor(string memory name_, string memory symbol_) {
                  _name = name_;
                  _symbol = symbol_;
              }
          
              /**
               * @dev See {IERC165-supportsInterface}.
               */
              function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
                  return
                      interfaceId == type(IERC721).interfaceId ||
                      interfaceId == type(IERC721Metadata).interfaceId ||
                      super.supportsInterface(interfaceId);
              }
          
              /**
               * @dev See {IERC721-balanceOf}.
               */
              function balanceOf(address owner) public view virtual override returns (uint256) {
                  require(owner != address(0), "ERC721: address zero is not a valid owner");
                  return _balances[owner];
              }
          
              /**
               * @dev See {IERC721-ownerOf}.
               */
              function ownerOf(uint256 tokenId) public view virtual override returns (address) {
                  address owner = _ownerOf(tokenId);
                  require(owner != address(0), "ERC721: invalid token ID");
                  return owner;
              }
          
              /**
               * @dev See {IERC721Metadata-name}.
               */
              function name() public view virtual override returns (string memory) {
                  return _name;
              }
          
              /**
               * @dev See {IERC721Metadata-symbol}.
               */
              function symbol() public view virtual override returns (string memory) {
                  return _symbol;
              }
          
              /**
               * @dev See {IERC721Metadata-tokenURI}.
               */
              function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
                  _requireMinted(tokenId);
          
                  string memory baseURI = _baseURI();
                  return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
              }
          
              /**
               * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
               * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
               * by default, can be overridden in child contracts.
               */
              function _baseURI() internal view virtual returns (string memory) {
                  return "";
              }
          
              /**
               * @dev See {IERC721-approve}.
               */
              function approve(address to, uint256 tokenId) public virtual override {
                  address owner = ERC721.ownerOf(tokenId);
                  require(to != owner, "ERC721: approval to current owner");
          
                  require(
                      _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
                      "ERC721: approve caller is not token owner or approved for all"
                  );
          
                  _approve(to, tokenId);
              }
          
              /**
               * @dev See {IERC721-getApproved}.
               */
              function getApproved(uint256 tokenId) public view virtual override returns (address) {
                  _requireMinted(tokenId);
          
                  return _tokenApprovals[tokenId];
              }
          
              /**
               * @dev See {IERC721-setApprovalForAll}.
               */
              function setApprovalForAll(address operator, bool approved) public virtual override {
                  _setApprovalForAll(_msgSender(), operator, approved);
              }
          
              /**
               * @dev See {IERC721-isApprovedForAll}.
               */
              function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
                  return _operatorApprovals[owner][operator];
              }
          
              /**
               * @dev See {IERC721-transferFrom}.
               */
              function transferFrom(address from, address to, uint256 tokenId) public virtual override {
                  //solhint-disable-next-line max-line-length
                  require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
          
                  _transfer(from, to, tokenId);
              }
          
              /**
               * @dev See {IERC721-safeTransferFrom}.
               */
              function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
                  safeTransferFrom(from, to, tokenId, "");
              }
          
              /**
               * @dev See {IERC721-safeTransferFrom}.
               */
              function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual override {
                  require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
                  _safeTransfer(from, to, tokenId, data);
              }
          
              /**
               * @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.
               *
               * `data` is additional data, it has no specified format and it is sent in call to `to`.
               *
               * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
               * implement alternative mechanisms to perform token transfer, such as signature-based.
               *
               * Requirements:
               *
               * - `from` cannot be the zero address.
               * - `to` cannot be the zero address.
               * - `tokenId` token must exist and be owned by `from`.
               * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
               *
               * Emits a {Transfer} event.
               */
              function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {
                  _transfer(from, to, tokenId);
                  require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer");
              }
          
              /**
               * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
               */
              function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
                  return _owners[tokenId];
              }
          
              /**
               * @dev Returns whether `tokenId` exists.
               *
               * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
               *
               * Tokens start existing when they are minted (`_mint`),
               * and stop existing when they are burned (`_burn`).
               */
              function _exists(uint256 tokenId) internal view virtual returns (bool) {
                  return _ownerOf(tokenId) != address(0);
              }
          
              /**
               * @dev Returns whether `spender` is allowed to manage `tokenId`.
               *
               * Requirements:
               *
               * - `tokenId` must exist.
               */
              function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
                  address owner = ERC721.ownerOf(tokenId);
                  return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
              }
          
              /**
               * @dev Safely mints `tokenId` and transfers it to `to`.
               *
               * Requirements:
               *
               * - `tokenId` must not exist.
               * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
               *
               * Emits a {Transfer} event.
               */
              function _safeMint(address to, uint256 tokenId) internal virtual {
                  _safeMint(to, tokenId, "");
              }
          
              /**
               * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
               * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
               */
              function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
                  _mint(to, tokenId);
                  require(
                      _checkOnERC721Received(address(0), to, tokenId, data),
                      "ERC721: transfer to non ERC721Receiver implementer"
                  );
              }
          
              /**
               * @dev Mints `tokenId` and transfers it to `to`.
               *
               * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
               *
               * Requirements:
               *
               * - `tokenId` must not exist.
               * - `to` cannot be the zero address.
               *
               * Emits a {Transfer} event.
               */
              function _mint(address to, uint256 tokenId) internal virtual {
                  require(to != address(0), "ERC721: mint to the zero address");
                  require(!_exists(tokenId), "ERC721: token already minted");
          
                  _beforeTokenTransfer(address(0), to, tokenId, 1);
          
                  // Check that tokenId was not minted by `_beforeTokenTransfer` hook
                  require(!_exists(tokenId), "ERC721: token already minted");
          
                  unchecked {
                      // Will not overflow unless all 2**256 token ids are minted to the same owner.
                      // Given that tokens are minted one by one, it is impossible in practice that
                      // this ever happens. Might change if we allow batch minting.
                      // The ERC fails to describe this case.
                      _balances[to] += 1;
                  }
          
                  _owners[tokenId] = to;
          
                  emit Transfer(address(0), to, tokenId);
          
                  _afterTokenTransfer(address(0), to, tokenId, 1);
              }
          
              /**
               * @dev Destroys `tokenId`.
               * The approval is cleared when the token is burned.
               * This is an internal function that does not check if the sender is authorized to operate on the token.
               *
               * Requirements:
               *
               * - `tokenId` must exist.
               *
               * Emits a {Transfer} event.
               */
              function _burn(uint256 tokenId) internal virtual {
                  address owner = ERC721.ownerOf(tokenId);
          
                  _beforeTokenTransfer(owner, address(0), tokenId, 1);
          
                  // Update ownership in case tokenId was transferred by `_beforeTokenTransfer` hook
                  owner = ERC721.ownerOf(tokenId);
          
                  // Clear approvals
                  delete _tokenApprovals[tokenId];
          
                  unchecked {
                      // Cannot overflow, as that would require more tokens to be burned/transferred
                      // out than the owner initially received through minting and transferring in.
                      _balances[owner] -= 1;
                  }
                  delete _owners[tokenId];
          
                  emit Transfer(owner, address(0), tokenId);
          
                  _afterTokenTransfer(owner, address(0), tokenId, 1);
              }
          
              /**
               * @dev Transfers `tokenId` from `from` to `to`.
               *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
               *
               * Requirements:
               *
               * - `to` cannot be the zero address.
               * - `tokenId` token must be owned by `from`.
               *
               * Emits a {Transfer} event.
               */
              function _transfer(address from, address to, uint256 tokenId) internal virtual {
                  require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
                  require(to != address(0), "ERC721: transfer to the zero address");
          
                  _beforeTokenTransfer(from, to, tokenId, 1);
          
                  // Check that tokenId was not transferred by `_beforeTokenTransfer` hook
                  require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
          
                  // Clear approvals from the previous owner
                  delete _tokenApprovals[tokenId];
          
                  unchecked {
                      // `_balances[from]` cannot overflow for the same reason as described in `_burn`:
                      // `from`'s balance is the number of token held, which is at least one before the current
                      // transfer.
                      // `_balances[to]` could overflow in the conditions described in `_mint`. That would require
                      // all 2**256 token ids to be minted, which in practice is impossible.
                      _balances[from] -= 1;
                      _balances[to] += 1;
                  }
                  _owners[tokenId] = to;
          
                  emit Transfer(from, to, tokenId);
          
                  _afterTokenTransfer(from, to, tokenId, 1);
              }
          
              /**
               * @dev Approve `to` to operate on `tokenId`
               *
               * Emits an {Approval} event.
               */
              function _approve(address to, uint256 tokenId) internal virtual {
                  _tokenApprovals[tokenId] = to;
                  emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
              }
          
              /**
               * @dev Approve `operator` to operate on all of `owner` tokens
               *
               * Emits an {ApprovalForAll} event.
               */
              function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
                  require(owner != operator, "ERC721: approve to caller");
                  _operatorApprovals[owner][operator] = approved;
                  emit ApprovalForAll(owner, operator, approved);
              }
          
              /**
               * @dev Reverts if the `tokenId` has not been minted yet.
               */
              function _requireMinted(uint256 tokenId) internal view virtual {
                  require(_exists(tokenId), "ERC721: invalid token ID");
              }
          
              /**
               * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
               * The call is not executed if the target address is not a contract.
               *
               * @param from address representing the previous owner of the given token ID
               * @param to target address that will receive the tokens
               * @param tokenId uint256 ID of the token to be transferred
               * @param data bytes optional data to send along with the call
               * @return bool whether the call correctly returned the expected magic value
               */
              function _checkOnERC721Received(
                  address from,
                  address to,
                  uint256 tokenId,
                  bytes memory data
              ) private returns (bool) {
                  if (to.isContract()) {
                      try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
                          return retval == IERC721Receiver.onERC721Received.selector;
                      } catch (bytes memory reason) {
                          if (reason.length == 0) {
                              revert("ERC721: transfer to non ERC721Receiver implementer");
                          } else {
                              /// @solidity memory-safe-assembly
                              assembly {
                                  revert(add(32, reason), mload(reason))
                              }
                          }
                      }
                  } else {
                      return true;
                  }
              }
          
              /**
               * @dev Hook that is called before any token transfer. This includes minting and burning. If {ERC721Consecutive} is
               * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
               *
               * Calling conditions:
               *
               * - When `from` and `to` are both non-zero, ``from``'s tokens will be transferred to `to`.
               * - When `from` is zero, the tokens will be minted for `to`.
               * - When `to` is zero, ``from``'s tokens will be burned.
               * - `from` and `to` are never both zero.
               * - `batchSize` is non-zero.
               *
               * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
               */
              function _beforeTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal virtual {}
          
              /**
               * @dev Hook that is called after any token transfer. This includes minting and burning. If {ERC721Consecutive} is
               * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
               *
               * Calling conditions:
               *
               * - When `from` and `to` are both non-zero, ``from``'s tokens were transferred to `to`.
               * - When `from` is zero, the tokens were minted for `to`.
               * - When `to` is zero, ``from``'s tokens were burned.
               * - `from` and `to` are never both zero.
               * - `batchSize` is non-zero.
               *
               * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
               */
              function _afterTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal virtual {}
          
              /**
               * @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override.
               *
               * WARNING: Anyone calling this MUST ensure that the balances remain consistent with the ownership. The invariant
               * being that for any address `a` the value returned by `balanceOf(a)` must be equal to the number of tokens such
               * that `ownerOf(tokenId)` is `a`.
               */
              // solhint-disable-next-line func-name-mixedcase
              function __unsafe_increaseBalance(address account, uint256 amount) internal {
                  _balances[account] += amount;
              }
          }
          
          
          // File @openzeppelin/contracts/token/ERC721/extensions/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol)
          
          pragma solidity ^0.8.0;
          
          /**
           * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
           * @dev See https://eips.ethereum.org/EIPS/eip-721
           */
          interface IERC721Enumerable is IERC721 {
              /**
               * @dev Returns the total amount of tokens stored by the contract.
               */
              function totalSupply() external view returns (uint256);
          
              /**
               * @dev Returns a token ID owned by `owner` at a given `index` of its token list.
               * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
               */
              function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
          
              /**
               * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
               * Use along with {totalSupply} to enumerate all tokens.
               */
              function tokenByIndex(uint256 index) external view returns (uint256);
          }
          
          
          // File @openzeppelin/contracts/token/ERC721/extensions/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/extensions/ERC721Enumerable.sol)
          
          pragma solidity ^0.8.0;
          
          
          /**
           * @dev This implements an optional extension of {ERC721} defined in the EIP that adds
           * enumerability of all the token ids in the contract as well as all token ids owned by each
           * account.
           */
          abstract contract ERC721Enumerable is ERC721, IERC721Enumerable {
              // Mapping from owner to list of owned token IDs
              mapping(address => mapping(uint256 => uint256)) private _ownedTokens;
          
              // Mapping from token ID to index of the owner tokens list
              mapping(uint256 => uint256) private _ownedTokensIndex;
          
              // Array with all token ids, used for enumeration
              uint256[] private _allTokens;
          
              // Mapping from token id to position in the allTokens array
              mapping(uint256 => uint256) private _allTokensIndex;
          
              /**
               * @dev See {IERC165-supportsInterface}.
               */
              function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) {
                  return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId);
              }
          
              /**
               * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
               */
              function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
                  require(index < ERC721.balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
                  return _ownedTokens[owner][index];
              }
          
              /**
               * @dev See {IERC721Enumerable-totalSupply}.
               */
              function totalSupply() public view virtual override returns (uint256) {
                  return _allTokens.length;
              }
          
              /**
               * @dev See {IERC721Enumerable-tokenByIndex}.
               */
              function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
                  require(index < ERC721Enumerable.totalSupply(), "ERC721Enumerable: global index out of bounds");
                  return _allTokens[index];
              }
          
              /**
               * @dev See {ERC721-_beforeTokenTransfer}.
               */
              function _beforeTokenTransfer(
                  address from,
                  address to,
                  uint256 firstTokenId,
                  uint256 batchSize
              ) internal virtual override {
                  super._beforeTokenTransfer(from, to, firstTokenId, batchSize);
          
                  if (batchSize > 1) {
                      // Will only trigger during construction. Batch transferring (minting) is not available afterwards.
                      revert("ERC721Enumerable: consecutive transfers not supported");
                  }
          
                  uint256 tokenId = firstTokenId;
          
                  if (from == address(0)) {
                      _addTokenToAllTokensEnumeration(tokenId);
                  } else if (from != to) {
                      _removeTokenFromOwnerEnumeration(from, tokenId);
                  }
                  if (to == address(0)) {
                      _removeTokenFromAllTokensEnumeration(tokenId);
                  } else if (to != from) {
                      _addTokenToOwnerEnumeration(to, tokenId);
                  }
              }
          
              /**
               * @dev Private function to add a token to this extension's ownership-tracking data structures.
               * @param to address representing the new owner of the given token ID
               * @param tokenId uint256 ID of the token to be added to the tokens list of the given address
               */
              function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
                  uint256 length = ERC721.balanceOf(to);
                  _ownedTokens[to][length] = tokenId;
                  _ownedTokensIndex[tokenId] = length;
              }
          
              /**
               * @dev Private function to add a token to this extension's token tracking data structures.
               * @param tokenId uint256 ID of the token to be added to the tokens list
               */
              function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
                  _allTokensIndex[tokenId] = _allTokens.length;
                  _allTokens.push(tokenId);
              }
          
              /**
               * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
               * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
               * gas optimizations e.g. when performing a transfer operation (avoiding double writes).
               * This has O(1) time complexity, but alters the order of the _ownedTokens array.
               * @param from address representing the previous owner of the given token ID
               * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
               */
              function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
                  // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
                  // then delete the last slot (swap and pop).
          
                  uint256 lastTokenIndex = ERC721.balanceOf(from) - 1;
                  uint256 tokenIndex = _ownedTokensIndex[tokenId];
          
                  // When the token to delete is the last token, the swap operation is unnecessary
                  if (tokenIndex != lastTokenIndex) {
                      uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];
          
                      _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
                      _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
                  }
          
                  // This also deletes the contents at the last position of the array
                  delete _ownedTokensIndex[tokenId];
                  delete _ownedTokens[from][lastTokenIndex];
              }
          
              /**
               * @dev Private function to remove a token from this extension's token tracking data structures.
               * This has O(1) time complexity, but alters the order of the _allTokens array.
               * @param tokenId uint256 ID of the token to be removed from the tokens list
               */
              function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
                  // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
                  // then delete the last slot (swap and pop).
          
                  uint256 lastTokenIndex = _allTokens.length - 1;
                  uint256 tokenIndex = _allTokensIndex[tokenId];
          
                  // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
                  // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
                  // an 'if' statement (like in _removeTokenFromOwnerEnumeration)
                  uint256 lastTokenId = _allTokens[lastTokenIndex];
          
                  _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
                  _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
          
                  // This also deletes the contents at the last position of the array
                  delete _allTokensIndex[tokenId];
                  _allTokens.pop();
              }
          }
          
          
          // File contracts/IActiveControlUpgradeable.sol
          
          // Original license: SPDX_License_Identifier: MIT
          pragma solidity ^0.8.17;
          
          /// @notice Interface of {ActiveControlUpgradeable}
          interface IActiveControlUpgradeable {
              /// @notice Active period
              struct ActivePeriod {
                  /// @notice period start timestamp
                  uint256 startAt;
                  /// @notice period end timestamp
                  uint256 endAt;
              }
          
              /**
               *  @notice Emits when set a new active period.
               *  @param operator                 account that originated the contract call
               *  @param previousActivePeriod     previous active period
               *  @param newActivePeriod          new active period
               */
              event SetActivePeriod(
                  address indexed operator,
                  ActivePeriod previousActivePeriod,
                  ActivePeriod newActivePeriod
              );
          
              /// @notice Gets the active period.
              function activePeriod() external returns (ActivePeriod memory);
          
              /**
               *  @notice Gets the state whether this contract is active.
               *  @return true: active / false: inactive
               */
              function isActive() external returns (bool);
          
              /**
               *  @notice Inactivate this contract.
               *  @dev Sets period (start: 0, end: 0)
               */
              function pause() external;
          
              /**
               *  @notice Sets a new active period.
               *  @param activePeriod_    new active period
               */
              function setActivePeriod(ActivePeriod calldata activePeriod_) external;
          }
          
          
          // File contracts/ActiveControlUpgradeable.sol
          
          // Original license: SPDX_License_Identifier: MIT
          pragma solidity ^0.8.17;
          
          
          /// @notice Controls active period
          abstract contract ActiveControlUpgradeable is
              AccessControlUpgradeable,
              IActiveControlUpgradeable
          {
              /// @dev active period
              ActivePeriod private _activePeriod;
          
              /**
               *  @notice Initializes contract
               *  @param activePeriod_    active period
               *
               *  @dev
               *  Emits:
               *  - SetActivePeriod
               */
              function __ActiveControl_init(
                  ActivePeriod memory activePeriod_
              ) internal onlyInitializing {
                  __AccessControl_init();
                  __ActiveControl_init_unchained(activePeriod_);
              }
          
              /// @dev See {__ActiveControl_init}
              function __ActiveControl_init_unchained(
                  ActivePeriod memory activePeriod_
              ) internal onlyInitializing {
                  _setActivePeriod(activePeriod_);
              }
          
              // --- public / external ---
          
              /// @notice Gets the active period.
              function activePeriod() public view override returns (ActivePeriod memory) {
                  return _activePeriod;
              }
          
              /**
               *  @notice Gets the state whether this contract is active.
               *  @return true: active / false: inactive
               */
              function isActive() public view override returns (bool) {
                  return
                      block.timestamp >= _activePeriod.startAt &&
                      block.timestamp <= _activePeriod.endAt;
              }
          
              /**
               *  @notice Inactivate this contract.
               *  @dev Sets period (start: 0, end: 0)
               *  Requirements:
               *  - the caller has `DEFAULT_ADMIN_ROLE` role
               *
               *  Emits:
               *  - SetActivePeriod
               *
               *  Reverts:
               *  - the caller does not have `DEFAULT_ADMIN_ROLE` role
               */
              function pause() external override onlyRole(DEFAULT_ADMIN_ROLE) {
                  _setActivePeriod(ActivePeriod(0, 0));
              }
          
              /**
               *  @notice Sets a new active period.
               *  @param activePeriod_    new active period
               *  @dev
               *  Requirements:
               *  - the caller has `DEFAULT_ADMIN_ROLE` role
               *
               *  Emits:
               *  - SetActivePeriod
               *
               *  Reverts:
               *  - the caller does not have `DEFAULT_ADMIN_ROLE` role
               *  - endAt is not greater than startAt
               *    - except: both startAt and endAt are 0
               */
              function setActivePeriod(
                  ActivePeriod memory activePeriod_
              ) external virtual override onlyRole(DEFAULT_ADMIN_ROLE) {
                  _setActivePeriod(activePeriod_);
              }
          
              /**
               * @dev See {IERC165-supportsInterface}.
               */
              function supportsInterface(
                  bytes4 interfaceId
              ) public view virtual override returns (bool) {
                  return
                      interfaceId == type(IActiveControlUpgradeable).interfaceId ||
                      super.supportsInterface(interfaceId);
              }
          
              // --- internal ---
          
              /**
               *  @notice Sets a new active period.
               *  @param activePeriod_    new active period
               *  @dev
               *  Emits:
               *  - SetActivePeriod
               *
               *  Reverts:
               *  - endAt is not greater than startAt
               *    - except: both startAt and endAt are 0
               */
              function _setActivePeriod(ActivePeriod memory activePeriod_) internal {
                  require(
                      activePeriod_.endAt > activePeriod_.startAt ||
                          (activePeriod_.startAt == 0 && activePeriod_.endAt == 0),
                      "endAt must be greater than startAt or both startAt and endAt must be 0"
                  );
                  ActivePeriod memory previousActivePeriod = _activePeriod;
                  _activePeriod = activePeriod_;
                  emit SetActivePeriod(_msgSender(), previousActivePeriod, _activePeriod);
              }
          
              /**
               * @dev This empty reserved space is put in place to allow future versions to add new
               * variables without shifting down storage in the inheritance chain.
               * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
               */
              uint256[49] private __gap;
          }
          
          
          // File @openzeppelin/contracts-upgradeable/utils/cryptography/[email protected]
          
          // Original license: SPDX_License_Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.9.2) (utils/cryptography/MerkleProof.sol)
          
          pragma solidity ^0.8.0;
          
          /**
           * @dev These functions deal with verification of Merkle Tree proofs.
           *
           * The tree and the proofs can be generated using our
           * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
           * You will find a quickstart guide in the readme.
           *
           * WARNING: You should avoid using leaf values that are 64 bytes long prior to
           * hashing, or use a hash function other than keccak256 for hashing leaves.
           * This is because the concatenation of a sorted pair of internal nodes in
           * the merkle tree could be reinterpreted as a leaf value.
           * OpenZeppelin's JavaScript library generates merkle trees that are safe
           * against this attack out of the box.
           */
          library MerkleProofUpgradeable {
              /**
               * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
               * defined by `root`. For this, a `proof` must be provided, containing
               * sibling hashes on the branch from the leaf to the root of the tree. Each
               * pair of leaves and each pair of pre-images are assumed to be sorted.
               */
              function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
                  return processProof(proof, leaf) == root;
              }
          
              /**
               * @dev Calldata version of {verify}
               *
               * _Available since v4.7._
               */
              function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
                  return processProofCalldata(proof, leaf) == root;
              }
          
              /**
               * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
               * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
               * hash matches the root of the tree. When processing the proof, the pairs
               * of leafs & pre-images are assumed to be sorted.
               *
               * _Available since v4.4._
               */
              function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
                  bytes32 computedHash = leaf;
                  for (uint256 i = 0; i < proof.length; i++) {
                      computedHash = _hashPair(computedHash, proof[i]);
                  }
                  return computedHash;
              }
          
              /**
               * @dev Calldata version of {processProof}
               *
               * _Available since v4.7._
               */
              function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
                  bytes32 computedHash = leaf;
                  for (uint256 i = 0; i < proof.length; i++) {
                      computedHash = _hashPair(computedHash, proof[i]);
                  }
                  return computedHash;
              }
          
              /**
               * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a merkle tree defined by
               * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
               *
               * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
               *
               * _Available since v4.7._
               */
              function multiProofVerify(
                  bytes32[] memory proof,
                  bool[] memory proofFlags,
                  bytes32 root,
                  bytes32[] memory leaves
              ) internal pure returns (bool) {
                  return processMultiProof(proof, proofFlags, leaves) == root;
              }
          
              /**
               * @dev Calldata version of {multiProofVerify}
               *
               * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
               *
               * _Available since v4.7._
               */
              function multiProofVerifyCalldata(
                  bytes32[] calldata proof,
                  bool[] calldata proofFlags,
                  bytes32 root,
                  bytes32[] memory leaves
              ) internal pure returns (bool) {
                  return processMultiProofCalldata(proof, proofFlags, leaves) == root;
              }
          
              /**
               * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
               * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
               * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
               * respectively.
               *
               * CAUTION: Not all merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
               * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
               * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
               *
               * _Available since v4.7._
               */
              function processMultiProof(
                  bytes32[] memory proof,
                  bool[] memory proofFlags,
                  bytes32[] memory leaves
              ) internal pure returns (bytes32 merkleRoot) {
                  // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
                  // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
                  // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
                  // the merkle tree.
                  uint256 leavesLen = leaves.length;
                  uint256 proofLen = proof.length;
                  uint256 totalHashes = proofFlags.length;
          
                  // Check proof validity.
                  require(leavesLen + proofLen - 1 == totalHashes, "MerkleProof: invalid multiproof");
          
                  // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
                  // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
                  bytes32[] memory hashes = new bytes32[](totalHashes);
                  uint256 leafPos = 0;
                  uint256 hashPos = 0;
                  uint256 proofPos = 0;
                  // At each step, we compute the next hash using two values:
                  // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
                  //   get the next hash.
                  // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
                  //   `proof` array.
                  for (uint256 i = 0; i < totalHashes; i++) {
                      bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
                      bytes32 b = proofFlags[i]
                          ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
                          : proof[proofPos++];
                      hashes[i] = _hashPair(a, b);
                  }
          
                  if (totalHashes > 0) {
                      require(proofPos == proofLen, "MerkleProof: invalid multiproof");
                      unchecked {
                          return hashes[totalHashes - 1];
                      }
                  } else if (leavesLen > 0) {
                      return leaves[0];
                  } else {
                      return proof[0];
                  }
              }
          
              /**
               * @dev Calldata version of {processMultiProof}.
               *
               * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
               *
               * _Available since v4.7._
               */
              function processMultiProofCalldata(
                  bytes32[] calldata proof,
                  bool[] calldata proofFlags,
                  bytes32[] memory leaves
              ) internal pure returns (bytes32 merkleRoot) {
                  // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
                  // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
                  // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
                  // the merkle tree.
                  uint256 leavesLen = leaves.length;
                  uint256 proofLen = proof.length;
                  uint256 totalHashes = proofFlags.length;
          
                  // Check proof validity.
                  require(leavesLen + proofLen - 1 == totalHashes, "MerkleProof: invalid multiproof");
          
                  // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
                  // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
                  bytes32[] memory hashes = new bytes32[](totalHashes);
                  uint256 leafPos = 0;
                  uint256 hashPos = 0;
                  uint256 proofPos = 0;
                  // At each step, we compute the next hash using two values:
                  // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
                  //   get the next hash.
                  // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
                  //   `proof` array.
                  for (uint256 i = 0; i < totalHashes; i++) {
                      bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
                      bytes32 b = proofFlags[i]
                          ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
                          : proof[proofPos++];
                      hashes[i] = _hashPair(a, b);
                  }
          
                  if (totalHashes > 0) {
                      require(proofPos == proofLen, "MerkleProof: invalid multiproof");
                      unchecked {
                          return hashes[totalHashes - 1];
                      }
                  } else if (leavesLen > 0) {
                      return leaves[0];
                  } else {
                      return proof[0];
                  }
              }
          
              function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
                  return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
              }
          
              function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      mstore(0x00, a)
                      mstore(0x20, b)
                      value := keccak256(0x00, 0x40)
                  }
              }
          }
          
          
          // File contracts/IAllowListNFTStore.sol
          
          // Original license: SPDX_License_Identifier: MIT
          pragma solidity ^0.8.17;
          
          /// @notice Interface of {AllowListNFTStore}
          interface IAllowListNFTStore {
              /**
               *  @notice Emits when set a new merkle root.
               *  @param operator                 account that originated the contract call
               *  @param previousMerkleRoot       previous merkle root
               *  @param newMerkleRoot            new merkle root
               */
              event SetAllowListMerkleRoot(
                  address indexed operator,
                  bytes32 indexed previousMerkleRoot,
                  bytes32 indexed newMerkleRoot
              );
          
              /// @notice Gets the merkle root.
              function merkleRoot() external returns (bytes32);
          
              /**
               *  @notice Verifies allow list merkle proof.
               *  @param proof                allow list merkle proof data
               *  @param allowedAddress       allowed address
               *  @param allowance            allowance
               *  @return true: verified / false: failed to verify
               */
              function verifyAllowListMerkleProof(
                  bytes32[] memory proof,
                  address allowedAddress,
                  uint256 allowance
              ) external returns (bool);
          
              /**
               *  @notice Sets a new merkle root.
               *  @param merkleRoot_      new merkle root
               */
              function setAllowListMerkleRoot(bytes32 merkleRoot_) external;
          
              /**
               *  @notice Buys NFTs.
               *  @param recipient            NFT recipient address
               *  @param proof                allow list merkle proof data
               *  @param allowance            allowance
               *  @param buyAmount            buy amount
               */
              function buy(
                  address recipient,
                  bytes32[] memory proof,
                  uint256 allowance,
                  uint256 buyAmount
              ) external payable;
          }
          
          
          // File contracts/INFTStore.sol
          
          // Original license: SPDX_License_Identifier: MIT
          pragma solidity ^0.8.7;
          
          /// @notice Interface of {NFTStore}
          interface INFTStore is IActiveControlUpgradeable {
              /**
               *  @notice Emits when set a new NFT price. (for direct)
               *  @param operator                 account that originated the contract call
               *  @param previousPrice            previous NFT price [wei]
               *  @param newPrice                 new NFT price [wei]
               */
              event SetNFTPriceForDirect(
                  address indexed operator,
                  uint256 indexed previousPrice,
                  uint256 indexed newPrice
              );
              /**
               *  @notice Emits when set a new NFT price. (for Piement)
               *  @param operator                 account that originated the contract call
               *  @param previousPrice            previous NFT price [wei]
               *  @param newPrice                 new NFT price [wei]
               */
              event SetNFTPriceForPiement(
                  address indexed operator,
                  uint256 indexed previousPrice,
                  uint256 indexed newPrice
              );
              /**
               *  @notice Emits when set a new max total bought count.
               *  @param operator                         account that originated the contract call
               *  @param previousMaxTotalBoughtCount      previous max total bought count
               *  @param newMaxTotalBoughtCount           new max total bought count
               */
              event SetMaxTotalBoughtCount(
                  address indexed operator,
                  uint256 indexed previousMaxTotalBoughtCount,
                  uint256 indexed newMaxTotalBoughtCount
              );
              /**
               *  @notice Emits when set a new Piement address.
               *  @param operator                     account that originated the contract call
               *  @param previousPiementAddress       previous Piement address
               *  @param newPiementAddress            new Piement address
               */
              event SetPiementAddress(
                  address indexed operator,
                  address indexed previousPiementAddress,
                  address indexed newPiementAddress
              );
              /**
               *  @notice Emits when set a new NFT Storage.
               *  @param operator                 account that originated the contract call
               *  @param previousNFTStorage       previous NFT Storage
               *  @param newNFTStorage            new NFT Storage
               */
              event SetNFTStorage(
                  address indexed operator,
                  address indexed previousNFTStorage,
                  address indexed newNFTStorage
              );
              /**
               *  @notice Emits when collected sales.
               *  @param operator                 account that originated the contract call
               *  @param recipient                account that collected sales to
               *  @param collectedAmount          collected amount [wei]
               */
              event CollectedSales(
                  address indexed operator,
                  address indexed recipient,
                  uint256 indexed collectedAmount
              );
              /**
               *  @notice Emits when bought NFT.
               *  @param payer                    account that paid
               *  @param recipient                account that receive NFT
               *  @param price                    amount that payer spent [wei]
               *  @param tokenIds                 bought NFT IDs
               */
              event BoughtNFT(
                  address indexed payer,
                  address indexed recipient,
                  uint256 indexed price,
                  uint256[] tokenIds
              );
          
              // -- read --
          
              /// @notice Gets the total bought count in this contract.
              function totalBoughtCount() external returns (uint256);
          
              /**
               *  @notice Gets the specified buyer bought count.
               *  @param buyer        buyer address to get
               */
              function boughtCount(address buyer) external returns (uint256);
          
              /// @notice Gets the NFT price [wei]. (for direct)
              function nftPriceForDirect() external returns (uint256);
          
              /// @notice Gets the NFT price [wei]. (for Piement)
              function nftPriceForPiement() external returns (uint256);
          
              /// @notice Gets the max total bought count.
              function maxTotalBoughtCount() external returns (uint256);
          
              // @notice Gets the piement address.
              function piementAddress() external returns (address);
          
              /// @notice Gets the NFT storage address.
              function nftStorage() external returns (address);
          
              /// @notice Gets the NFT contract address.
              function nftContract() external returns (address);
          
              // -- write --
          
              /**
               *  @notice Sets a new NFT price. (for direct)
               *  @param nftPrice_    new NFT price [wei]
               */
              function setNFTPriceForDirect(uint256 nftPrice_) external;
          
              /**
               *  @notice Sets a new NFT price. (for Piement)
               *  @param nftPrice_    new NFT price [wei]
               */
              function setNFTPriceForPiement(uint256 nftPrice_) external;
          
              /**
               *  @notice Sets a new max total bought count.
               *  @param maxTotalBoughtCount_     new max total bought count
               */
              function setMaxTotalBoughtCount(uint256 maxTotalBoughtCount_) external;
          
              /**
               *  @notice Sets a new Piement address.
               *  @param piementAddress_      new Piement address
               */
              function setPiementAddress(address piementAddress_) external;
          
              /**
               *  @notice Sets a new NFT storage.
               *  @param nftStorage_      new NFT storage address
               */
              function setNFTStorage(address nftStorage_) external;
          
              /**
               *  @notice Withdraws sales.
               *  @param recipient        account to receive
               */
              function collectSales(address payable recipient) external;
          }
          
          
          // File contracts/NFTStore.sol
          
          // Original license: SPDX_License_Identifier: MIT
          pragma solidity ^0.8.17;
          
          
          
          /// @notice {NFTStore} is NFT Store base.
          abstract contract NFTStore is ActiveControlUpgradeable, INFTStore {
              /// @dev NFT price (for direct) [wei]
              uint256 private _nftPriceForDirect;
          
              /// @dev NFT price (for Piement) [wei]
              uint256 private _nftPriceForPiement;
          
              /// @dev Buyer and bought count mapping
              mapping(address => uint256) private _boughtCountMap;
          
              /// @dev Total bought count in this contract
              uint256 private _totalBoughtCount;
          
              /// @dev Max total bought count
              uint256 private _maxTotalBoughtCount;
          
              /// @dev Piement address
              address private _piementAddress;
          
              /// @dev NFT storage address
              address private _nftStorage;
          
              /// @dev NFT contract interface
              IERC721Enumerable private _nftContract;
          
              /**
               *  @notice Initializes contract
               *  @param nftPriceForDirect_       NFT price (for direct) [wei]
               *  @param nftPriceForPiement_      NFT price (for Piement) [wei]
               *  @param maxTotalBoughtCount_     max total bought count
               *  @param piementAddress_          Piement address
               *  @param nftStorage_              NFT storage address
               *  @param nftContract_             NFT contract
               *  @param activePeriod_            active period
               *  @dev
               *  Emits:
               *  - SetNFTPriceForDirect
               *  - SetNFTPriceForPiement
               *  - SetMaxBoughtCount
               *  - SetPiementAddress
               *  - SetNFTStorage
               */
              function __NFTStore_init(
                  uint256 nftPriceForDirect_,
                  uint256 nftPriceForPiement_,
                  uint256 maxTotalBoughtCount_,
                  address piementAddress_,
                  address nftStorage_,
                  IERC721Enumerable nftContract_,
                  ActivePeriod memory activePeriod_
              ) internal onlyInitializing {
                  __ActiveControl_init(activePeriod_);
                  __NFTStore_init_unchained(
                      nftPriceForDirect_,
                      nftPriceForPiement_,
                      maxTotalBoughtCount_,
                      piementAddress_,
                      nftStorage_,
                      nftContract_
                  );
              }
          
              /// @dev See {__NFTStore_init}
              function __NFTStore_init_unchained(
                  uint256 nftPriceForDirect_,
                  uint256 nftPriceForPiement_,
                  uint256 maxTotalBoughtCount_,
                  address piementAddress_,
                  address nftStorage_,
                  IERC721Enumerable nftContract_
              ) internal onlyInitializing {
                  _setNFTPriceForDirect(nftPriceForDirect_);
                  _setNFTPriceForPiement(nftPriceForPiement_);
                  _setMaxTotalBoughtCount(maxTotalBoughtCount_);
                  _setPiementAddress(piementAddress_);
                  _setNFTStorage(nftStorage_);
          
                  require(
                      address(nftContract_) != address(0),
                      "nftContract is zero address"
                  );
                  _nftContract = nftContract_;
              }
          
              // --- public / external ---
          
              // -- read --
          
              /// @notice Gets the total bought count in this contract.
              function totalBoughtCount() public view override returns (uint256) {
                  return _totalBoughtCount;
              }
          
              /**
               *  @notice Gets the specified buyer bought count.
               *  @param buyer        buyer address to get
               */
              function boughtCount(address buyer) public view override returns (uint256) {
                  return _boughtCountMap[buyer];
              }
          
              /// @notice Gets the NFT price (for direct) [wei].
              function nftPriceForDirect() public view override returns (uint256) {
                  return _nftPriceForDirect;
              }
          
              /// @notice Gets the NFT price (for Piement) [wei].
              function nftPriceForPiement() public view override returns (uint256) {
                  return _nftPriceForPiement;
              }
          
              /// @notice Gets the max total bought count.
              function maxTotalBoughtCount() public view override returns (uint256) {
                  return _maxTotalBoughtCount;
              }
          
              // @notice Gets the piement address.
              function piementAddress() public view override returns (address) {
                  return _piementAddress;
              }
          
              /// @notice Gets the NFT storage address.
              function nftStorage() public view override returns (address) {
                  return _nftStorage;
              }
          
              /// @notice Gets the NFT contract address.
              function nftContract() public view override returns (address) {
                  return address(_nftContract);
              }
          
              // -- write --
          
              /**
               *  @notice Sets a new NFT price. (for direct)
               *  @param nftPriceForDirect_    new NFT price [wei]
               *  @dev
               *  Requirements:
               *  - the caller has `DEFAULT_ADMIN_ROLE` role
               *
               *  Emits:
               *  - SetNFTPriceForDirect
               *
               *  Reverts:
               *  - the caller does not have `DEFAULT_ADMIN_ROLE` role
               *  - `nftPriceForDirect_` is 0
               */
              function setNFTPriceForDirect(
                  uint256 nftPriceForDirect_
              ) external override onlyRole(DEFAULT_ADMIN_ROLE) {
                  _setNFTPriceForDirect(nftPriceForDirect_);
              }
          
              /**
               *  @notice Sets a new NFT price. (for direct)
               *  @param nftPriceForPiement_    new NFT price [wei]
               *  @dev
               *  Requirements:
               *  - the caller has `DEFAULT_ADMIN_ROLE` role
               *
               *  Emits:
               *  - SetNFTPriceForPiement
               *
               *  Reverts:
               *  - the caller does not have `DEFAULT_ADMIN_ROLE` role
               *  - `nftPriceForPiement_` is 0
               */
              function setNFTPriceForPiement(
                  uint256 nftPriceForPiement_
              ) external override onlyRole(DEFAULT_ADMIN_ROLE) {
                  _setNFTPriceForPiement(nftPriceForPiement_);
              }
          
              /**
               *  @notice Sets a new max total bought count.
               *  @param maxTotalBoughtCount_     new max total bought count
               *  @dev
               *  Requirements:
               *  - the caller has `DEFAULT_ADMIN_ROLE` role
               *
               *  Emits:
               *  - SetMaxTotalBoughtCount
               *
               *  Reverts:
               *  - the caller does not have `DEFAULT_ADMIN_ROLE` role
               *  - `maxTotalBoughtCount_` is 0
               */
              function setMaxTotalBoughtCount(
                  uint256 maxTotalBoughtCount_
              ) external override onlyRole(DEFAULT_ADMIN_ROLE) {
                  _setMaxTotalBoughtCount(maxTotalBoughtCount_);
              }
          
              /**
               *  @notice Sets a new Piement address.
               *  @param piementAddress_      new Piement address
               *  @dev
               *  Requirements:
               *  - the caller has `DEFAULT_ADMIN_ROLE` role
               *
               *  Emits:
               *  - SetPiementAddress
               *
               *  Reverts:
               *  - the caller does not have `DEFAULT_ADMIN_ROLE` role
               *  - `piementAddress_` is zero address
               */
              function setPiementAddress(
                  address piementAddress_
              ) external override onlyRole(DEFAULT_ADMIN_ROLE) {
                  _setPiementAddress(piementAddress_);
              }
          
              /**
               *  @notice Sets a new NFT storage.
               *  @param nftStorage_      new NFT storage address
               *  @dev
               *  Requirements:
               *  - the caller has `DEFAULT_ADMIN_ROLE` role
               *
               *  Emits:
               *  - SetNFTStorage
               *
               *  Reverts:
               *  - the caller does not have `DEFAULT_ADMIN_ROLE` role
               *  - `nftStorage_` is zero address
               */
              function setNFTStorage(
                  address nftStorage_
              ) external override onlyRole(DEFAULT_ADMIN_ROLE) {
                  _setNFTStorage(nftStorage_);
              }
          
              /**
               *  @notice Withdraws sales.
               *  @param recipient        account to receive
               *  @dev
               *  Requirements:
               *  - the caller has `DEFAULT_ADMIN_ROLE` role
               *
               *  Emits:
               *  - CollectedSales
               *
               *  Reverts:
               *  - the caller does not have `DEFAULT_ADMIN_ROLE` role
               *  - `recipient` is zero address
               *  - sales is empty
               *  - failed to transfer value to recipient
               */
              function collectSales(
                  address payable recipient
              ) external override onlyRole(DEFAULT_ADMIN_ROLE) {
                  require(recipient != address(0), "recipient is zero address");
                  require(address(this).balance > 0, "sales is empty");
          
                  emit CollectedSales(_msgSender(), recipient, address(this).balance);
                  (bool success, ) = recipient.call{value: address(this).balance}("");
                  require(success, "failed to transfer");
              }
          
              /**
               * @dev See {IERC165-supportsInterface}.
               */
              function supportsInterface(
                  bytes4 interfaceId
              ) public view virtual override returns (bool) {
                  return
                      interfaceId == type(INFTStore).interfaceId ||
                      super.supportsInterface(interfaceId);
              }
          
              // --- internal ---
          
              /**
               *  @notice Sets a new NFT price. (for direct)
               *  @param nftPriceForDirect_    new NFT price [wei]
               *  @dev
               *  Emits:
               *  - SetNFTPriceForDirect
               *
               *  Reverts:
               *  - `nftPriceForDirect_` is 0
               */
              function _setNFTPriceForDirect(uint256 nftPriceForDirect_) internal {
                  require(
                      nftPriceForDirect_ > 0,
                      "nftPriceForDirect must be greater than 0"
                  );
                  uint256 previousPrice = _nftPriceForDirect;
                  _nftPriceForDirect = nftPriceForDirect_;
                  emit SetNFTPriceForDirect(
                      _msgSender(),
                      previousPrice,
                      _nftPriceForDirect
                  );
              }
          
              /**
               *  @notice Sets a new NFT price. (for Piement)
               *  @param nftPriceForPiement_    new NFT price [wei]
               *  @dev
               *  Emits:
               *  - SetNFTPriceForPiement
               *
               *  Reverts:
               *  - `nftPriceForPiement_` is 0
               */
              function _setNFTPriceForPiement(uint256 nftPriceForPiement_) internal {
                  require(
                      nftPriceForPiement_ > 0,
                      "nftPriceForPiement must be greater than 0"
                  );
                  uint256 previousPrice = _nftPriceForPiement;
                  _nftPriceForPiement = nftPriceForPiement_;
                  emit SetNFTPriceForPiement(
                      _msgSender(),
                      previousPrice,
                      _nftPriceForPiement
                  );
              }
          
              /**
               *  @notice Sets a new max total bought count.
               *  @param maxTotalBoughtCount_     new max total bought count
               *  @dev
               *  Emits:
               *  - SetMaxTotalBoughtCount
               *
               *  Reverts:
               *  - `maxTotalBoughtCount_` is not greater than totalBoughtCount
               */
              function _setMaxTotalBoughtCount(uint256 maxTotalBoughtCount_) internal {
                  require(
                      maxTotalBoughtCount_ > _totalBoughtCount,
                      "maxTotalBoughtCount must be greater than totalBoughtCount"
                  );
                  uint256 previousMaxTotalBoughtCount = _maxTotalBoughtCount;
                  _maxTotalBoughtCount = maxTotalBoughtCount_;
                  emit SetMaxTotalBoughtCount(
                      _msgSender(),
                      previousMaxTotalBoughtCount,
                      _maxTotalBoughtCount
                  );
              }
          
              /**
               *  @notice Sets a new Piement address.
               *  @param piementAddress_      new Piement address
               *  @dev
               *  Emits:
               *  - SetPiementAddress
               *
               *  Reverts:
               *  - `piementAddress_` is zero address
               */
              function _setPiementAddress(address piementAddress_) internal {
                  require(
                      piementAddress_ != address(0),
                      "piementAddress is zero address"
                  );
                  address previousPiementAddress = _piementAddress;
                  _piementAddress = piementAddress_;
                  emit SetPiementAddress(
                      _msgSender(),
                      previousPiementAddress,
                      _piementAddress
                  );
              }
          
              /**
               *  @notice Sets a new NFT storage.
               *  @param nftStorage_      new NFT storage address
               *  @dev
               *  Emits:
               *  - SetNFTStorage
               *
               *  Reverts:
               *  - `nftStorage_` is zero address
               */
              function _setNFTStorage(address nftStorage_) internal {
                  require(nftStorage_ != address(0), "nftStorage is zero address");
                  address previousNFTStorage = _nftStorage;
                  _nftStorage = nftStorage_;
                  emit SetNFTStorage(_msgSender(), previousNFTStorage, _nftStorage);
              }
          
              /**
               *  @notice Buys NFTs.
               *  @param recipient        NFT recipient address
               *  @param buyAmount        buy amount
               *  @dev
               *  Requirements:
               *  - Buyer sends a price (nftPrice * buyAmount) in msg.value
               *
               *  Emits:
               *  - BoughtNFT: emits per NFTs
               *
               *  Reverts:
               *  - store is inactive
               *  - `buyAmount` is not greater than 0
               *  - `buyAmount` is exceeded remaining count
               *  - `buyAmount` is exceeded remaining stock in nftStorage
               *  - msg.value is different to price (nftPrice * buyAmount)
               *  - recipient is zero address
               */
              function _buy(address recipient, uint256 buyAmount) internal {
                  require(isActive(), "store is inactive");
                  require(buyAmount > 0, "buyAmount must be greater than 0");
                  require(
                      _totalBoughtCount + buyAmount <= _maxTotalBoughtCount,
                      "buyAmount exceeded remaining total count"
                  );
          
                  _boughtCountMap[recipient] += buyAmount;
                  _totalBoughtCount += buyAmount;
          
                  uint256 price = (
                      _msgSender() == _piementAddress
                          ? _nftPriceForPiement
                          : _nftPriceForDirect
                  ) * buyAmount;
                  require(msg.value == price, "sent value is different to price");
          
                  uint256[] memory tokenIds = new uint256[](buyAmount);
                  for (uint256 i = 0; i < buyAmount; i++) {
                      tokenIds[i] = _nftContract.tokenOfOwnerByIndex(_nftStorage, i);
                  }
          
                  emit BoughtNFT(_msgSender(), recipient, price, tokenIds);
          
                  for (uint256 i = 0; i < buyAmount; i++) {
                      _nftContract.safeTransferFrom(_nftStorage, recipient, tokenIds[i]);
                  }
              }
          
              /**
               * @dev This empty reserved space is put in place to allow future versions to add new
               * variables without shifting down storage in the inheritance chain.
               * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
               */
              uint256[44] private __gap;
          }
          
          
          // File contracts/AllowListNFTStore.sol
          
          // Original license: SPDX_License_Identifier: MIT
          pragma solidity ^0.8.17;
          
          
          
          
          
          /// @notice {AllowListNFTStore} is private sale NFT Store using allow list merkle proof.
          contract AllowListNFTStore is
              UUPSUpgradeable,
              NFTStore,
              ReentrancyGuardUpgradeable,
              IAllowListNFTStore
          {
              /// @notice Merkle root
              bytes32 private _merkleRoot;
          
              /**
               *  @notice Initializes contract
               *  @param admin                    account that grant admin role to
               *  @param merkleRoot_              merkle root
               *  @param nftPriceForDirect_       NFT price (for direct) [wei]
               *  @param nftPriceForPiement_      NFT price (for Piement) [wei]
               *  @param maxTotalBoughtCount_     max total bought count
               *  @param piementAddress_          Piement address
               *  @param nftStorage_              NFT storage address
               *  @param nftContract_             NFT contract
               *  @param activePeriod_            active period
               *  @dev
               *  Emits:
               *  - SetNFTPriceForDirect
               *  - SetNFTPriceForPiement
               *  - SetMaxBoughtCount
               *  - SetPiementAddress
               *  - SetNFTStorage
               *  - SetAllowListMerkleRoot
               */
              function initialize(
                  address admin,
                  bytes32 merkleRoot_,
                  uint256 nftPriceForDirect_,
                  uint256 nftPriceForPiement_,
                  uint256 maxTotalBoughtCount_,
                  address piementAddress_,
                  address nftStorage_,
                  IERC721Enumerable nftContract_,
                  ActivePeriod memory activePeriod_
              ) public initializer {
                  __UUPSUpgradeable_init();
                  __NFTStore_init(
                      nftPriceForDirect_,
                      nftPriceForPiement_,
                      maxTotalBoughtCount_,
                      piementAddress_,
                      nftStorage_,
                      nftContract_,
                      activePeriod_
                  );
                  __ReentrancyGuard_init();
          
                  _grantRole(DEFAULT_ADMIN_ROLE, admin);
                  _setAllowListMerkleRoot(merkleRoot_);
              }
          
              /// @notice Gets the merkle root.
              function merkleRoot() external view override returns (bytes32) {
                  return _merkleRoot;
              }
          
              /**
               *  @notice Verifies allow list merkle proof.
               *  @param proof                allow list merkle proof data
               *  @param allowedAddress       allowed address
               *  @param allowance            allowance
               *  @return true: verified / false: failed to verify
               */
              function verifyAllowListMerkleProof(
                  bytes32[] memory proof,
                  address allowedAddress,
                  uint256 allowance
              ) public view override returns (bool) {
                  return
                      MerkleProofUpgradeable.verify(
                          proof,
                          _merkleRoot,
                          keccak256(
                              bytes.concat(
                                  keccak256(abi.encode(allowedAddress, allowance))
                              )
                          )
                      );
              }
          
              /**
               *  @notice Sets a new merkle root.
               *  @param merkleRoot_      new merkle root
               *  @dev
               *  Requirements:
               *  - the caller has `DEFAULT_ADMIN_ROLE` role
               *
               *  Emits:
               *  - SetAllowListMerkleRoot
               *
               *  Reverts:
               *  - the caller does not have `DEFAULT_ADMIN_ROLE` role
               *  - `merkleRoot_` is no data
               */
              function setAllowListMerkleRoot(
                  bytes32 merkleRoot_
              ) external override onlyRole(DEFAULT_ADMIN_ROLE) {
                  _setAllowListMerkleRoot(merkleRoot_);
              }
          
              /**
               *  @notice Buys NFTs.
               *  @param recipient            NFT recipient address
               *  @param proof                allow list merkle proof data
               *  @param allowance            allowance
               *  @param buyAmount            buy amount
               *  @dev
               *  Requirements:
               *  - Buyer sends a price (nftPrice * buyAmount) in msg.value
               *
               *  Emits:
               *  - BoughtNFT: emits per NFTs
               *
               *  Reverts:
               *  - failed to verify allow list merkle proof
               *  - `buyAmount` is exceeded remaining allowance
               *  - store is inactive
               *  - `buyAmount` is not greater than 0
               *  - `buyAmount` is exceeded remaining count
               *  - `buyAmount` is exceeded remaining stock in nftStorage
               *  - msg.value is different to price (nftPrice * buyAmount)
               *  - NFT storage's NFTs is out of stock
               *  - recipient is zero address
               */
              function buy(
                  address recipient,
                  bytes32[] memory proof,
                  uint256 allowance,
                  uint256 buyAmount
              ) external payable override nonReentrant {
                  require(
                      verifyAllowListMerkleProof(proof, recipient, allowance),
                      "failed to verify allow list merkle proof"
                  );
                  uint256 boughtCount = boughtCount(recipient);
                  require(
                      boughtCount + buyAmount <= allowance,
                      "buyAmount exceeded remaining allowance"
                  );
          
                  _buy(recipient, buyAmount);
              }
          
              /**
               * @dev See {IERC165-supportsInterface}.
               */
              function supportsInterface(
                  bytes4 interfaceId
              ) public view virtual override returns (bool) {
                  return
                      interfaceId == type(IAllowListNFTStore).interfaceId ||
                      super.supportsInterface(interfaceId);
              }
          
              // --- internal ---
          
              /**
               *  @notice Sets a new merkle root.
               *  @param merkleRoot_      new merkle root
               *  @dev
               *  Emits:
               *  - SetAllowListMerkleRoot
               *
               *  Reverts:
               *  - `merkleRoot_` is no data
               */
              function _setAllowListMerkleRoot(bytes32 merkleRoot_) internal {
                  require(merkleRoot_ != 0x00, "merkle root is no data");
                  bytes32 previousMerkleRoot = _merkleRoot;
                  _merkleRoot = merkleRoot_;
                  emit SetAllowListMerkleRoot(
                      _msgSender(),
                      previousMerkleRoot,
                      _merkleRoot
                  );
              }
          
              /**
               *  @dev Function that should revert when `msg.sender` does not have `DEFAULT_ADMIN_ROLE` role to upgrade the contract.
               *  Called by {upgradeTo} and {upgradeToAndCall}.
               *
               *  Requirements:
               *  - the caller does not have `DEFAULT_ADMIN_ROLE` role.
               */
              function _authorizeUpgrade(
                  address newImplementation
              ) internal override onlyRole(DEFAULT_ADMIN_ROLE) {}
          
              /**
               * @dev This empty reserved space is put in place to allow future versions to add new
               * variables without shifting down storage in the inheritance chain.
               * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
               */
              uint256[49] private __gap;
          }
          
          
          // File contracts/BloodCrystal.sol
          
          // Original license: SPDX_License_Identifier: MIT
          pragma solidity ^0.8.9;
          /*
               : '::                                                                                                      ,  .__  
             "Jf?v7]]Y!  '!..'.  ....!'`  .'...     ..    .`     .;.`-   `.          .-".''  ."`-`.:    .  .~.  `;.._'. >It7>ICs]-
             !y{Cl////" T}}LLxJ> JxL*}usT =)t(l[Cl `(<?_  -lf- >tL*txC_ ~Js         IlJtT}{  YJttfC3- exx ,IC! yzIJ()}Y =>>CxfL/!.
             ;Vyoc      ,"sCC2/~ oCn=   _ 1C[  [Cl `]jCyv -]C, tuf  Co; ;C]        .}C/"?Ce. eCY  Cu_ ACj ~CC( AC3  2uj   .Aoo!   
             "AoeeeaaA/   2eC=   A2e3eeA/ <2eoe2Az .2e<!3Al44, foyxePS; ;eA        .2o"      2oeCeaZ, /2eu2Av: aAoyoAAz   .A3o_   
             rka5l        S4e=   4S2)     i5AAhZ7= `wZr `<wZw, ]S2<?kw; ;S5_.      .Ca! ;JT  y4aZPC'   -eSa^   ZPa?<<>,   .SAS=   
           'oe5U9PSZPo!   6651   G9Zee2". TEa>2%]  `G4>   ^UkA:29] .wm" ;9U932;    .U6a]2pO..P6h7EpL    CSE~   EmP:       .9kG?`  
          'la25ZSZPZkk?   ?ye+   |}yeoeA( !sj  *oT.`jyC_  .eoA~*ul .oy; _CI]2oA+    oeyeoeA,Coy<.t3y"   Jyz'   22J`       .k44SL  
          
          
               ;eeeeyjIr`  "eel                         ~[ex-       "x40W5I!.                          .~__`            =ee=      
               !BBNV?%BB3. /BBO                         "0B$_      !NB0rr0BQ!                          ,GBM/            zBBz      
               !BB0" .BBR` /BBO  _!}uf"-   ~>zu7"   :Tu*LgB$_     .uBBO  OBB* "JJ+_("rJJ_ .JJJ `=}u<_ .3RBQPL. `!luf+.  zBBz      
               !BBNV)8Bm;  /BBO 1gBGv0Bb; /QBAtBB8 ~%BMu3NB$_     .2BBO  .... tBB$Mg1`BBe._BB>`&BO(@M<.I8BQ4/.`RBMtQBR  zBBz      
               !BBQko#B8*. /BBO zBBz OBB! QBB''BBB !BB8'"0B$_     .2BBO       tBB%~   TBB'6BM'`RBM<;;  ,GBM/  `=1"!8BB  zBBz      
               !BB0"  BBB: /BBO zBBz OBB! BBB''BBB !BB8'"0B$_     .oBBO  lee! tBBo    `RBeBBi  ,e&Bg]` ,GBM/   !98uRBB  zBBz      
               !BB0" "BBB- /BBO zBBz OBB! gBB''BBB !BB8'"0B$_      ?BB8  OBB/ tBBo     rBBBg" .!!-<BB8 ,GBQ*  JBB~`mBB  zBBz      
               !BBBBQBQRT. /BBO "qB8AMQ4: "RB0YBgo 'wBB9aQB$_      ,ZQBQQBQ5~ tBBo     'PBBy  `EQM{BMA .yQBBQ'<QB8u9BB. zBBz      
               `_______.   `__~   _;^;_    `;;^;-   '_^;-__~.        ~;^^;~   '__-     ~XBMr   .~;^;:   ._;^;  ~;;,`__. -__-      
                                                                                      `8$q=                                       
          */
          
          
          contract BloodCrystal is ERC20, ERC20Burnable {
              constructor(
                  string memory name,
                  string memory symbol,
                  uint256 initialSupply,
                  address mintDestination
              ) ERC20(name, symbol) {
                  _mint(mintDestination, initialSupply);
              }
          }
          
          
          // File contracts/IPublicNFTStore.sol
          
          // Original license: SPDX_License_Identifier: MIT
          pragma solidity ^0.8.17;
          
          /// @notice Interface of {PublicNFTStore}
          interface IPublicNFTStore is INFTStore {
              /**
               *  @notice Emits when set a new max bought count per account.
               *  @param operator                             account that originated the contract call
               *  @param previousMaxBoughtCountPerAccount     previous max bought count per account
               *  @param newMaxBoughtCountPerAccount          new max bought count per account
               */
              event SetMaxBoughtCountPerAccount(
                  address indexed operator,
                  uint256 indexed previousMaxBoughtCountPerAccount,
                  uint256 indexed newMaxBoughtCountPerAccount
              );
          
              /// @notice Gets the max bought count per account
              function maxBoughtCountPerAccount() external returns (uint256);
          
              /**
               *  @notice Buys NFTs.
               *  @param recipient        NFT recipient address
               *  @param buyAmount        buy amount
               */
              function buy(address recipient, uint256 buyAmount) external payable;
          
              /**
               *  @notice Sets a new max bought count per account.
               *  @param maxBoughtCountPerAccount_        max bought count per account
               */
              function setMaxBoughtCountPerAccount(
                  uint256 maxBoughtCountPerAccount_
              ) external;
          }
          
          
          // File contracts/PublicNFTStore.sol
          
          // Original license: SPDX_License_Identifier: MIT
          pragma solidity ^0.8.17;
          
          
          
          
          /// @notice {PublicNFTStore} is public sale NFT store
          contract PublicNFTStore is
              UUPSUpgradeable,
              NFTStore,
              ReentrancyGuardUpgradeable,
              IPublicNFTStore
          {
              /// @notice max bought count per account
              uint256 private _maxBoughtCountPerAccount;
          
              /**
               *  @notice Initializes contract
               *  @param admin                        account that grant admin role to
               *  @param maxBoughtCountPerAccount_    max bought count per account
               *  @param nftPriceForDirect_           NFT price (for direct) [wei]
               *  @param nftPriceForPiement_          NFT price (for Piement) [wei]
               *  @param maxTotalBoughtCount_         max total bought count
               *  @param piementAddress_              Piement address
               *  @param nftStorage_                  NFT storage address
               *  @param nftContract_                 NFT contract
               *  @param activePeriod_                active period
               *  @dev
               *  Emits:
               *  - SetNFTPriceForDirect
               *  - SetNFTPriceForPiement
               *  - SetMaxTotalBoughtCount
               *  - SetPiementAddress
               *  - SetNFTStorage
               *  - SetMaxBoughtCountPerAccount
               */
              function initialize(
                  address admin,
                  uint256 maxBoughtCountPerAccount_,
                  uint256 nftPriceForDirect_,
                  uint256 nftPriceForPiement_,
                  uint256 maxTotalBoughtCount_,
                  address piementAddress_,
                  address nftStorage_,
                  IERC721Enumerable nftContract_,
                  ActivePeriod memory activePeriod_
              ) public initializer {
                  __UUPSUpgradeable_init();
                  __NFTStore_init(
                      nftPriceForDirect_,
                      nftPriceForPiement_,
                      maxTotalBoughtCount_,
                      piementAddress_,
                      nftStorage_,
                      nftContract_,
                      activePeriod_
                  );
                  __ReentrancyGuard_init();
          
                  _grantRole(DEFAULT_ADMIN_ROLE, admin);
                  _setMaxBoughtCountPerAccount(maxBoughtCountPerAccount_);
              }
          
              // --- public / external ---
          
              /// @notice Gets the max bought count per account
              function maxBoughtCountPerAccount()
                  external
                  view
                  override
                  returns (uint256)
              {
                  return _maxBoughtCountPerAccount;
              }
          
              /**
               *  @notice Buys NFTs.
               *  @param recipient        NFT recipient address
               *  @param buyAmount        buy amount
               *  @dev
               *  Requirements:
               *  - Buyer sends a price (nftPrice * buyAmount) in msg.value
               *
               *  Emits:
               *  - BoughtNFT: emits per NFTs
               *
               *  Reverts:
               *  - `buyAmount` is exceeded remaining bought count
               *  - store is inactive
               *  - `buyAmount` is not greater than 0
               *  - `buyAmount` is exceeded remaining count
               *  - `buyAmount` is exceeded remaining stock in nftStorage
               *  - msg.value is different to price (nftPrice * buyAmount)
               *  - NFT storage's NFTs is out of stock
               *  - recipient is zero address
               */
              function buy(
                  address recipient,
                  uint256 buyAmount
              ) external payable nonReentrant {
                  uint256 boughtCount = boughtCount(recipient);
                  require(
                      boughtCount + buyAmount <= _maxBoughtCountPerAccount,
                      "buyAmount exceeded remaining bought count"
                  );
          
                  _buy(recipient, buyAmount);
              }
          
              /**
               *  @notice Sets a new max bought count per account.
               *  @param maxBoughtCountPerAccount_        max bought count per account
               *  @dev
               *  Emits:
               *  - SetMaxBoughtCountPerAccount
               *
               *  Reverts:
               *  - `maxBoughtCountPerAccount_` is not greater than 0
               */
              function setMaxBoughtCountPerAccount(
                  uint256 maxBoughtCountPerAccount_
              ) external override onlyRole(DEFAULT_ADMIN_ROLE) {
                  _setMaxBoughtCountPerAccount(maxBoughtCountPerAccount_);
              }
          
              /**
               * @dev See {IERC165-supportsInterface}.
               */
              function supportsInterface(
                  bytes4 interfaceId
              ) public view virtual override returns (bool) {
                  return
                      interfaceId == type(IPublicNFTStore).interfaceId ||
                      super.supportsInterface(interfaceId);
              }
          
              // --- internal ---
          
              /**
               *  @notice Sets a new max bought count per account.
               *  @param maxBoughtCountPerAccount_        max bought count per account
               *  @dev
               *  Requirements:
               *  - the caller has `DEFAULT_ADMIN_ROLE` role
               *
               *  Emits:
               *  - SetMaxBoughtCountPerAccount
               *
               *  Reverts:
               *  - the caller does not have `DEFAULT_ADMIN_ROLE` role
               *  - `maxBoughtCountPerAccount_` is not greater than 0, not less than nor equal maxTotalBoughtCount
               */
              function _setMaxBoughtCountPerAccount(
                  uint256 maxBoughtCountPerAccount_
              ) internal {
                  require(
                      maxBoughtCountPerAccount_ > 0 &&
                          maxBoughtCountPerAccount_ <= maxTotalBoughtCount(),
                      "maxBoughtCountPerAccount must be grater than 0 and less than or equal maxTotalBoughtCount"
                  );
                  uint256 previousMaxBoughtCountPerAccount = _maxBoughtCountPerAccount;
                  _maxBoughtCountPerAccount = maxBoughtCountPerAccount_;
                  emit SetMaxBoughtCountPerAccount(
                      _msgSender(),
                      previousMaxBoughtCountPerAccount,
                      _maxBoughtCountPerAccount
                  );
              }
          
              /**
               *  @dev Function that should revert when `msg.sender` does not have `DEFAULT_ADMIN_ROLE` role to upgrade the contract.
               *  Called by {upgradeTo} and {upgradeToAndCall}.
               *
               *  Requirements:
               *  - the caller does not have `DEFAULT_ADMIN_ROLE` role.
               */
              function _authorizeUpgrade(
                  address newImplementation
              ) internal override onlyRole(DEFAULT_ADMIN_ROLE) {}
          
              /**
               * @dev This empty reserved space is put in place to allow future versions to add new
               * variables without shifting down storage in the inheritance chain.
               * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
               */
              uint256[50] private __gap;
          }
          
          
          // File contracts/test/HHTestERC721Token.sol
          
          // Original license: SPDX_License_Identifier: MIT
          pragma solidity ^0.8.17;
          
          contract HHTestERC721Token is ERC721Enumerable {
              constructor(
                  string memory name_,
                  string memory symbol_,
                  address initialSupplyTo,
                  uint256 initialSupply
              ) ERC721(name_, symbol_) {
                  for (uint256 tokenId = 0; tokenId < initialSupply; tokenId++) {
                      _safeMint(initialSupplyTo, tokenId);
                  }
              }
          
              function burn(uint256[] memory tokenIds) external {
                  uint256 tokenLength = tokenIds.length;
                  for (uint256 i = 0; i < tokenLength; i++) {
                      require(
                          ownerOf(tokenIds[i]) == _msgSender(),
                          "caller is not token owner"
                      );
                      _burn(tokenIds[i]);
                  }
              }
          }
          
          
          // File contracts/test/TestERC721Token.sol
          
          // Original license: SPDX_License_Identifier: MIT
          pragma solidity ^0.8.17;
          
          contract TestERC721Token is ERC721Enumerable {
              constructor(
                  string memory name_,
                  string memory symbol_,
                  address initialSupplyTo,
                  uint256 initialSupply
              ) ERC721(name_, symbol_) {
                  for (uint256 tokenId = 0; tokenId < initialSupply; tokenId++) {
                      _safeMint(initialSupplyTo, tokenId);
                  }
              }
          }

          File 4 of 6: RootChainManager
          pragma solidity 0.6.6;
          import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol";
          import {IRootChainManager} from "./IRootChainManager.sol";
          import {RootChainManagerStorage} from "./RootChainManagerStorage.sol";
          import {IStateSender} from "../StateSender/IStateSender.sol";
          import {ICheckpointManager} from "../ICheckpointManager.sol";
          import {RLPReader} from "../../lib/RLPReader.sol";
          import {ExitPayloadReader} from "../../lib/ExitPayloadReader.sol";
          import {MerklePatriciaProof} from "../../lib/MerklePatriciaProof.sol";
          import {Merkle} from "../../lib/Merkle.sol";
          import {ITokenPredicate} from "../TokenPredicates/ITokenPredicate.sol";
          import {Initializable} from "../../common/Initializable.sol";
          import {NativeMetaTransaction} from "../../common/NativeMetaTransaction.sol";
          import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
          import {AccessControlMixin} from "../../common/AccessControlMixin.sol";
          import {ContextMixin} from "../../common/ContextMixin.sol";
          contract RootChainManager is
              IRootChainManager,
              Initializable,
              AccessControl, // included to match old storage layout while upgrading
              RootChainManagerStorage, // created to match old storage layout while upgrading
              AccessControlMixin,
              NativeMetaTransaction,
              ContextMixin
          {
              using ExitPayloadReader for bytes;
              using ExitPayloadReader for ExitPayloadReader.ExitPayload;
              using ExitPayloadReader for ExitPayloadReader.Log;
              using ExitPayloadReader for ExitPayloadReader.Receipt;
              using Merkle for bytes32;
              using SafeMath for uint256;
              // maybe DEPOSIT and MAP_TOKEN can be reduced to bytes4
              bytes32 public constant DEPOSIT = keccak256("DEPOSIT");
              bytes32 public constant MAP_TOKEN = keccak256("MAP_TOKEN");
              address public constant ETHER_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
              bytes32 public constant MAPPER_ROLE = keccak256("MAPPER_ROLE");
              constructor() public {
                  // Disable initializer on implementation contract
                  _disableInitializer();
              }
              function _msgSender()
                  internal
                  override
                  view
                  returns (address payable sender)
              {
                  return ContextMixin.msgSender();
              }
              /**
               * @notice Deposit ether by directly sending to the contract
               * The account sending ether receives WETH on child chain
               */
              receive() external payable {
                  _depositEtherFor(_msgSender());
              }
              /**
               * @notice Initialize the contract after it has been proxified
               * @dev meant to be called once immediately after deployment
               * @param _owner the account that should be granted admin role
               */
              function initialize(
                  address _owner
              )
                  external
                  initializer
              {
                  _initializeEIP712("RootChainManager");
                  _setupContractId("RootChainManager");
                  _setupRole(DEFAULT_ADMIN_ROLE, _owner);
                  _setupRole(MAPPER_ROLE, _owner);
              }
              // adding seperate function setupContractId since initialize is already called with old implementation
              function setupContractId()
                  external
                  only(DEFAULT_ADMIN_ROLE)
              {
                  _setupContractId("RootChainManager");
              }
              // adding seperate function initializeEIP712 since initialize is already called with old implementation
              function initializeEIP712()
                  external
                  only(DEFAULT_ADMIN_ROLE)
              {
                  _setDomainSeperator("RootChainManager");
              }
              /**
               * @notice Set the state sender, callable only by admins
               * @dev This should be the state sender from plasma contracts
               * It is used to send bytes from root to child chain
               * @param newStateSender address of state sender contract
               */
              function setStateSender(address newStateSender)
                  external
                  only(DEFAULT_ADMIN_ROLE)
              {
                  require(newStateSender != address(0), "RootChainManager: BAD_NEW_STATE_SENDER");
                  _stateSender = IStateSender(newStateSender);
              }
              /**
               * @notice Get the address of contract set as state sender
               * @return The address of state sender contract
               */
              function stateSenderAddress() external view returns (address) {
                  return address(_stateSender);
              }
              /**
               * @notice Set the checkpoint manager, callable only by admins
               * @dev This should be the plasma contract responsible for keeping track of checkpoints
               * @param newCheckpointManager address of checkpoint manager contract
               */
              function setCheckpointManager(address newCheckpointManager)
                  external
                  only(DEFAULT_ADMIN_ROLE)
              {
                  require(newCheckpointManager != address(0), "RootChainManager: BAD_NEW_CHECKPOINT_MANAGER");
                  _checkpointManager = ICheckpointManager(newCheckpointManager);
              }
              /**
               * @notice Get the address of contract set as checkpoint manager
               * @return The address of checkpoint manager contract
               */
              function checkpointManagerAddress() external view returns (address) {
                  return address(_checkpointManager);
              }
              /**
               * @notice Set the child chain manager, callable only by admins
               * @dev This should be the contract responsible to receive deposit bytes on child chain
               * @param newChildChainManager address of child chain manager contract
               */
              function setChildChainManagerAddress(address newChildChainManager)
                  external
                  only(DEFAULT_ADMIN_ROLE)
              {
                  require(newChildChainManager != address(0x0), "RootChainManager: INVALID_CHILD_CHAIN_ADDRESS");
                  childChainManagerAddress = newChildChainManager;
              }
              /**
               * @notice Register a token predicate address against its type, callable only by ADMIN
               * @dev A predicate is a contract responsible to process the token specific logic while locking or exiting tokens
               * @param tokenType bytes32 unique identifier for the token type
               * @param predicateAddress address of token predicate address
               */
              function registerPredicate(bytes32 tokenType, address predicateAddress)
                  external
                  override
                  only(DEFAULT_ADMIN_ROLE)
              {
                  typeToPredicate[tokenType] = predicateAddress;
                  emit PredicateRegistered(tokenType, predicateAddress);
              }
              /**
               * @notice Map a token to enable its movement via the PoS Portal, callable only by mappers
               * @param rootToken address of token on root chain
               * @param childToken address of token on child chain
               * @param tokenType bytes32 unique identifier for the token type
               */
              function mapToken(
                  address rootToken,
                  address childToken,
                  bytes32 tokenType
              ) external override only(MAPPER_ROLE) {
                  // explicit check if token is already mapped to avoid accidental remaps
                  require(
                      rootToChildToken[rootToken] == address(0) &&
                      childToRootToken[childToken] == address(0),
                      "RootChainManager: ALREADY_MAPPED"
                  );
                  _mapToken(rootToken, childToken, tokenType);
              }
              /**
               * @notice Clean polluted token mapping
               * @param rootToken address of token on root chain. Since rename token was introduced later stage,
               * clean method is used to clean pollulated mapping
               */
              function cleanMapToken(
                  address rootToken,
                  address childToken
              ) external override only(DEFAULT_ADMIN_ROLE) {
                  rootToChildToken[rootToken] = address(0);
                  childToRootToken[childToken] = address(0);
                  tokenToType[rootToken] = bytes32(0);
                  emit TokenMapped(rootToken, childToken, tokenToType[rootToken]);
              }
              /**
               * @notice Remap a token that has already been mapped, properly cleans up old mapping
               * Callable only by ADMIN
               * @param rootToken address of token on root chain
               * @param childToken address of token on child chain
               * @param tokenType bytes32 unique identifier for the token type
               */
              function remapToken(
                  address rootToken,
                  address childToken,
                  bytes32 tokenType
              ) external override only(DEFAULT_ADMIN_ROLE) {
                  // cleanup old mapping
                  address oldChildToken = rootToChildToken[rootToken];
                  address oldRootToken = childToRootToken[childToken];
                  if (rootToChildToken[oldRootToken] != address(0)) {
                      rootToChildToken[oldRootToken] = address(0);
                      tokenToType[oldRootToken] = bytes32(0);
                  }
                  if (childToRootToken[oldChildToken] != address(0)) {
                      childToRootToken[oldChildToken] = address(0);
                  }
                  _mapToken(rootToken, childToken, tokenType);
              }
              function _mapToken(
                  address rootToken,
                  address childToken,
                  bytes32 tokenType
              ) private {
                  require(
                      typeToPredicate[tokenType] != address(0x0),
                      "RootChainManager: TOKEN_TYPE_NOT_SUPPORTED"
                  );
                  rootToChildToken[rootToken] = childToken;
                  childToRootToken[childToken] = rootToken;
                  tokenToType[rootToken] = tokenType;
                  emit TokenMapped(rootToken, childToken, tokenType);
                  bytes memory syncData = abi.encode(rootToken, childToken, tokenType);
                  _stateSender.syncState(
                      childChainManagerAddress,
                      abi.encode(MAP_TOKEN, syncData)
                  );
              }
              /**
               * @notice Move ether from root to child chain, accepts ether transfer
               * Keep in mind this ether cannot be used to pay gas on child chain
               * Use Matic tokens deposited using plasma mechanism for that
               * @param user address of account that should receive WETH on child chain
               */
              function depositEtherFor(address user) external override payable {
                  _depositEtherFor(user);
              }
              /**
               * @notice Move tokens from root to child chain
               * @dev This mechanism supports arbitrary tokens as long as its predicate has been registered and the token is mapped
               * @param user address of account that should receive this deposit on child chain
               * @param rootToken address of token that is being deposited
               * @param depositData bytes data that is sent to predicate and child token contracts to handle deposit
               */
              function depositFor(
                  address user,
                  address rootToken,
                  bytes calldata depositData
              ) external override {
                  require(
                      rootToken != ETHER_ADDRESS,
                      "RootChainManager: INVALID_ROOT_TOKEN"
                  );
                  _depositFor(user, rootToken, depositData);
              }
              function _depositEtherFor(address user) private {
                  bytes memory depositData = abi.encode(msg.value);
                  _depositFor(user, ETHER_ADDRESS, depositData);
                  // payable(typeToPredicate[tokenToType[ETHER_ADDRESS]]).transfer(msg.value);
                  // transfer doesn't work as expected when receiving contract is proxified so using call
                  (bool success, /* bytes memory data */) = typeToPredicate[tokenToType[ETHER_ADDRESS]].call{value: msg.value}("");
                  if (!success) {
                      revert("RootChainManager: ETHER_TRANSFER_FAILED");
                  }
              }
              function _depositFor(
                  address user,
                  address rootToken,
                  bytes memory depositData
              ) private {
                  bytes32 tokenType = tokenToType[rootToken];
                  require(
                      rootToChildToken[rootToken] != address(0x0) &&
                         tokenType != 0,
                      "RootChainManager: TOKEN_NOT_MAPPED"
                  );
                  address predicateAddress = typeToPredicate[tokenType];
                  require(
                      predicateAddress != address(0),
                      "RootChainManager: INVALID_TOKEN_TYPE"
                  );
                  require(
                      user != address(0),
                      "RootChainManager: INVALID_USER"
                  );
                  ITokenPredicate(predicateAddress).lockTokens(
                      _msgSender(),
                      user,
                      rootToken,
                      depositData
                  );
                  bytes memory syncData = abi.encode(user, rootToken, depositData);
                  _stateSender.syncState(
                      childChainManagerAddress,
                      abi.encode(DEPOSIT, syncData)
                  );
              }
              /**
               * @notice exit tokens by providing proof
               * @dev This function verifies if the transaction actually happened on child chain
               * the transaction log is then sent to token predicate to handle it accordingly
               *
               * @param inputData RLP encoded data of the reference tx containing following list of fields
               *  0 - headerNumber - Checkpoint header block number containing the reference tx
               *  1 - blockProof - Proof that the block header (in the child chain) is a leaf in the submitted merkle root
               *  2 - blockNumber - Block number containing the reference tx on child chain
               *  3 - blockTime - Reference tx block time
               *  4 - txRoot - Transactions root of block
               *  5 - receiptRoot - Receipts root of block
               *  6 - receipt - Receipt of the reference transaction
               *  7 - receiptProof - Merkle proof of the reference receipt
               *  8 - branchMask - 32 bits denoting the path of receipt in merkle tree
               *  9 - receiptLogIndex - Log Index to read from the receipt
               */
              function exit(bytes calldata inputData) external override {
                  ExitPayloadReader.ExitPayload memory payload = inputData.toExitPayload();
                  bytes memory branchMaskBytes = payload.getBranchMaskAsBytes();
                  // checking if exit has already been processed
                  // unique exit is identified using hash of (blockNumber, branchMask, receiptLogIndex)
                  bytes32 exitHash = keccak256(
                      abi.encodePacked(
                          payload.getBlockNumber(),
                          // first 2 nibbles are dropped while generating nibble array
                          // this allows branch masks that are valid but bypass exitHash check (changing first 2 nibbles only)
                          // so converting to nibble array and then hashing it
                          MerklePatriciaProof._getNibbleArray(branchMaskBytes),
                          payload.getReceiptLogIndex()
                      )
                  );
                  require(
                      processedExits[exitHash] == false,
                      "RootChainManager: EXIT_ALREADY_PROCESSED"
                  );
                  processedExits[exitHash] = true;
                  ExitPayloadReader.Receipt memory receipt = payload.getReceipt();
                  ExitPayloadReader.Log memory log = receipt.getLog();
                  // log should be emmited only by the child token
                  address rootToken = childToRootToken[log.getEmitter()];
                  require(
                      rootToken != address(0),
                      "RootChainManager: TOKEN_NOT_MAPPED"
                  );
                  address predicateAddress = typeToPredicate[
                      tokenToType[rootToken]
                  ];
                  // branch mask can be maximum 32 bits
                  require(
                      payload.getBranchMaskAsUint() &
                      0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000 ==
                      0,
                      "RootChainManager: INVALID_BRANCH_MASK"
                  );
                  // verify receipt inclusion
                  require(
                      MerklePatriciaProof.verify(
                          receipt.toBytes(),
                          branchMaskBytes,
                          payload.getReceiptProof(),
                          payload.getReceiptRoot()
                      ),
                      "RootChainManager: INVALID_PROOF"
                  );
                  // verify checkpoint inclusion
                  _checkBlockMembershipInCheckpoint(
                      payload.getBlockNumber(),
                      payload.getBlockTime(),
                      payload.getTxRoot(),
                      payload.getReceiptRoot(),
                      payload.getHeaderNumber(),
                      payload.getBlockProof()
                  );
                  ITokenPredicate(predicateAddress).exitTokens(
                      _msgSender(),
                      rootToken,
                      log.toRlpBytes()
                  );
              }
              function _checkBlockMembershipInCheckpoint(
                  uint256 blockNumber,
                  uint256 blockTime,
                  bytes32 txRoot,
                  bytes32 receiptRoot,
                  uint256 headerNumber,
                  bytes memory blockProof
              ) private view {
                  (
                      bytes32 headerRoot,
                      uint256 startBlock,
                      ,
                      ,
                  ) = _checkpointManager.headerBlocks(headerNumber);
                  require(
                      keccak256(
                          abi.encodePacked(blockNumber, blockTime, txRoot, receiptRoot)
                      )
                          .checkMembership(
                          blockNumber.sub(startBlock),
                          headerRoot,
                          blockProof
                      ),
                      "RootChainManager: INVALID_HEADER"
                  );
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.0;
          /**
           * @dev Wrappers over Solidity's arithmetic operations with added overflow
           * checks.
           *
           * Arithmetic operations in Solidity wrap on overflow. This can easily result
           * in bugs, because programmers usually assume that an overflow raises an
           * error, which is the standard behavior in high level programming languages.
           * `SafeMath` restores this intuition by reverting the transaction when an
           * operation overflows.
           *
           * Using this library instead of the unchecked operations eliminates an entire
           * class of bugs, so it's recommended to use it always.
           */
          library SafeMath {
              /**
               * @dev Returns the addition of two unsigned integers, reverting on
               * overflow.
               *
               * Counterpart to Solidity's `+` operator.
               *
               * Requirements:
               *
               * - Addition cannot overflow.
               */
              function add(uint256 a, uint256 b) internal pure returns (uint256) {
                  uint256 c = a + b;
                  require(c >= a, "SafeMath: addition overflow");
                  return c;
              }
              /**
               * @dev Returns the subtraction of two unsigned integers, reverting on
               * overflow (when the result is negative).
               *
               * Counterpart to Solidity's `-` operator.
               *
               * Requirements:
               *
               * - Subtraction cannot overflow.
               */
              function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                  return sub(a, b, "SafeMath: subtraction overflow");
              }
              /**
               * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
               * overflow (when the result is negative).
               *
               * Counterpart to Solidity's `-` operator.
               *
               * Requirements:
               *
               * - Subtraction cannot overflow.
               */
              function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                  require(b <= a, errorMessage);
                  uint256 c = a - b;
                  return c;
              }
              /**
               * @dev Returns the multiplication of two unsigned integers, reverting on
               * overflow.
               *
               * Counterpart to Solidity's `*` operator.
               *
               * Requirements:
               *
               * - Multiplication cannot overflow.
               */
              function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                  // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                  // benefit is lost if 'b' is also tested.
                  // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                  if (a == 0) {
                      return 0;
                  }
                  uint256 c = a * b;
                  require(c / a == b, "SafeMath: multiplication overflow");
                  return c;
              }
              /**
               * @dev Returns the integer division of two unsigned integers. Reverts on
               * division by zero. The result is rounded towards zero.
               *
               * Counterpart to Solidity's `/` operator. Note: this function uses a
               * `revert` opcode (which leaves remaining gas untouched) while Solidity
               * uses an invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               *
               * - The divisor cannot be zero.
               */
              function div(uint256 a, uint256 b) internal pure returns (uint256) {
                  return div(a, b, "SafeMath: division by zero");
              }
              /**
               * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
               * division by zero. The result is rounded towards zero.
               *
               * Counterpart to Solidity's `/` operator. Note: this function uses a
               * `revert` opcode (which leaves remaining gas untouched) while Solidity
               * uses an invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               *
               * - The divisor cannot be zero.
               */
              function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                  require(b > 0, errorMessage);
                  uint256 c = a / b;
                  // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                  return c;
              }
              /**
               * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
               * Reverts when dividing by zero.
               *
               * Counterpart to Solidity's `%` operator. This function uses a `revert`
               * opcode (which leaves remaining gas untouched) while Solidity uses an
               * invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               *
               * - The divisor cannot be zero.
               */
              function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                  return mod(a, b, "SafeMath: modulo by zero");
              }
              /**
               * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
               * Reverts with custom message when dividing by zero.
               *
               * Counterpart to Solidity's `%` operator. This function uses a `revert`
               * opcode (which leaves remaining gas untouched) while Solidity uses an
               * invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               *
               * - The divisor cannot be zero.
               */
              function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                  require(b != 0, errorMessage);
                  return a % b;
              }
          }
          pragma solidity 0.6.6;
          interface IRootChainManager {
              event TokenMapped(
                  address indexed rootToken,
                  address indexed childToken,
                  bytes32 indexed tokenType
              );
              event PredicateRegistered(
                  bytes32 indexed tokenType,
                  address indexed predicateAddress
              );
              function registerPredicate(bytes32 tokenType, address predicateAddress)
                  external;
              function mapToken(
                  address rootToken,
                  address childToken,
                  bytes32 tokenType
              ) external;
              function cleanMapToken(
                  address rootToken,
                  address childToken
              ) external;
              function remapToken(
                  address rootToken,
                  address childToken,
                  bytes32 tokenType
              ) external;
              function depositEtherFor(address user) external payable;
              function depositFor(
                  address user,
                  address rootToken,
                  bytes calldata depositData
              ) external;
              function exit(bytes calldata inputData) external;
          }
          pragma solidity 0.6.6;
          import {IStateSender} from "../StateSender/IStateSender.sol";
          import {ICheckpointManager} from "../ICheckpointManager.sol";
          abstract contract RootChainManagerStorage {
              mapping(bytes32 => address) public typeToPredicate;
              mapping(address => address) public rootToChildToken;
              mapping(address => address) public childToRootToken;
              mapping(address => bytes32) public tokenToType;
              mapping(bytes32 => bool) public processedExits;
              IStateSender internal _stateSender;
              ICheckpointManager internal _checkpointManager;
              address public childChainManagerAddress;
          }
          pragma solidity 0.6.6;
          interface IStateSender {
              function syncState(address receiver, bytes calldata data) external;
          }
          pragma solidity 0.6.6;
          contract ICheckpointManager {
              struct HeaderBlock {
                  bytes32 root;
                  uint256 start;
                  uint256 end;
                  uint256 createdAt;
                  address proposer;
              }
              /**
               * @notice mapping of checkpoint header numbers to block details
               * @dev These checkpoints are submited by plasma contracts
               */
              mapping(uint256 => HeaderBlock) public headerBlocks;
          }
          /*
           * @author Hamdi Allam [email protected]
           * Please reach out with any questions or concerns
           * https://github.com/hamdiallam/Solidity-RLP/blob/e681e25a376dbd5426b509380bc03446f05d0f97/contracts/RLPReader.sol
           */
          pragma solidity 0.6.6;
          library RLPReader {
              uint8 constant STRING_SHORT_START = 0x80;
              uint8 constant STRING_LONG_START  = 0xb8;
              uint8 constant LIST_SHORT_START   = 0xc0;
              uint8 constant LIST_LONG_START    = 0xf8;
              uint8 constant WORD_SIZE = 32;
              struct RLPItem {
                  uint len;
                  uint memPtr;
              }
              struct Iterator {
                  RLPItem item;   // Item that's being iterated over.
                  uint nextPtr;   // Position of the next item in the list.
              }
              /*
              * @dev Returns the next element in the iteration. Reverts if it has not next element.
              * @param self The iterator.
              * @return The next element in the iteration.
              */
              function next(Iterator memory self) internal pure returns (RLPItem memory) {
                  require(hasNext(self));
                  uint ptr = self.nextPtr;
                  uint itemLength = _itemLength(ptr);
                  self.nextPtr = ptr + itemLength;
                  return RLPItem(itemLength, ptr);
              }
              /*
              * @dev Returns true if the iteration has more elements.
              * @param self The iterator.
              * @return true if the iteration has more elements.
              */
              function hasNext(Iterator memory self) internal pure returns (bool) {
                  RLPItem memory item = self.item;
                  return self.nextPtr < item.memPtr + item.len;
              }
              /*
              * @param item RLP encoded bytes
              */
              function toRlpItem(bytes memory item) internal pure returns (RLPItem memory) {
                  uint memPtr;
                  assembly {
                      memPtr := add(item, 0x20)
                  }
                  return RLPItem(item.length, memPtr);
              }
              /*
              * @dev Create an iterator. Reverts if item is not a list.
              * @param self The RLP item.
              * @return An 'Iterator' over the item.
              */
              function iterator(RLPItem memory self) internal pure returns (Iterator memory) {
                  require(isList(self));
                  uint ptr = self.memPtr + _payloadOffset(self.memPtr);
                  return Iterator(self, ptr);
              }
              /*
              * @param the RLP item.
              */
              function rlpLen(RLPItem memory item) internal pure returns (uint) {
                  return item.len;
              }
              /*
               * @param the RLP item.
               * @return (memPtr, len) pair: location of the item's payload in memory.
               */
              function payloadLocation(RLPItem memory item) internal pure returns (uint, uint) {
                  uint offset = _payloadOffset(item.memPtr);
                  uint memPtr = item.memPtr + offset;
                  uint len = item.len - offset; // data length
                  return (memPtr, len);
              }
              /*
              * @param the RLP item.
              */
              function payloadLen(RLPItem memory item) internal pure returns (uint) {
                  (, uint len) = payloadLocation(item);
                  return len;
              }
              /*
              * @param the RLP item containing the encoded list.
              */
              function toList(RLPItem memory item) internal pure returns (RLPItem[] memory) {
                  require(isList(item));
                  uint items = numItems(item);
                  RLPItem[] memory result = new RLPItem[](items);
                  uint memPtr = item.memPtr + _payloadOffset(item.memPtr);
                  uint dataLen;
                  for (uint i = 0; i < items; i++) {
                      dataLen = _itemLength(memPtr);
                      result[i] = RLPItem(dataLen, memPtr); 
                      memPtr = memPtr + dataLen;
                  }
                  require(memPtr - item.memPtr == item.len, "Wrong total length.");
                  return result;
              }
              // @return indicator whether encoded payload is a list. negate this function call for isData.
              function isList(RLPItem memory item) internal pure returns (bool) {
                  if (item.len == 0) return false;
                  uint8 byte0;
                  uint memPtr = item.memPtr;
                  assembly {
                      byte0 := byte(0, mload(memPtr))
                  }
                  if (byte0 < LIST_SHORT_START)
                      return false;
                  return true;
              }
              /*
               * @dev A cheaper version of keccak256(toRlpBytes(item)) that avoids copying memory.
               * @return keccak256 hash of RLP encoded bytes.
               */
              function rlpBytesKeccak256(RLPItem memory item) internal pure returns (bytes32) {
                  uint256 ptr = item.memPtr;
                  uint256 len = item.len;
                  bytes32 result;
                  assembly {
                      result := keccak256(ptr, len)
                  }
                  return result;
              }
              /*
               * @dev A cheaper version of keccak256(toBytes(item)) that avoids copying memory.
               * @return keccak256 hash of the item payload.
               */
              function payloadKeccak256(RLPItem memory item) internal pure returns (bytes32) {
                  (uint memPtr, uint len) = payloadLocation(item);
                  bytes32 result;
                  assembly {
                      result := keccak256(memPtr, len)
                  }
                  return result;
              }
              /** RLPItem conversions into data types **/
              // @returns raw rlp encoding in bytes
              function toRlpBytes(RLPItem memory item) internal pure returns (bytes memory) {
                  bytes memory result = new bytes(item.len);
                  if (result.length == 0) return result;
                  
                  uint ptr;
                  assembly {
                      ptr := add(0x20, result)
                  }
                  copy(item.memPtr, ptr, item.len);
                  return result;
              }
              // any non-zero byte except "0x80" is considered true
              function toBoolean(RLPItem memory item) internal pure returns (bool) {
                  require(item.len == 1);
                  uint result;
                  uint memPtr = item.memPtr;
                  assembly {
                      result := byte(0, mload(memPtr))
                  }
                  // SEE Github Issue #5.
                  // Summary: Most commonly used RLP libraries (i.e Geth) will encode
                  // "0" as "0x80" instead of as "0". We handle this edge case explicitly
                  // here.
                  if (result == 0 || result == STRING_SHORT_START) {
                      return false;
                  } else {
                      return true;
                  }
              }
              function toAddress(RLPItem memory item) internal pure returns (address) {
                  // 1 byte for the length prefix
                  require(item.len == 21);
                  return address(toUint(item));
              }
              function toUint(RLPItem memory item) internal pure returns (uint) {
                  require(item.len > 0 && item.len <= 33);
                  (uint memPtr, uint len) = payloadLocation(item);
                  uint result;
                  assembly {
                      result := mload(memPtr)
                      // shfit to the correct location if neccesary
                      if lt(len, 32) {
                          result := div(result, exp(256, sub(32, len)))
                      }
                  }
                  return result;
              }
              // enforces 32 byte length
              function toUintStrict(RLPItem memory item) internal pure returns (uint) {
                  // one byte prefix
                  require(item.len == 33);
                  uint result;
                  uint memPtr = item.memPtr + 1;
                  assembly {
                      result := mload(memPtr)
                  }
                  return result;
              }
              function toBytes(RLPItem memory item) internal pure returns (bytes memory) {
                  require(item.len > 0);
                  (uint memPtr, uint len) = payloadLocation(item);
                  bytes memory result = new bytes(len);
                  uint destPtr;
                  assembly {
                      destPtr := add(0x20, result)
                  }
                  copy(memPtr, destPtr, len);
                  return result;
              }
              /*
              * Private Helpers
              */
              // @return number of payload items inside an encoded list.
              function numItems(RLPItem memory item) private pure returns (uint) {
                  if (item.len == 0) return 0;
                  uint count = 0;
                  uint currPtr = item.memPtr + _payloadOffset(item.memPtr);
                  uint endPtr = item.memPtr + item.len;
                  while (currPtr < endPtr) {
                     currPtr = currPtr + _itemLength(currPtr); // skip over an item
                     count++;
                  }
                  return count;
              }
              // @return entire rlp item byte length
              function _itemLength(uint memPtr) private pure returns (uint) {
                  uint itemLen;
                  uint byte0;
                  assembly {
                      byte0 := byte(0, mload(memPtr))
                  }
                  if (byte0 < STRING_SHORT_START)
                      itemLen = 1;
                  
                  else if (byte0 < STRING_LONG_START)
                      itemLen = byte0 - STRING_SHORT_START + 1;
                  else if (byte0 < LIST_SHORT_START) {
                      assembly {
                          let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is
                          memPtr := add(memPtr, 1) // skip over the first byte
                          
                          /* 32 byte word size */
                          let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to get the len
                          itemLen := add(dataLen, add(byteLen, 1))
                      }
                  }
                  else if (byte0 < LIST_LONG_START) {
                      itemLen = byte0 - LIST_SHORT_START + 1;
                  } 
                  else {
                      assembly {
                          let byteLen := sub(byte0, 0xf7)
                          memPtr := add(memPtr, 1)
                          let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to the correct length
                          itemLen := add(dataLen, add(byteLen, 1))
                      }
                  }
                  return itemLen;
              }
              // @return number of bytes until the data
              function _payloadOffset(uint memPtr) private pure returns (uint) {
                  uint byte0;
                  assembly {
                      byte0 := byte(0, mload(memPtr))
                  }
                  if (byte0 < STRING_SHORT_START) 
                      return 0;
                  else if (byte0 < STRING_LONG_START || (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START))
                      return 1;
                  else if (byte0 < LIST_SHORT_START)  // being explicit
                      return byte0 - (STRING_LONG_START - 1) + 1;
                  else
                      return byte0 - (LIST_LONG_START - 1) + 1;
              }
              /*
              * @param src Pointer to source
              * @param dest Pointer to destination
              * @param len Amount of memory to copy from the source
              */
              function copy(uint src, uint dest, uint len) private pure {
                  if (len == 0) return;
                  // copy as many word sizes as possible
                  for (; len >= WORD_SIZE; len -= WORD_SIZE) {
                      assembly {
                          mstore(dest, mload(src))
                      }
                      src += WORD_SIZE;
                      dest += WORD_SIZE;
                  }
                  if (len > 0) {
                      // left over bytes. Mask is used to remove unwanted bytes from the word
                      uint mask = 256 ** (WORD_SIZE - len) - 1;
                      assembly {
                          let srcpart := and(mload(src), not(mask)) // zero out src
                          let destpart := and(mload(dest), mask) // retrieve the bytes
                          mstore(dest, or(destpart, srcpart))
                      }
                  }
              }
          }
          pragma solidity 0.6.6;
          import { RLPReader } from "./RLPReader.sol";
          library ExitPayloadReader {
            using RLPReader for bytes;
            using RLPReader for RLPReader.RLPItem;
            uint8 constant WORD_SIZE = 32;
            struct ExitPayload {
              RLPReader.RLPItem[] data;
            }
            struct Receipt {
              RLPReader.RLPItem[] data;
              bytes raw;
              uint256 logIndex;
            }
            struct Log {
              RLPReader.RLPItem data;
              RLPReader.RLPItem[] list;
            }
            struct LogTopics {
              RLPReader.RLPItem[] data;
            }
            // copy paste of private copy() from RLPReader to avoid changing of existing contracts
            function copy(uint src, uint dest, uint len) private pure {
                  if (len == 0) return;
                  // copy as many word sizes as possible
                  for (; len >= WORD_SIZE; len -= WORD_SIZE) {
                      assembly {
                          mstore(dest, mload(src))
                      }
                      src += WORD_SIZE;
                      dest += WORD_SIZE;
                  }
                  // left over bytes. Mask is used to remove unwanted bytes from the word
                  uint mask = 256 ** (WORD_SIZE - len) - 1;
                  assembly {
                      let srcpart := and(mload(src), not(mask)) // zero out src
                      let destpart := and(mload(dest), mask) // retrieve the bytes
                      mstore(dest, or(destpart, srcpart))
                  }
              }
            function toExitPayload(bytes memory data)
                  internal
                  pure
                  returns (ExitPayload memory)
              {
                  RLPReader.RLPItem[] memory payloadData = data
                      .toRlpItem()
                      .toList();
                  return ExitPayload(payloadData);
              }
              function getHeaderNumber(ExitPayload memory payload) internal pure returns(uint256) {
                return payload.data[0].toUint();
              }
              function getBlockProof(ExitPayload memory payload) internal pure returns(bytes memory) {
                return payload.data[1].toBytes();
              }
              function getBlockNumber(ExitPayload memory payload) internal pure returns(uint256) {
                return payload.data[2].toUint();
              }
              function getBlockTime(ExitPayload memory payload) internal pure returns(uint256) {
                return payload.data[3].toUint();
              }
              function getTxRoot(ExitPayload memory payload) internal pure returns(bytes32) {
                return bytes32(payload.data[4].toUint());
              }
              function getReceiptRoot(ExitPayload memory payload) internal pure returns(bytes32) {
                return bytes32(payload.data[5].toUint());
              }
              function getReceipt(ExitPayload memory payload) internal pure returns(Receipt memory receipt) {
                receipt.raw = payload.data[6].toBytes();
                RLPReader.RLPItem memory receiptItem = receipt.raw.toRlpItem();
                if (receiptItem.isList()) {
                    // legacy tx
                    receipt.data = receiptItem.toList();
                } else {
                    // pop first byte before parsting receipt
                    bytes memory typedBytes = receipt.raw;
                    bytes memory result = new bytes(typedBytes.length - 1);
                    uint256 srcPtr;
                    uint256 destPtr;
                    assembly {
                        srcPtr := add(33, typedBytes)
                        destPtr := add(0x20, result)
                    }
                    copy(srcPtr, destPtr, result.length);
                    receipt.data = result.toRlpItem().toList();
                }
                receipt.logIndex = getReceiptLogIndex(payload);
                return receipt;
              }
              function getReceiptProof(ExitPayload memory payload) internal pure returns(bytes memory) {
                return payload.data[7].toBytes();
              }
              function getBranchMaskAsBytes(ExitPayload memory payload) internal pure returns(bytes memory) {
                return payload.data[8].toBytes();
              }
              function getBranchMaskAsUint(ExitPayload memory payload) internal pure returns(uint256) {
                return payload.data[8].toUint();
              }
              function getReceiptLogIndex(ExitPayload memory payload) internal pure returns(uint256) {
                return payload.data[9].toUint();
              }
              
              // Receipt methods
              function toBytes(Receipt memory receipt) internal pure returns(bytes memory) {
                  return receipt.raw;
              }
              function getLog(Receipt memory receipt) internal pure returns(Log memory) {
                  RLPReader.RLPItem memory logData = receipt.data[3].toList()[receipt.logIndex];
                  return Log(logData, logData.toList());
              }
              // Log methods
              function getEmitter(Log memory log) internal pure returns(address) {
                return RLPReader.toAddress(log.list[0]);
              }
              function getTopics(Log memory log) internal pure returns(LogTopics memory) {
                  return LogTopics(log.list[1].toList());
              }
              function getData(Log memory log) internal pure returns(bytes memory) {
                  return log.list[2].toBytes();
              }
              function toRlpBytes(Log memory log) internal pure returns(bytes memory) {
                return log.data.toRlpBytes();
              }
              // LogTopics methods
              function getField(LogTopics memory topics, uint256 index) internal pure returns(RLPReader.RLPItem memory) {
                return topics.data[index];
              }
          }
          /*
           * @title MerklePatriciaVerifier
           * @author Sam Mayo ([email protected])
           *
           * @dev Library for verifing merkle patricia proofs.
           */
          pragma solidity 0.6.6;
          import {RLPReader} from "./RLPReader.sol";
          library MerklePatriciaProof {
              /*
               * @dev Verifies a merkle patricia proof.
               * @param value The terminating value in the trie.
               * @param encodedPath The path in the trie leading to value.
               * @param rlpParentNodes The rlp encoded stack of nodes.
               * @param root The root hash of the trie.
               * @return The boolean validity of the proof.
               */
              function verify(
                  bytes memory value,
                  bytes memory encodedPath,
                  bytes memory rlpParentNodes,
                  bytes32 root
              ) internal pure returns (bool) {
                  RLPReader.RLPItem memory item = RLPReader.toRlpItem(rlpParentNodes);
                  RLPReader.RLPItem[] memory parentNodes = RLPReader.toList(item);
                  bytes memory currentNode;
                  RLPReader.RLPItem[] memory currentNodeList;
                  bytes32 nodeKey = root;
                  uint256 pathPtr = 0;
                  bytes memory path = _getNibbleArray(encodedPath);
                  if (path.length == 0) {
                      return false;
                  }
                  for (uint256 i = 0; i < parentNodes.length; i++) {
                      if (pathPtr > path.length) {
                          return false;
                      }
                      currentNode = RLPReader.toRlpBytes(parentNodes[i]);
                      if (nodeKey != keccak256(currentNode)) {
                          return false;
                      }
                      currentNodeList = RLPReader.toList(parentNodes[i]);
                      if (currentNodeList.length == 17) {
                          if (pathPtr == path.length) {
                              if (
                                  keccak256(RLPReader.toBytes(currentNodeList[16])) ==
                                  keccak256(value)
                              ) {
                                  return true;
                              } else {
                                  return false;
                              }
                          }
                          uint8 nextPathNibble = uint8(path[pathPtr]);
                          if (nextPathNibble > 16) {
                              return false;
                          }
                          nodeKey = bytes32(
                              RLPReader.toUintStrict(currentNodeList[nextPathNibble])
                          );
                          pathPtr += 1;
                      } else if (currentNodeList.length == 2) {
                          bytes memory nodeValue = RLPReader.toBytes(currentNodeList[0]);
                          uint256 traversed = _nibblesToTraverse(
                              nodeValue,
                              path,
                              pathPtr
                          );
                          //enforce correct nibble
                          bytes1 prefix = _getNthNibbleOfBytes(0, nodeValue);
                          if (pathPtr + traversed == path.length) {
                              //leaf node
                              if (
                                  keccak256(RLPReader.toBytes(currentNodeList[1])) == keccak256(value) && 
                                  (prefix == bytes1(uint8(2)) || prefix == bytes1(uint8(3)))
                              ) {
                                  return true;
                              } else {
                                  return false;
                              }
                          }
                          //extension node
                          if (traversed == 0 || (prefix != bytes1(uint8(0)) && prefix != bytes1(uint8(1)))) {
                              return false;
                          }
                          pathPtr += traversed;
                          nodeKey = bytes32(RLPReader.toUintStrict(currentNodeList[1]));
                      } else {
                          return false;
                      }
                  }
                  return false; // default
              }
              function _nibblesToTraverse(
                  bytes memory encodedPartialPath,
                  bytes memory path,
                  uint256 pathPtr
              ) private pure returns (uint256) {
                  uint256 len = 0;
                  // encodedPartialPath has elements that are each two hex characters (1 byte), but partialPath
                  // and slicedPath have elements that are each one hex character (1 nibble)
                  bytes memory partialPath = _getNibbleArray(encodedPartialPath);
                  bytes memory slicedPath = new bytes(partialPath.length);
                  // pathPtr counts nibbles in path
                  // partialPath.length is a number of nibbles
                  for (uint256 i = pathPtr; i < pathPtr + partialPath.length; i++) {
                      bytes1 pathNibble = path[i];
                      slicedPath[i - pathPtr] = pathNibble;
                  }
                  if (keccak256(partialPath) == keccak256(slicedPath)) {
                      len = partialPath.length;
                  } else {
                      len = 0;
                  }
                  return len;
              }
              // bytes b must be hp encoded
              function _getNibbleArray(bytes memory b)
                  internal
                  pure
                  returns (bytes memory)
              {
                  bytes memory nibbles = "";
                  if (b.length > 0) {
                      uint8 offset;
                      uint8 hpNibble = uint8(_getNthNibbleOfBytes(0, b));
                      if (hpNibble == 1 || hpNibble == 3) {
                          nibbles = new bytes(b.length * 2 - 1);
                          bytes1 oddNibble = _getNthNibbleOfBytes(1, b);
                          nibbles[0] = oddNibble;
                          offset = 1;
                      } else {
                          nibbles = new bytes(b.length * 2 - 2);
                          offset = 0;
                      }
                      for (uint256 i = offset; i < nibbles.length; i++) {
                          nibbles[i] = _getNthNibbleOfBytes(i - offset + 2, b);
                      }
                  }
                  return nibbles;
              }
              function _getNthNibbleOfBytes(uint256 n, bytes memory str)
                  private
                  pure
                  returns (bytes1)
              {
                  return
                      bytes1(
                          n % 2 == 0 ? uint8(str[n / 2]) / 0x10 : uint8(str[n / 2]) % 0x10
                      );
              }
          }
          pragma solidity 0.6.6;
          library Merkle {
              function checkMembership(
                  bytes32 leaf,
                  uint256 index,
                  bytes32 rootHash,
                  bytes memory proof
              ) internal pure returns (bool) {
                  require(proof.length % 32 == 0, "Invalid proof length");
                  uint256 proofHeight = proof.length / 32;
                  // Proof of size n means, height of the tree is n+1.
                  // In a tree of height n+1, max #leafs possible is 2 ^ n
                  require(index < 2 ** proofHeight, "Leaf index is too big");
                  bytes32 proofElement;
                  bytes32 computedHash = leaf;
                  for (uint256 i = 32; i <= proof.length; i += 32) {
                      assembly {
                          proofElement := mload(add(proof, i))
                      }
                      if (index % 2 == 0) {
                          computedHash = keccak256(
                              abi.encodePacked(computedHash, proofElement)
                          );
                      } else {
                          computedHash = keccak256(
                              abi.encodePacked(proofElement, computedHash)
                          );
                      }
                      index = index / 2;
                  }
                  return computedHash == rootHash;
              }
          }
          pragma solidity 0.6.6;
          import {RLPReader} from "../../lib/RLPReader.sol";
          /// @title Token predicate interface for all pos portal predicates
          /// @notice Abstract interface that defines methods for custom predicates
          interface ITokenPredicate {
              /**
               * @notice Deposit tokens into pos portal
               * @dev When `depositor` deposits tokens into pos portal, tokens get locked into predicate contract.
               * @param depositor Address who wants to deposit tokens
               * @param depositReceiver Address (address) who wants to receive tokens on side chain
               * @param rootToken Token which gets deposited
               * @param depositData Extra data for deposit (amount for ERC20, token id for ERC721 etc.) [ABI encoded]
               */
              function lockTokens(
                  address depositor,
                  address depositReceiver,
                  address rootToken,
                  bytes calldata depositData
              ) external;
              /**
               * @notice Validates and processes exit while withdraw process
               * @dev Validates exit log emitted on sidechain. Reverts if validation fails.
               * @dev Processes withdraw based on custom logic. Example: transfer ERC20/ERC721, mint ERC721 if mintable withdraw
               * @param sender unused for polygon predicates, being kept for abi compatability
               * @param rootToken Token which gets withdrawn
               * @param logRLPList Valid sidechain log for data like amount, token id etc.
               */
              function exitTokens(
                  address sender,
                  address rootToken,
                  bytes calldata logRLPList
              ) external;
          }
          pragma solidity 0.6.6;
          contract Initializable {
              bool inited = false;
              modifier initializer() {
                  require(!inited, "already inited");
                  _;
                  inited = true;
              }
              function _disableInitializer() internal {
                  inited = true;
              }
          }
          pragma solidity 0.6.6;
          /**
           * @notice DISCLAIMER:
           * Do not use NativeMetaTransaction and ContextMixin together with OpenZeppelin's "multicall"
           * nor any other form of self delegatecall!
           * Risk of address spoofing attacks.
           * Read more: https://blog.openzeppelin.com/arbitrary-address-spoofing-vulnerability-erc2771context-multicall-public-disclosure
           */
          import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol";
          import {EIP712Base} from "./EIP712Base.sol";
          contract NativeMetaTransaction is EIP712Base {
              using SafeMath for uint256;
              bytes32 private constant META_TRANSACTION_TYPEHASH = keccak256(
                  bytes(
                      "MetaTransaction(uint256 nonce,address from,bytes functionSignature)"
                  )
              );
              event MetaTransactionExecuted(
                  address indexed userAddress,
                  address payable indexed relayerAddress,
                  bytes functionSignature
              );
              mapping(address => uint256) nonces;
              /*
               * Meta transaction structure.
               * No point of including value field here as if user is doing value transfer then he has the funds to pay for gas
               * He should call the desired function directly in that case.
               */
              struct MetaTransaction {
                  uint256 nonce;
                  address from;
                  bytes functionSignature;
              }
              function executeMetaTransaction(
                  address userAddress,
                  bytes calldata functionSignature,
                  bytes32 sigR,
                  bytes32 sigS,
                  uint8 sigV
              ) external payable returns (bytes memory) {
                  MetaTransaction memory metaTx = MetaTransaction({
                      nonce: nonces[userAddress],
                      from: userAddress,
                      functionSignature: functionSignature
                  });
                  require(
                      verify(userAddress, metaTx, sigR, sigS, sigV),
                      "Signer and signature do not match"
                  );
                  // increase nonce for user (to avoid re-use)
                  ++nonces[userAddress];
                  emit MetaTransactionExecuted(
                      userAddress,
                      msg.sender,
                      functionSignature
                  );
                  // Append userAddress and relayer address at the end to extract it from calling context
                  (bool success, bytes memory returnData) = address(this).call(
                      abi.encodePacked(functionSignature, userAddress)
                  );
                  require(success, "Function call not successful");
                  return returnData;
              }
              function getNonce(address user) external view returns (uint256 nonce) {
                  nonce = nonces[user];
              }
              function hashMetaTransaction(MetaTransaction memory metaTx)
                  internal
                  pure
                  returns (bytes32)
              {
                  return
                      keccak256(
                          abi.encode(
                              META_TRANSACTION_TYPEHASH,
                              metaTx.nonce,
                              metaTx.from,
                              keccak256(metaTx.functionSignature)
                          )
                      );
              }
              function verify(
                  address signer,
                  MetaTransaction memory metaTx,
                  bytes32 sigR,
                  bytes32 sigS,
                  uint8 sigV
              ) internal view returns (bool) {
                  require(signer != address(0), "NativeMetaTransaction: INVALID_SIGNER");
                  return
                      signer ==
                      ecrecover(
                          toTypedMessageHash(hashMetaTransaction(metaTx)),
                          sigV,
                          sigR,
                          sigS
                      );
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.0;
          import "../utils/EnumerableSet.sol";
          import "../utils/Address.sol";
          import "../GSN/Context.sol";
          /**
           * @dev Contract module that allows children to implement role-based access
           * control mechanisms.
           *
           * Roles are referred to by their `bytes32` identifier. These should be exposed
           * in the external API and be unique. The best way to achieve this is by
           * using `public constant` hash digests:
           *
           * ```
           * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
           * ```
           *
           * Roles can be used to represent a set of permissions. To restrict access to a
           * function call, use {hasRole}:
           *
           * ```
           * function foo() public {
           *     require(hasRole(MY_ROLE, msg.sender));
           *     ...
           * }
           * ```
           *
           * Roles can be granted and revoked dynamically via the {grantRole} and
           * {revokeRole} functions. Each role has an associated admin role, and only
           * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
           *
           * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
           * that only accounts with this role will be able to grant or revoke other
           * roles. More complex role relationships can be created by using
           * {_setRoleAdmin}.
           *
           * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
           * grant and revoke this role. Extra precautions should be taken to secure
           * accounts that have been granted it.
           */
          abstract contract AccessControl is Context {
              using EnumerableSet for EnumerableSet.AddressSet;
              using Address for address;
              struct RoleData {
                  EnumerableSet.AddressSet members;
                  bytes32 adminRole;
              }
              mapping (bytes32 => RoleData) private _roles;
              bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
              /**
               * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
               *
               * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
               * {RoleAdminChanged} not being emitted signaling this.
               *
               * _Available since v3.1._
               */
              event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
              /**
               * @dev Emitted when `account` is granted `role`.
               *
               * `sender` is the account that originated the contract call, an admin role
               * bearer except when using {_setupRole}.
               */
              event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
              /**
               * @dev Emitted when `account` is revoked `role`.
               *
               * `sender` is the account that originated the contract call:
               *   - if using `revokeRole`, it is the admin role bearer
               *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
               */
              event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
              /**
               * @dev Returns `true` if `account` has been granted `role`.
               */
              function hasRole(bytes32 role, address account) public view returns (bool) {
                  return _roles[role].members.contains(account);
              }
              /**
               * @dev Returns the number of accounts that have `role`. Can be used
               * together with {getRoleMember} to enumerate all bearers of a role.
               */
              function getRoleMemberCount(bytes32 role) public view returns (uint256) {
                  return _roles[role].members.length();
              }
              /**
               * @dev Returns one of the accounts that have `role`. `index` must be a
               * value between 0 and {getRoleMemberCount}, non-inclusive.
               *
               * Role bearers are not sorted in any particular way, and their ordering may
               * change at any point.
               *
               * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
               * you perform all queries on the same block. See the following
               * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
               * for more information.
               */
              function getRoleMember(bytes32 role, uint256 index) public view returns (address) {
                  return _roles[role].members.at(index);
              }
              /**
               * @dev Returns the admin role that controls `role`. See {grantRole} and
               * {revokeRole}.
               *
               * To change a role's admin, use {_setRoleAdmin}.
               */
              function getRoleAdmin(bytes32 role) public view returns (bytes32) {
                  return _roles[role].adminRole;
              }
              /**
               * @dev Grants `role` to `account`.
               *
               * If `account` had not been already granted `role`, emits a {RoleGranted}
               * event.
               *
               * Requirements:
               *
               * - the caller must have ``role``'s admin role.
               */
              function grantRole(bytes32 role, address account) public virtual {
                  require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to grant");
                  _grantRole(role, account);
              }
              /**
               * @dev Revokes `role` from `account`.
               *
               * If `account` had been granted `role`, emits a {RoleRevoked} event.
               *
               * Requirements:
               *
               * - the caller must have ``role``'s admin role.
               */
              function revokeRole(bytes32 role, address account) public virtual {
                  require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to revoke");
                  _revokeRole(role, account);
              }
              /**
               * @dev Revokes `role` from the calling account.
               *
               * Roles are often managed via {grantRole} and {revokeRole}: this function's
               * purpose is to provide a mechanism for accounts to lose their privileges
               * if they are compromised (such as when a trusted device is misplaced).
               *
               * If the calling account had been granted `role`, emits a {RoleRevoked}
               * event.
               *
               * Requirements:
               *
               * - the caller must be `account`.
               */
              function renounceRole(bytes32 role, address account) public virtual {
                  require(account == _msgSender(), "AccessControl: can only renounce roles for self");
                  _revokeRole(role, account);
              }
              /**
               * @dev Grants `role` to `account`.
               *
               * If `account` had not been already granted `role`, emits a {RoleGranted}
               * event. Note that unlike {grantRole}, this function doesn't perform any
               * checks on the calling account.
               *
               * [WARNING]
               * ====
               * This function should only be called from the constructor when setting
               * up the initial roles for the system.
               *
               * Using this function in any other way is effectively circumventing the admin
               * system imposed by {AccessControl}.
               * ====
               */
              function _setupRole(bytes32 role, address account) internal virtual {
                  _grantRole(role, account);
              }
              /**
               * @dev Sets `adminRole` as ``role``'s admin role.
               *
               * Emits a {RoleAdminChanged} event.
               */
              function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
                  emit RoleAdminChanged(role, _roles[role].adminRole, adminRole);
                  _roles[role].adminRole = adminRole;
              }
              function _grantRole(bytes32 role, address account) private {
                  if (_roles[role].members.add(account)) {
                      emit RoleGranted(role, account, _msgSender());
                  }
              }
              function _revokeRole(bytes32 role, address account) private {
                  if (_roles[role].members.remove(account)) {
                      emit RoleRevoked(role, account, _msgSender());
                  }
              }
          }
          pragma solidity 0.6.6;
          import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
          contract AccessControlMixin is AccessControl {
              string private _revertMsg;
              function _setupContractId(string memory contractId) internal {
                  _revertMsg = string(abi.encodePacked(contractId, ": INSUFFICIENT_PERMISSIONS"));
              }
              modifier only(bytes32 role) {
                  require(
                      hasRole(role, _msgSender()),
                      _revertMsg
                  );
                  _;
              }
          }
          pragma solidity 0.6.6;
          /**
           * @notice DISCLAIMER:
           * Do not use NativeMetaTransaction and ContextMixin together with OpenZeppelin's "multicall"
           * nor any other form of self delegatecall!
           * Risk of address spoofing attacks.
           * Read more: https://blog.openzeppelin.com/arbitrary-address-spoofing-vulnerability-erc2771context-multicall-public-disclosure
           */
          abstract contract ContextMixin {
              function msgSender()
                  internal
                  view
                  returns (address payable sender)
              {
                  if (msg.sender == address(this)) {
                      bytes memory array = msg.data;
                      uint256 index = msg.data.length;
                      assembly {
                          // Load the 32 bytes word from memory with the address on the lower 20 bytes, and mask those.
                          sender := and(
                              mload(add(array, index)),
                              0xffffffffffffffffffffffffffffffffffffffff
                          )
                      }
                  } else {
                      sender = msg.sender;
                  }
                  return sender;
              }
          }
          pragma solidity 0.6.6;
          import {Initializable} from "./Initializable.sol";
          contract EIP712Base is Initializable {
              struct EIP712Domain {
                  string name;
                  string version;
                  address verifyingContract;
                  bytes32 salt;
              }
              string constant public ERC712_VERSION = "1";
              bytes32 internal constant EIP712_DOMAIN_TYPEHASH = keccak256(
                  bytes(
                      "EIP712Domain(string name,string version,address verifyingContract,bytes32 salt)"
                  )
              );
              bytes32 internal domainSeperator;
              // supposed to be called once while initializing.
              // one of the contractsa that inherits this contract follows proxy pattern
              // so it is not possible to do this in a constructor
              function _initializeEIP712(
                  string memory name
              )
                  internal
                  initializer
              {
                  _setDomainSeperator(name);
              }
              function _setDomainSeperator(string memory name) internal {
                  domainSeperator = keccak256(
                      abi.encode(
                          EIP712_DOMAIN_TYPEHASH,
                          keccak256(bytes(name)),
                          keccak256(bytes(ERC712_VERSION)),
                          address(this),
                          bytes32(getChainId())
                      )
                  );
              }
              function getDomainSeperator() public view returns (bytes32) {
                  return domainSeperator;
              }
              function getChainId() public pure returns (uint256) {
                  uint256 id;
                  assembly {
                      id := chainid()
                  }
                  return id;
              }
              /**
               * Accept message hash and returns hash message in EIP712 compatible form
               * So that it can be used to recover signer from signature signed using EIP712 formatted data
               * https://eips.ethereum.org/EIPS/eip-712
               * "\\\\x19" makes the encoding deterministic
               * "\\\\x01" is the version byte to make it compatible to EIP-191
               */
              function toTypedMessageHash(bytes32 messageHash)
                  internal
                  view
                  returns (bytes32)
              {
                  return
                      keccak256(
                          abi.encodePacked("\\x19\\x01", getDomainSeperator(), messageHash)
                      );
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.0;
          /**
           * @dev Library for managing
           * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
           * types.
           *
           * Sets have the following properties:
           *
           * - Elements are added, removed, and checked for existence in constant time
           * (O(1)).
           * - Elements are enumerated in O(n). No guarantees are made on the ordering.
           *
           * ```
           * contract Example {
           *     // Add the library methods
           *     using EnumerableSet for EnumerableSet.AddressSet;
           *
           *     // Declare a set state variable
           *     EnumerableSet.AddressSet private mySet;
           * }
           * ```
           *
           * As of v3.0.0, only sets of type `address` (`AddressSet`) and `uint256`
           * (`UintSet`) are supported.
           */
          library EnumerableSet {
              // To implement this library for multiple types with as little code
              // repetition as possible, we write it in terms of a generic Set type with
              // bytes32 values.
              // The Set implementation uses private functions, and user-facing
              // implementations (such as AddressSet) are just wrappers around the
              // underlying Set.
              // This means that we can only create new EnumerableSets for types that fit
              // in bytes32.
              struct Set {
                  // Storage of set values
                  bytes32[] _values;
                  // Position of the value in the `values` array, plus 1 because index 0
                  // means a value is not in the set.
                  mapping (bytes32 => uint256) _indexes;
              }
              /**
               * @dev Add a value to a set. O(1).
               *
               * Returns true if the value was added to the set, that is if it was not
               * already present.
               */
              function _add(Set storage set, bytes32 value) private returns (bool) {
                  if (!_contains(set, value)) {
                      set._values.push(value);
                      // The value is stored at length-1, but we add 1 to all indexes
                      // and use 0 as a sentinel value
                      set._indexes[value] = set._values.length;
                      return true;
                  } else {
                      return false;
                  }
              }
              /**
               * @dev Removes a value from a set. O(1).
               *
               * Returns true if the value was removed from the set, that is if it was
               * present.
               */
              function _remove(Set storage set, bytes32 value) private returns (bool) {
                  // We read and store the value's index to prevent multiple reads from the same storage slot
                  uint256 valueIndex = set._indexes[value];
                  if (valueIndex != 0) { // Equivalent to contains(set, value)
                      // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                      // the array, and then remove the last element (sometimes called as 'swap and pop').
                      // This modifies the order of the array, as noted in {at}.
                      uint256 toDeleteIndex = valueIndex - 1;
                      uint256 lastIndex = set._values.length - 1;
                      // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
                      // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
                      bytes32 lastvalue = set._values[lastIndex];
                      // Move the last value to the index where the value to delete is
                      set._values[toDeleteIndex] = lastvalue;
                      // Update the index for the moved value
                      set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based
                      // Delete the slot where the moved value was stored
                      set._values.pop();
                      // Delete the index for the deleted slot
                      delete set._indexes[value];
                      return true;
                  } else {
                      return false;
                  }
              }
              /**
               * @dev Returns true if the value is in the set. O(1).
               */
              function _contains(Set storage set, bytes32 value) private view returns (bool) {
                  return set._indexes[value] != 0;
              }
              /**
               * @dev Returns the number of values on the set. O(1).
               */
              function _length(Set storage set) private view returns (uint256) {
                  return set._values.length;
              }
             /**
              * @dev Returns the value stored at position `index` in the set. O(1).
              *
              * Note that there are no guarantees on the ordering of values inside the
              * array, and it may change when more values are added or removed.
              *
              * Requirements:
              *
              * - `index` must be strictly less than {length}.
              */
              function _at(Set storage set, uint256 index) private view returns (bytes32) {
                  require(set._values.length > index, "EnumerableSet: index out of bounds");
                  return set._values[index];
              }
              // AddressSet
              struct AddressSet {
                  Set _inner;
              }
              /**
               * @dev Add a value to a set. O(1).
               *
               * Returns true if the value was added to the set, that is if it was not
               * already present.
               */
              function add(AddressSet storage set, address value) internal returns (bool) {
                  return _add(set._inner, bytes32(uint256(value)));
              }
              /**
               * @dev Removes a value from a set. O(1).
               *
               * Returns true if the value was removed from the set, that is if it was
               * present.
               */
              function remove(AddressSet storage set, address value) internal returns (bool) {
                  return _remove(set._inner, bytes32(uint256(value)));
              }
              /**
               * @dev Returns true if the value is in the set. O(1).
               */
              function contains(AddressSet storage set, address value) internal view returns (bool) {
                  return _contains(set._inner, bytes32(uint256(value)));
              }
              /**
               * @dev Returns the number of values in the set. O(1).
               */
              function length(AddressSet storage set) internal view returns (uint256) {
                  return _length(set._inner);
              }
             /**
              * @dev Returns the value stored at position `index` in the set. O(1).
              *
              * Note that there are no guarantees on the ordering of values inside the
              * array, and it may change when more values are added or removed.
              *
              * Requirements:
              *
              * - `index` must be strictly less than {length}.
              */
              function at(AddressSet storage set, uint256 index) internal view returns (address) {
                  return address(uint256(_at(set._inner, index)));
              }
              // UintSet
              struct UintSet {
                  Set _inner;
              }
              /**
               * @dev Add a value to a set. O(1).
               *
               * Returns true if the value was added to the set, that is if it was not
               * already present.
               */
              function add(UintSet storage set, uint256 value) internal returns (bool) {
                  return _add(set._inner, bytes32(value));
              }
              /**
               * @dev Removes a value from a set. O(1).
               *
               * Returns true if the value was removed from the set, that is if it was
               * present.
               */
              function remove(UintSet storage set, uint256 value) internal returns (bool) {
                  return _remove(set._inner, bytes32(value));
              }
              /**
               * @dev Returns true if the value is in the set. O(1).
               */
              function contains(UintSet storage set, uint256 value) internal view returns (bool) {
                  return _contains(set._inner, bytes32(value));
              }
              /**
               * @dev Returns the number of values on the set. O(1).
               */
              function length(UintSet storage set) internal view returns (uint256) {
                  return _length(set._inner);
              }
             /**
              * @dev Returns the value stored at position `index` in the set. O(1).
              *
              * Note that there are no guarantees on the ordering of values inside the
              * array, and it may change when more values are added or removed.
              *
              * Requirements:
              *
              * - `index` must be strictly less than {length}.
              */
              function at(UintSet storage set, uint256 index) internal view returns (uint256) {
                  return uint256(_at(set._inner, index));
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.2;
          /**
           * @dev Collection of functions related to the address type
           */
          library Address {
              /**
               * @dev Returns true if `account` is a contract.
               *
               * [IMPORTANT]
               * ====
               * It is unsafe to assume that an address for which this function returns
               * false is an externally-owned account (EOA) and not a contract.
               *
               * Among others, `isContract` will return false for the following
               * types of addresses:
               *
               *  - an externally-owned account
               *  - a contract in construction
               *  - an address where a contract will be created
               *  - an address where a contract lived, but was destroyed
               * ====
               */
              function isContract(address account) internal view returns (bool) {
                  // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
                  // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
                  // for accounts without code, i.e. `keccak256('')`
                  bytes32 codehash;
                  bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
                  // solhint-disable-next-line no-inline-assembly
                  assembly { codehash := extcodehash(account) }
                  return (codehash != accountHash && codehash != 0x0);
              }
              /**
               * @dev 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");
                  return _functionCallWithValue(target, data, value, errorMessage);
              }
              function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
                  require(isContract(target), "Address: call to non-contract");
                  // solhint-disable-next-line avoid-low-level-calls
                  (bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
                  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.6.0;
          /*
           * @dev Provides information about the current execution context, including the
           * sender of the transaction and its data. While these are generally available
           * via msg.sender and msg.data, they should not be accessed in such a direct
           * manner, since when dealing with GSN meta-transactions the account sending and
           * paying for execution may not be the actual sender (as far as an application
           * is concerned).
           *
           * This contract is only required for intermediate, library-like contracts.
           */
          abstract contract Context {
              function _msgSender() internal view virtual returns (address payable) {
                  return msg.sender;
              }
              function _msgData() internal view virtual returns (bytes memory) {
                  this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                  return msg.data;
              }
          }
          

          File 5 of 6: RootChainProxy
          // File: contracts/common/governance/IGovernance.sol
          
          pragma solidity ^0.5.2;
          
          
          interface IGovernance {
              function update(address target, bytes calldata data) external;
          }
          
          // File: contracts/common/governance/Governable.sol
          
          pragma solidity ^0.5.2;
          
          
          contract Governable {
              IGovernance public governance;
          
              constructor(address _governance) public {
                  governance = IGovernance(_governance);
              }
          
              modifier onlyGovernance() {
                  require(msg.sender == address(governance), "Only governance contract is authorized");
                  _;
              }
          }
          
          // File: contracts/root/withdrawManager/IWithdrawManager.sol
          
          pragma solidity ^0.5.2;
          
          
          contract IWithdrawManager {
              function createExitQueue(address token) external;
          
              function verifyInclusion(
                  bytes calldata data,
                  uint8 offset,
                  bool verifyTxInclusion
              ) external view returns (uint256 age);
          
              function addExitToQueue(
                  address exitor,
                  address childToken,
                  address rootToken,
                  uint256 exitAmountOrTokenId,
                  bytes32 txHash,
                  bool isRegularExit,
                  uint256 priority
              ) external;
          
              function addInput(
                  uint256 exitId,
                  uint256 age,
                  address utxoOwner,
                  address token
              ) external;
          
              function challengeExit(
                  uint256 exitId,
                  uint256 inputId,
                  bytes calldata challengeData,
                  address adjudicatorPredicate
              ) external;
          }
          
          // File: contracts/common/Registry.sol
          
          pragma solidity ^0.5.2;
          
          
          contract Registry is Governable {
              // @todo hardcode constants
              bytes32 private constant WETH_TOKEN = keccak256("wethToken");
              bytes32 private constant DEPOSIT_MANAGER = keccak256("depositManager");
              bytes32 private constant STAKE_MANAGER = keccak256("stakeManager");
              bytes32 private constant VALIDATOR_SHARE = keccak256("validatorShare");
              bytes32 private constant WITHDRAW_MANAGER = keccak256("withdrawManager");
              bytes32 private constant CHILD_CHAIN = keccak256("childChain");
              bytes32 private constant STATE_SENDER = keccak256("stateSender");
              bytes32 private constant SLASHING_MANAGER = keccak256("slashingManager");
          
              address public erc20Predicate;
              address public erc721Predicate;
          
              mapping(bytes32 => address) public contractMap;
              mapping(address => address) public rootToChildToken;
              mapping(address => address) public childToRootToken;
              mapping(address => bool) public proofValidatorContracts;
              mapping(address => bool) public isERC721;
          
              enum Type {Invalid, ERC20, ERC721, Custom}
              struct Predicate {
                  Type _type;
              }
              mapping(address => Predicate) public predicates;
          
              event TokenMapped(address indexed rootToken, address indexed childToken);
              event ProofValidatorAdded(address indexed validator, address indexed from);
              event ProofValidatorRemoved(address indexed validator, address indexed from);
              event PredicateAdded(address indexed predicate, address indexed from);
              event PredicateRemoved(address indexed predicate, address indexed from);
              event ContractMapUpdated(bytes32 indexed key, address indexed previousContract, address indexed newContract);
          
              constructor(address _governance) public Governable(_governance) {}
          
              function updateContractMap(bytes32 _key, address _address) external onlyGovernance {
                  emit ContractMapUpdated(_key, contractMap[_key], _address);
                  contractMap[_key] = _address;
              }
          
              /**
               * @dev Map root token to child token
               * @param _rootToken Token address on the root chain
               * @param _childToken Token address on the child chain
               * @param _isERC721 Is the token being mapped ERC721
               */
              function mapToken(
                  address _rootToken,
                  address _childToken,
                  bool _isERC721
              ) external onlyGovernance {
                  require(_rootToken != address(0x0) && _childToken != address(0x0), "INVALID_TOKEN_ADDRESS");
                  rootToChildToken[_rootToken] = _childToken;
                  childToRootToken[_childToken] = _rootToken;
                  isERC721[_rootToken] = _isERC721;
                  IWithdrawManager(contractMap[WITHDRAW_MANAGER]).createExitQueue(_rootToken);
                  emit TokenMapped(_rootToken, _childToken);
              }
          
              function addErc20Predicate(address predicate) public onlyGovernance {
                  require(predicate != address(0x0), "Can not add null address as predicate");
                  erc20Predicate = predicate;
                  addPredicate(predicate, Type.ERC20);
              }
          
              function addErc721Predicate(address predicate) public onlyGovernance {
                  erc721Predicate = predicate;
                  addPredicate(predicate, Type.ERC721);
              }
          
              function addPredicate(address predicate, Type _type) public onlyGovernance {
                  require(predicates[predicate]._type == Type.Invalid, "Predicate already added");
                  predicates[predicate]._type = _type;
                  emit PredicateAdded(predicate, msg.sender);
              }
          
              function removePredicate(address predicate) public onlyGovernance {
                  require(predicates[predicate]._type != Type.Invalid, "Predicate does not exist");
                  delete predicates[predicate];
                  emit PredicateRemoved(predicate, msg.sender);
              }
          
              function getValidatorShareAddress() public view returns (address) {
                  return contractMap[VALIDATOR_SHARE];
              }
          
              function getWethTokenAddress() public view returns (address) {
                  return contractMap[WETH_TOKEN];
              }
          
              function getDepositManagerAddress() public view returns (address) {
                  return contractMap[DEPOSIT_MANAGER];
              }
          
              function getStakeManagerAddress() public view returns (address) {
                  return contractMap[STAKE_MANAGER];
              }
          
              function getSlashingManagerAddress() public view returns (address) {
                  return contractMap[SLASHING_MANAGER];
              }
          
              function getWithdrawManagerAddress() public view returns (address) {
                  return contractMap[WITHDRAW_MANAGER];
              }
          
              function getChildChainAndStateSender() public view returns (address, address) {
                  return (contractMap[CHILD_CHAIN], contractMap[STATE_SENDER]);
              }
          
              function isTokenMapped(address _token) public view returns (bool) {
                  return rootToChildToken[_token] != address(0x0);
              }
          
              function isTokenMappedAndIsErc721(address _token) public view returns (bool) {
                  require(isTokenMapped(_token), "TOKEN_NOT_MAPPED");
                  return isERC721[_token];
              }
          
              function isTokenMappedAndGetPredicate(address _token) public view returns (address) {
                  if (isTokenMappedAndIsErc721(_token)) {
                      return erc721Predicate;
                  }
                  return erc20Predicate;
              }
          
              function isChildTokenErc721(address childToken) public view returns (bool) {
                  address rootToken = childToRootToken[childToken];
                  require(rootToken != address(0x0), "Child token is not mapped");
                  return isERC721[rootToken];
              }
          }
          
          // File: openzeppelin-solidity/contracts/ownership/Ownable.sol
          
          pragma solidity ^0.5.2;
          
          
          /**
           * @title Ownable
           * @dev The Ownable contract has an owner address, and provides basic authorization control
           * functions, this simplifies the implementation of "user permissions".
           */
          contract Ownable {
              address private _owner;
          
              event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          
              /**
               * @dev The Ownable constructor sets the original `owner` of the contract to the sender
               * account.
               */
              constructor() internal {
                  _owner = msg.sender;
                  emit OwnershipTransferred(address(0), _owner);
              }
          
              /**
               * @return the address of the owner.
               */
              function owner() public view returns (address) {
                  return _owner;
              }
          
              /**
               * @dev Throws if called by any account other than the owner.
               */
              modifier onlyOwner() {
                  require(isOwner());
                  _;
              }
          
              /**
               * @return true if `msg.sender` is the owner of the contract.
               */
              function isOwner() public view returns (bool) {
                  return msg.sender == _owner;
              }
          
              /**
               * @dev Allows the current owner to relinquish control of the contract.
               * It will not be possible to call the functions with the `onlyOwner`
               * modifier anymore.
               * @notice Renouncing ownership will leave the contract without an owner,
               * thereby removing any functionality that is only available to the owner.
               */
              function renounceOwnership() public onlyOwner {
                  emit OwnershipTransferred(_owner, address(0));
                  _owner = address(0);
              }
          
              /**
               * @dev Allows the current owner to transfer control of the contract to a newOwner.
               * @param newOwner The address to transfer ownership to.
               */
              function transferOwnership(address newOwner) public onlyOwner {
                  _transferOwnership(newOwner);
              }
          
              /**
               * @dev Transfers control of the contract to a newOwner.
               * @param newOwner The address to transfer ownership to.
               */
              function _transferOwnership(address newOwner) internal {
                  require(newOwner != address(0));
                  emit OwnershipTransferred(_owner, newOwner);
                  _owner = newOwner;
              }
          }
          
          // File: contracts/common/misc/ProxyStorage.sol
          
          pragma solidity ^0.5.2;
          
          
          contract ProxyStorage is Ownable {
              address internal proxyTo;
          }
          
          // File: contracts/common/mixin/ChainIdMixin.sol
          
          pragma solidity ^0.5.2;
          
          
          contract ChainIdMixin {
              bytes public constant networkId = hex"89";
              uint256 public constant CHAINID = 137;
          }
          
          // File: contracts/root/RootChainStorage.sol
          
          pragma solidity ^0.5.2;
          
          
          contract RootChainHeader {
              event NewHeaderBlock(
                  address indexed proposer,
                  uint256 indexed headerBlockId,
                  uint256 indexed reward,
                  uint256 start,
                  uint256 end,
                  bytes32 root
              );
              // housekeeping event
              event ResetHeaderBlock(address indexed proposer, uint256 indexed headerBlockId);
              struct HeaderBlock {
                  bytes32 root;
                  uint256 start;
                  uint256 end;
                  uint256 createdAt;
                  address proposer;
              }
          }
          
          
          contract RootChainStorage is ProxyStorage, RootChainHeader, ChainIdMixin {
              bytes32 public heimdallId;
              uint8 public constant VOTE_TYPE = 2;
          
              uint16 internal constant MAX_DEPOSITS = 10000;
              uint256 public _nextHeaderBlock = MAX_DEPOSITS;
              uint256 internal _blockDepositId = 1;
              mapping(uint256 => HeaderBlock) public headerBlocks;
              Registry internal registry;
          }
          
          // File: contracts/common/misc/ERCProxy.sol
          
          /*
           * SPDX-License-Identitifer:    MIT
           */
          
          pragma solidity ^0.5.2;
          
          
          // See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-897.md
          
          interface ERCProxy {
              function proxyType() external pure returns (uint256 proxyTypeId);
          
              function implementation() external view returns (address codeAddr);
          }
          
          // File: contracts/common/misc/DelegateProxy.sol
          
          pragma solidity ^0.5.2;
          
          
          contract DelegateProxy is ERCProxy {
              function proxyType() external pure returns (uint256 proxyTypeId) {
                  // Upgradeable proxy
                  proxyTypeId = 2;
              }
          
              function implementation() external view returns (address);
          
              function delegatedFwd(address _dst, bytes memory _calldata) internal {
                  // solium-disable-next-line security/no-inline-assembly
                  assembly {
                      let result := delegatecall(sub(gas, 10000), _dst, add(_calldata, 0x20), mload(_calldata), 0, 0)
                      let size := returndatasize
          
                      let ptr := mload(0x40)
                      returndatacopy(ptr, 0, size)
          
                      // revert instead of invalid() bc if the underlying call failed with invalid() it already wasted gas.
                      // if the call returned error data, forward it
                      switch result
                          case 0 {
                              revert(ptr, size)
                          }
                          default {
                              return(ptr, size)
                          }
                  }
              }
          }
          
          // File: contracts/common/misc/Proxy.sol
          
          pragma solidity ^0.5.2;
          
          
          contract Proxy is ProxyStorage, DelegateProxy {
              event ProxyUpdated(address indexed _new, address indexed _old);
              event OwnerUpdate(address _prevOwner, address _newOwner);
          
              constructor(address _proxyTo) public {
                  updateImplementation(_proxyTo);
              }
          
              function() external payable {
                  // require(currentContract != 0, "If app code has not been set yet, do not call");
                  // Todo: filter out some calls or handle in the end fallback
                  delegatedFwd(proxyTo, msg.data);
              }
          
              function implementation() external view returns (address) {
                  return proxyTo;
              }
          
              function updateImplementation(address _newProxyTo) public onlyOwner {
                  require(_newProxyTo != address(0x0), "INVALID_PROXY_ADDRESS");
                  require(isContract(_newProxyTo), "DESTINATION_ADDRESS_IS_NOT_A_CONTRACT");
                  emit ProxyUpdated(_newProxyTo, proxyTo);
                  proxyTo = _newProxyTo;
              }
          
              function isContract(address _target) internal view returns (bool) {
                  if (_target == address(0)) {
                      return false;
                  }
          
                  uint256 size;
                  assembly {
                      size := extcodesize(_target)
                  }
                  return size > 0;
              }
          }
          
          // File: contracts/root/RootChainProxy.sol
          
          pragma solidity ^0.5.2;
          
          
          contract RootChainProxy is Proxy, RootChainStorage {
              constructor(
                  address _proxyTo,
                  address _registry,
                  string memory _heimdallId
              ) public Proxy(_proxyTo) {
                  registry = Registry(_registry);
                  heimdallId = keccak256(abi.encodePacked(_heimdallId));
              }
          }

          File 6 of 6: ERC20Predicate
          pragma solidity 0.6.6;
          import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
          import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
          import {AccessControlMixin} from "../../common/AccessControlMixin.sol";
          import {RLPReader} from "../../lib/RLPReader.sol";
          import {ITokenPredicate} from "./ITokenPredicate.sol";
          import {Initializable} from "../../common/Initializable.sol";
          contract ERC20Predicate is ITokenPredicate, AccessControlMixin, Initializable {
              using RLPReader for bytes;
              using RLPReader for RLPReader.RLPItem;
              using SafeERC20 for IERC20;
              bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE");
              bytes32 public constant TOKEN_TYPE = keccak256("ERC20");
              bytes32 public constant TRANSFER_EVENT_SIG = 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
              event LockedERC20(
                  address indexed depositor,
                  address indexed depositReceiver,
                  address indexed rootToken,
                  uint256 amount
              );
              event ExitedERC20(
                  address indexed exitor,
                  address indexed rootToken,
                  uint256 amount
              );
              constructor() public {
                  // Disable initializer on implementation contract
                  _disableInitializer();
              }
              function initialize(address _owner) external initializer {
                  _setupContractId("ERC20Predicate");
                  _setupRole(DEFAULT_ADMIN_ROLE, _owner);
                  _setupRole(MANAGER_ROLE, _owner);
              }
              /**
               * @notice Lock ERC20 tokens for deposit, callable only by manager
               * @param depositor Address who wants to deposit tokens
               * @param depositReceiver Address (address) who wants to receive tokens on child chain
               * @param rootToken Token which gets deposited
               * @param depositData ABI encoded amount
               */
              function lockTokens(
                  address depositor,
                  address depositReceiver,
                  address rootToken,
                  bytes calldata depositData
              )
                  external
                  override
                  only(MANAGER_ROLE)
              {
                  uint256 amount = abi.decode(depositData, (uint256));
                  emit LockedERC20(depositor, depositReceiver, rootToken, amount);
                  IERC20(rootToken).safeTransferFrom(depositor, address(this), amount);
              }
              /**
               * @notice Validates log signature, from and to address
               * then sends the correct amount to withdrawer
               * callable only by manager
               * @notice address unused, being kept for abi compatability
               * @param rootToken Token which gets withdrawn
               * @param log Valid ERC20 burn log from child chain
               */
              function exitTokens(
                  address,
                  address rootToken,
                  bytes calldata log
              )
                  external
                  override
                  only(MANAGER_ROLE)
              {
                  RLPReader.RLPItem[] memory logRLPList = log.toRlpItem().toList();
                  RLPReader.RLPItem[] memory logTopicRLPList = logRLPList[1].toList(); // topics
                  require(
                      bytes32(logTopicRLPList[0].toUint()) == TRANSFER_EVENT_SIG, // topic0 is event sig
                      "ERC20Predicate: INVALID_SIGNATURE"
                  );
                  address withdrawer = address(logTopicRLPList[1].toUint()); // topic1 is from address
                  require(
                      address(logTopicRLPList[2].toUint()) == address(0), // topic2 is to address
                      "ERC20Predicate: INVALID_RECEIVER"
                  );
                  uint256 amount = logRLPList[2].toUint(); // log data field is the amount
                  IERC20(rootToken).safeTransfer(
                      withdrawer,
                      amount
                  );
                  emit ExitedERC20(withdrawer, rootToken, amount);
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.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.6.0;
          import "./IERC20.sol";
          import "../../math/SafeMath.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 SafeMath for uint256;
              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).add(value);
                  _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
              }
              function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                  uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
                  _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");
                  }
              }
          }
          pragma solidity 0.6.6;
          import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
          contract AccessControlMixin is AccessControl {
              string private _revertMsg;
              function _setupContractId(string memory contractId) internal {
                  _revertMsg = string(abi.encodePacked(contractId, ": INSUFFICIENT_PERMISSIONS"));
              }
              modifier only(bytes32 role) {
                  require(
                      hasRole(role, _msgSender()),
                      _revertMsg
                  );
                  _;
              }
          }
          /*
           * @author Hamdi Allam [email protected]
           * Please reach out with any questions or concerns
           * https://github.com/hamdiallam/Solidity-RLP/blob/e681e25a376dbd5426b509380bc03446f05d0f97/contracts/RLPReader.sol
           */
          pragma solidity 0.6.6;
          library RLPReader {
              uint8 constant STRING_SHORT_START = 0x80;
              uint8 constant STRING_LONG_START  = 0xb8;
              uint8 constant LIST_SHORT_START   = 0xc0;
              uint8 constant LIST_LONG_START    = 0xf8;
              uint8 constant WORD_SIZE = 32;
              struct RLPItem {
                  uint len;
                  uint memPtr;
              }
              struct Iterator {
                  RLPItem item;   // Item that's being iterated over.
                  uint nextPtr;   // Position of the next item in the list.
              }
              /*
              * @dev Returns the next element in the iteration. Reverts if it has not next element.
              * @param self The iterator.
              * @return The next element in the iteration.
              */
              function next(Iterator memory self) internal pure returns (RLPItem memory) {
                  require(hasNext(self));
                  uint ptr = self.nextPtr;
                  uint itemLength = _itemLength(ptr);
                  self.nextPtr = ptr + itemLength;
                  return RLPItem(itemLength, ptr);
              }
              /*
              * @dev Returns true if the iteration has more elements.
              * @param self The iterator.
              * @return true if the iteration has more elements.
              */
              function hasNext(Iterator memory self) internal pure returns (bool) {
                  RLPItem memory item = self.item;
                  return self.nextPtr < item.memPtr + item.len;
              }
              /*
              * @param item RLP encoded bytes
              */
              function toRlpItem(bytes memory item) internal pure returns (RLPItem memory) {
                  uint memPtr;
                  assembly {
                      memPtr := add(item, 0x20)
                  }
                  return RLPItem(item.length, memPtr);
              }
              /*
              * @dev Create an iterator. Reverts if item is not a list.
              * @param self The RLP item.
              * @return An 'Iterator' over the item.
              */
              function iterator(RLPItem memory self) internal pure returns (Iterator memory) {
                  require(isList(self));
                  uint ptr = self.memPtr + _payloadOffset(self.memPtr);
                  return Iterator(self, ptr);
              }
              /*
              * @param the RLP item.
              */
              function rlpLen(RLPItem memory item) internal pure returns (uint) {
                  return item.len;
              }
              /*
               * @param the RLP item.
               * @return (memPtr, len) pair: location of the item's payload in memory.
               */
              function payloadLocation(RLPItem memory item) internal pure returns (uint, uint) {
                  uint offset = _payloadOffset(item.memPtr);
                  uint memPtr = item.memPtr + offset;
                  uint len = item.len - offset; // data length
                  return (memPtr, len);
              }
              /*
              * @param the RLP item.
              */
              function payloadLen(RLPItem memory item) internal pure returns (uint) {
                  (, uint len) = payloadLocation(item);
                  return len;
              }
              /*
              * @param the RLP item containing the encoded list.
              */
              function toList(RLPItem memory item) internal pure returns (RLPItem[] memory) {
                  require(isList(item));
                  uint items = numItems(item);
                  RLPItem[] memory result = new RLPItem[](items);
                  uint memPtr = item.memPtr + _payloadOffset(item.memPtr);
                  uint dataLen;
                  for (uint i = 0; i < items; i++) {
                      dataLen = _itemLength(memPtr);
                      result[i] = RLPItem(dataLen, memPtr); 
                      memPtr = memPtr + dataLen;
                  }
                  require(memPtr - item.memPtr == item.len, "Wrong total length.");
                  return result;
              }
              // @return indicator whether encoded payload is a list. negate this function call for isData.
              function isList(RLPItem memory item) internal pure returns (bool) {
                  if (item.len == 0) return false;
                  uint8 byte0;
                  uint memPtr = item.memPtr;
                  assembly {
                      byte0 := byte(0, mload(memPtr))
                  }
                  if (byte0 < LIST_SHORT_START)
                      return false;
                  return true;
              }
              /*
               * @dev A cheaper version of keccak256(toRlpBytes(item)) that avoids copying memory.
               * @return keccak256 hash of RLP encoded bytes.
               */
              function rlpBytesKeccak256(RLPItem memory item) internal pure returns (bytes32) {
                  uint256 ptr = item.memPtr;
                  uint256 len = item.len;
                  bytes32 result;
                  assembly {
                      result := keccak256(ptr, len)
                  }
                  return result;
              }
              /*
               * @dev A cheaper version of keccak256(toBytes(item)) that avoids copying memory.
               * @return keccak256 hash of the item payload.
               */
              function payloadKeccak256(RLPItem memory item) internal pure returns (bytes32) {
                  (uint memPtr, uint len) = payloadLocation(item);
                  bytes32 result;
                  assembly {
                      result := keccak256(memPtr, len)
                  }
                  return result;
              }
              /** RLPItem conversions into data types **/
              // @returns raw rlp encoding in bytes
              function toRlpBytes(RLPItem memory item) internal pure returns (bytes memory) {
                  bytes memory result = new bytes(item.len);
                  if (result.length == 0) return result;
                  
                  uint ptr;
                  assembly {
                      ptr := add(0x20, result)
                  }
                  copy(item.memPtr, ptr, item.len);
                  return result;
              }
              // any non-zero byte except "0x80" is considered true
              function toBoolean(RLPItem memory item) internal pure returns (bool) {
                  require(item.len == 1);
                  uint result;
                  uint memPtr = item.memPtr;
                  assembly {
                      result := byte(0, mload(memPtr))
                  }
                  // SEE Github Issue #5.
                  // Summary: Most commonly used RLP libraries (i.e Geth) will encode
                  // "0" as "0x80" instead of as "0". We handle this edge case explicitly
                  // here.
                  if (result == 0 || result == STRING_SHORT_START) {
                      return false;
                  } else {
                      return true;
                  }
              }
              function toAddress(RLPItem memory item) internal pure returns (address) {
                  // 1 byte for the length prefix
                  require(item.len == 21);
                  return address(toUint(item));
              }
              function toUint(RLPItem memory item) internal pure returns (uint) {
                  require(item.len > 0 && item.len <= 33);
                  (uint memPtr, uint len) = payloadLocation(item);
                  uint result;
                  assembly {
                      result := mload(memPtr)
                      // shfit to the correct location if neccesary
                      if lt(len, 32) {
                          result := div(result, exp(256, sub(32, len)))
                      }
                  }
                  return result;
              }
              // enforces 32 byte length
              function toUintStrict(RLPItem memory item) internal pure returns (uint) {
                  // one byte prefix
                  require(item.len == 33);
                  uint result;
                  uint memPtr = item.memPtr + 1;
                  assembly {
                      result := mload(memPtr)
                  }
                  return result;
              }
              function toBytes(RLPItem memory item) internal pure returns (bytes memory) {
                  require(item.len > 0);
                  (uint memPtr, uint len) = payloadLocation(item);
                  bytes memory result = new bytes(len);
                  uint destPtr;
                  assembly {
                      destPtr := add(0x20, result)
                  }
                  copy(memPtr, destPtr, len);
                  return result;
              }
              /*
              * Private Helpers
              */
              // @return number of payload items inside an encoded list.
              function numItems(RLPItem memory item) private pure returns (uint) {
                  if (item.len == 0) return 0;
                  uint count = 0;
                  uint currPtr = item.memPtr + _payloadOffset(item.memPtr);
                  uint endPtr = item.memPtr + item.len;
                  while (currPtr < endPtr) {
                     currPtr = currPtr + _itemLength(currPtr); // skip over an item
                     count++;
                  }
                  return count;
              }
              // @return entire rlp item byte length
              function _itemLength(uint memPtr) private pure returns (uint) {
                  uint itemLen;
                  uint byte0;
                  assembly {
                      byte0 := byte(0, mload(memPtr))
                  }
                  if (byte0 < STRING_SHORT_START)
                      itemLen = 1;
                  
                  else if (byte0 < STRING_LONG_START)
                      itemLen = byte0 - STRING_SHORT_START + 1;
                  else if (byte0 < LIST_SHORT_START) {
                      assembly {
                          let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is
                          memPtr := add(memPtr, 1) // skip over the first byte
                          
                          /* 32 byte word size */
                          let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to get the len
                          itemLen := add(dataLen, add(byteLen, 1))
                      }
                  }
                  else if (byte0 < LIST_LONG_START) {
                      itemLen = byte0 - LIST_SHORT_START + 1;
                  } 
                  else {
                      assembly {
                          let byteLen := sub(byte0, 0xf7)
                          memPtr := add(memPtr, 1)
                          let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to the correct length
                          itemLen := add(dataLen, add(byteLen, 1))
                      }
                  }
                  return itemLen;
              }
              // @return number of bytes until the data
              function _payloadOffset(uint memPtr) private pure returns (uint) {
                  uint byte0;
                  assembly {
                      byte0 := byte(0, mload(memPtr))
                  }
                  if (byte0 < STRING_SHORT_START) 
                      return 0;
                  else if (byte0 < STRING_LONG_START || (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START))
                      return 1;
                  else if (byte0 < LIST_SHORT_START)  // being explicit
                      return byte0 - (STRING_LONG_START - 1) + 1;
                  else
                      return byte0 - (LIST_LONG_START - 1) + 1;
              }
              /*
              * @param src Pointer to source
              * @param dest Pointer to destination
              * @param len Amount of memory to copy from the source
              */
              function copy(uint src, uint dest, uint len) private pure {
                  if (len == 0) return;
                  // copy as many word sizes as possible
                  for (; len >= WORD_SIZE; len -= WORD_SIZE) {
                      assembly {
                          mstore(dest, mload(src))
                      }
                      src += WORD_SIZE;
                      dest += WORD_SIZE;
                  }
                  if (len > 0) {
                      // left over bytes. Mask is used to remove unwanted bytes from the word
                      uint mask = 256 ** (WORD_SIZE - len) - 1;
                      assembly {
                          let srcpart := and(mload(src), not(mask)) // zero out src
                          let destpart := and(mload(dest), mask) // retrieve the bytes
                          mstore(dest, or(destpart, srcpart))
                      }
                  }
              }
          }
          pragma solidity 0.6.6;
          import {RLPReader} from "../../lib/RLPReader.sol";
          /// @title Token predicate interface for all pos portal predicates
          /// @notice Abstract interface that defines methods for custom predicates
          interface ITokenPredicate {
              /**
               * @notice Deposit tokens into pos portal
               * @dev When `depositor` deposits tokens into pos portal, tokens get locked into predicate contract.
               * @param depositor Address who wants to deposit tokens
               * @param depositReceiver Address (address) who wants to receive tokens on side chain
               * @param rootToken Token which gets deposited
               * @param depositData Extra data for deposit (amount for ERC20, token id for ERC721 etc.) [ABI encoded]
               */
              function lockTokens(
                  address depositor,
                  address depositReceiver,
                  address rootToken,
                  bytes calldata depositData
              ) external;
              /**
               * @notice Validates and processes exit while withdraw process
               * @dev Validates exit log emitted on sidechain. Reverts if validation fails.
               * @dev Processes withdraw based on custom logic. Example: transfer ERC20/ERC721, mint ERC721 if mintable withdraw
               * @param sender unused for polygon predicates, being kept for abi compatability
               * @param rootToken Token which gets withdrawn
               * @param logRLPList Valid sidechain log for data like amount, token id etc.
               */
              function exitTokens(
                  address sender,
                  address rootToken,
                  bytes calldata logRLPList
              ) external;
          }
          pragma solidity 0.6.6;
          contract Initializable {
              bool inited = false;
              modifier initializer() {
                  require(!inited, "already inited");
                  _;
                  inited = true;
              }
              function _disableInitializer() internal {
                  inited = true;
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.0;
          /**
           * @dev Wrappers over Solidity's arithmetic operations with added overflow
           * checks.
           *
           * Arithmetic operations in Solidity wrap on overflow. This can easily result
           * in bugs, because programmers usually assume that an overflow raises an
           * error, which is the standard behavior in high level programming languages.
           * `SafeMath` restores this intuition by reverting the transaction when an
           * operation overflows.
           *
           * Using this library instead of the unchecked operations eliminates an entire
           * class of bugs, so it's recommended to use it always.
           */
          library SafeMath {
              /**
               * @dev Returns the addition of two unsigned integers, reverting on
               * overflow.
               *
               * Counterpart to Solidity's `+` operator.
               *
               * Requirements:
               *
               * - Addition cannot overflow.
               */
              function add(uint256 a, uint256 b) internal pure returns (uint256) {
                  uint256 c = a + b;
                  require(c >= a, "SafeMath: addition overflow");
                  return c;
              }
              /**
               * @dev Returns the subtraction of two unsigned integers, reverting on
               * overflow (when the result is negative).
               *
               * Counterpart to Solidity's `-` operator.
               *
               * Requirements:
               *
               * - Subtraction cannot overflow.
               */
              function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                  return sub(a, b, "SafeMath: subtraction overflow");
              }
              /**
               * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
               * overflow (when the result is negative).
               *
               * Counterpart to Solidity's `-` operator.
               *
               * Requirements:
               *
               * - Subtraction cannot overflow.
               */
              function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                  require(b <= a, errorMessage);
                  uint256 c = a - b;
                  return c;
              }
              /**
               * @dev Returns the multiplication of two unsigned integers, reverting on
               * overflow.
               *
               * Counterpart to Solidity's `*` operator.
               *
               * Requirements:
               *
               * - Multiplication cannot overflow.
               */
              function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                  // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                  // benefit is lost if 'b' is also tested.
                  // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                  if (a == 0) {
                      return 0;
                  }
                  uint256 c = a * b;
                  require(c / a == b, "SafeMath: multiplication overflow");
                  return c;
              }
              /**
               * @dev Returns the integer division of two unsigned integers. Reverts on
               * division by zero. The result is rounded towards zero.
               *
               * Counterpart to Solidity's `/` operator. Note: this function uses a
               * `revert` opcode (which leaves remaining gas untouched) while Solidity
               * uses an invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               *
               * - The divisor cannot be zero.
               */
              function div(uint256 a, uint256 b) internal pure returns (uint256) {
                  return div(a, b, "SafeMath: division by zero");
              }
              /**
               * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
               * division by zero. The result is rounded towards zero.
               *
               * Counterpart to Solidity's `/` operator. Note: this function uses a
               * `revert` opcode (which leaves remaining gas untouched) while Solidity
               * uses an invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               *
               * - The divisor cannot be zero.
               */
              function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                  require(b > 0, errorMessage);
                  uint256 c = a / b;
                  // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                  return c;
              }
              /**
               * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
               * Reverts when dividing by zero.
               *
               * Counterpart to Solidity's `%` operator. This function uses a `revert`
               * opcode (which leaves remaining gas untouched) while Solidity uses an
               * invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               *
               * - The divisor cannot be zero.
               */
              function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                  return mod(a, b, "SafeMath: modulo by zero");
              }
              /**
               * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
               * Reverts with custom message when dividing by zero.
               *
               * Counterpart to Solidity's `%` operator. This function uses a `revert`
               * opcode (which leaves remaining gas untouched) while Solidity uses an
               * invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               *
               * - The divisor cannot be zero.
               */
              function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                  require(b != 0, errorMessage);
                  return a % b;
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.2;
          /**
           * @dev Collection of functions related to the address type
           */
          library Address {
              /**
               * @dev Returns true if `account` is a contract.
               *
               * [IMPORTANT]
               * ====
               * It is unsafe to assume that an address for which this function returns
               * false is an externally-owned account (EOA) and not a contract.
               *
               * Among others, `isContract` will return false for the following
               * types of addresses:
               *
               *  - an externally-owned account
               *  - a contract in construction
               *  - an address where a contract will be created
               *  - an address where a contract lived, but was destroyed
               * ====
               */
              function isContract(address account) internal view returns (bool) {
                  // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
                  // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
                  // for accounts without code, i.e. `keccak256('')`
                  bytes32 codehash;
                  bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
                  // solhint-disable-next-line no-inline-assembly
                  assembly { codehash := extcodehash(account) }
                  return (codehash != accountHash && codehash != 0x0);
              }
              /**
               * @dev 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");
                  return _functionCallWithValue(target, data, value, errorMessage);
              }
              function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
                  require(isContract(target), "Address: call to non-contract");
                  // solhint-disable-next-line avoid-low-level-calls
                  (bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
                  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.6.0;
          import "../utils/EnumerableSet.sol";
          import "../utils/Address.sol";
          import "../GSN/Context.sol";
          /**
           * @dev Contract module that allows children to implement role-based access
           * control mechanisms.
           *
           * Roles are referred to by their `bytes32` identifier. These should be exposed
           * in the external API and be unique. The best way to achieve this is by
           * using `public constant` hash digests:
           *
           * ```
           * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
           * ```
           *
           * Roles can be used to represent a set of permissions. To restrict access to a
           * function call, use {hasRole}:
           *
           * ```
           * function foo() public {
           *     require(hasRole(MY_ROLE, msg.sender));
           *     ...
           * }
           * ```
           *
           * Roles can be granted and revoked dynamically via the {grantRole} and
           * {revokeRole} functions. Each role has an associated admin role, and only
           * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
           *
           * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
           * that only accounts with this role will be able to grant or revoke other
           * roles. More complex role relationships can be created by using
           * {_setRoleAdmin}.
           *
           * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
           * grant and revoke this role. Extra precautions should be taken to secure
           * accounts that have been granted it.
           */
          abstract contract AccessControl is Context {
              using EnumerableSet for EnumerableSet.AddressSet;
              using Address for address;
              struct RoleData {
                  EnumerableSet.AddressSet members;
                  bytes32 adminRole;
              }
              mapping (bytes32 => RoleData) private _roles;
              bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
              /**
               * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
               *
               * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
               * {RoleAdminChanged} not being emitted signaling this.
               *
               * _Available since v3.1._
               */
              event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
              /**
               * @dev Emitted when `account` is granted `role`.
               *
               * `sender` is the account that originated the contract call, an admin role
               * bearer except when using {_setupRole}.
               */
              event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
              /**
               * @dev Emitted when `account` is revoked `role`.
               *
               * `sender` is the account that originated the contract call:
               *   - if using `revokeRole`, it is the admin role bearer
               *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
               */
              event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
              /**
               * @dev Returns `true` if `account` has been granted `role`.
               */
              function hasRole(bytes32 role, address account) public view returns (bool) {
                  return _roles[role].members.contains(account);
              }
              /**
               * @dev Returns the number of accounts that have `role`. Can be used
               * together with {getRoleMember} to enumerate all bearers of a role.
               */
              function getRoleMemberCount(bytes32 role) public view returns (uint256) {
                  return _roles[role].members.length();
              }
              /**
               * @dev Returns one of the accounts that have `role`. `index` must be a
               * value between 0 and {getRoleMemberCount}, non-inclusive.
               *
               * Role bearers are not sorted in any particular way, and their ordering may
               * change at any point.
               *
               * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
               * you perform all queries on the same block. See the following
               * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
               * for more information.
               */
              function getRoleMember(bytes32 role, uint256 index) public view returns (address) {
                  return _roles[role].members.at(index);
              }
              /**
               * @dev Returns the admin role that controls `role`. See {grantRole} and
               * {revokeRole}.
               *
               * To change a role's admin, use {_setRoleAdmin}.
               */
              function getRoleAdmin(bytes32 role) public view returns (bytes32) {
                  return _roles[role].adminRole;
              }
              /**
               * @dev Grants `role` to `account`.
               *
               * If `account` had not been already granted `role`, emits a {RoleGranted}
               * event.
               *
               * Requirements:
               *
               * - the caller must have ``role``'s admin role.
               */
              function grantRole(bytes32 role, address account) public virtual {
                  require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to grant");
                  _grantRole(role, account);
              }
              /**
               * @dev Revokes `role` from `account`.
               *
               * If `account` had been granted `role`, emits a {RoleRevoked} event.
               *
               * Requirements:
               *
               * - the caller must have ``role``'s admin role.
               */
              function revokeRole(bytes32 role, address account) public virtual {
                  require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to revoke");
                  _revokeRole(role, account);
              }
              /**
               * @dev Revokes `role` from the calling account.
               *
               * Roles are often managed via {grantRole} and {revokeRole}: this function's
               * purpose is to provide a mechanism for accounts to lose their privileges
               * if they are compromised (such as when a trusted device is misplaced).
               *
               * If the calling account had been granted `role`, emits a {RoleRevoked}
               * event.
               *
               * Requirements:
               *
               * - the caller must be `account`.
               */
              function renounceRole(bytes32 role, address account) public virtual {
                  require(account == _msgSender(), "AccessControl: can only renounce roles for self");
                  _revokeRole(role, account);
              }
              /**
               * @dev Grants `role` to `account`.
               *
               * If `account` had not been already granted `role`, emits a {RoleGranted}
               * event. Note that unlike {grantRole}, this function doesn't perform any
               * checks on the calling account.
               *
               * [WARNING]
               * ====
               * This function should only be called from the constructor when setting
               * up the initial roles for the system.
               *
               * Using this function in any other way is effectively circumventing the admin
               * system imposed by {AccessControl}.
               * ====
               */
              function _setupRole(bytes32 role, address account) internal virtual {
                  _grantRole(role, account);
              }
              /**
               * @dev Sets `adminRole` as ``role``'s admin role.
               *
               * Emits a {RoleAdminChanged} event.
               */
              function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
                  emit RoleAdminChanged(role, _roles[role].adminRole, adminRole);
                  _roles[role].adminRole = adminRole;
              }
              function _grantRole(bytes32 role, address account) private {
                  if (_roles[role].members.add(account)) {
                      emit RoleGranted(role, account, _msgSender());
                  }
              }
              function _revokeRole(bytes32 role, address account) private {
                  if (_roles[role].members.remove(account)) {
                      emit RoleRevoked(role, account, _msgSender());
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.0;
          /**
           * @dev Library for managing
           * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
           * types.
           *
           * Sets have the following properties:
           *
           * - Elements are added, removed, and checked for existence in constant time
           * (O(1)).
           * - Elements are enumerated in O(n). No guarantees are made on the ordering.
           *
           * ```
           * contract Example {
           *     // Add the library methods
           *     using EnumerableSet for EnumerableSet.AddressSet;
           *
           *     // Declare a set state variable
           *     EnumerableSet.AddressSet private mySet;
           * }
           * ```
           *
           * As of v3.0.0, only sets of type `address` (`AddressSet`) and `uint256`
           * (`UintSet`) are supported.
           */
          library EnumerableSet {
              // To implement this library for multiple types with as little code
              // repetition as possible, we write it in terms of a generic Set type with
              // bytes32 values.
              // The Set implementation uses private functions, and user-facing
              // implementations (such as AddressSet) are just wrappers around the
              // underlying Set.
              // This means that we can only create new EnumerableSets for types that fit
              // in bytes32.
              struct Set {
                  // Storage of set values
                  bytes32[] _values;
                  // Position of the value in the `values` array, plus 1 because index 0
                  // means a value is not in the set.
                  mapping (bytes32 => uint256) _indexes;
              }
              /**
               * @dev Add a value to a set. O(1).
               *
               * Returns true if the value was added to the set, that is if it was not
               * already present.
               */
              function _add(Set storage set, bytes32 value) private returns (bool) {
                  if (!_contains(set, value)) {
                      set._values.push(value);
                      // The value is stored at length-1, but we add 1 to all indexes
                      // and use 0 as a sentinel value
                      set._indexes[value] = set._values.length;
                      return true;
                  } else {
                      return false;
                  }
              }
              /**
               * @dev Removes a value from a set. O(1).
               *
               * Returns true if the value was removed from the set, that is if it was
               * present.
               */
              function _remove(Set storage set, bytes32 value) private returns (bool) {
                  // We read and store the value's index to prevent multiple reads from the same storage slot
                  uint256 valueIndex = set._indexes[value];
                  if (valueIndex != 0) { // Equivalent to contains(set, value)
                      // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                      // the array, and then remove the last element (sometimes called as 'swap and pop').
                      // This modifies the order of the array, as noted in {at}.
                      uint256 toDeleteIndex = valueIndex - 1;
                      uint256 lastIndex = set._values.length - 1;
                      // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
                      // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
                      bytes32 lastvalue = set._values[lastIndex];
                      // Move the last value to the index where the value to delete is
                      set._values[toDeleteIndex] = lastvalue;
                      // Update the index for the moved value
                      set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based
                      // Delete the slot where the moved value was stored
                      set._values.pop();
                      // Delete the index for the deleted slot
                      delete set._indexes[value];
                      return true;
                  } else {
                      return false;
                  }
              }
              /**
               * @dev Returns true if the value is in the set. O(1).
               */
              function _contains(Set storage set, bytes32 value) private view returns (bool) {
                  return set._indexes[value] != 0;
              }
              /**
               * @dev Returns the number of values on the set. O(1).
               */
              function _length(Set storage set) private view returns (uint256) {
                  return set._values.length;
              }
             /**
              * @dev Returns the value stored at position `index` in the set. O(1).
              *
              * Note that there are no guarantees on the ordering of values inside the
              * array, and it may change when more values are added or removed.
              *
              * Requirements:
              *
              * - `index` must be strictly less than {length}.
              */
              function _at(Set storage set, uint256 index) private view returns (bytes32) {
                  require(set._values.length > index, "EnumerableSet: index out of bounds");
                  return set._values[index];
              }
              // AddressSet
              struct AddressSet {
                  Set _inner;
              }
              /**
               * @dev Add a value to a set. O(1).
               *
               * Returns true if the value was added to the set, that is if it was not
               * already present.
               */
              function add(AddressSet storage set, address value) internal returns (bool) {
                  return _add(set._inner, bytes32(uint256(value)));
              }
              /**
               * @dev Removes a value from a set. O(1).
               *
               * Returns true if the value was removed from the set, that is if it was
               * present.
               */
              function remove(AddressSet storage set, address value) internal returns (bool) {
                  return _remove(set._inner, bytes32(uint256(value)));
              }
              /**
               * @dev Returns true if the value is in the set. O(1).
               */
              function contains(AddressSet storage set, address value) internal view returns (bool) {
                  return _contains(set._inner, bytes32(uint256(value)));
              }
              /**
               * @dev Returns the number of values in the set. O(1).
               */
              function length(AddressSet storage set) internal view returns (uint256) {
                  return _length(set._inner);
              }
             /**
              * @dev Returns the value stored at position `index` in the set. O(1).
              *
              * Note that there are no guarantees on the ordering of values inside the
              * array, and it may change when more values are added or removed.
              *
              * Requirements:
              *
              * - `index` must be strictly less than {length}.
              */
              function at(AddressSet storage set, uint256 index) internal view returns (address) {
                  return address(uint256(_at(set._inner, index)));
              }
              // UintSet
              struct UintSet {
                  Set _inner;
              }
              /**
               * @dev Add a value to a set. O(1).
               *
               * Returns true if the value was added to the set, that is if it was not
               * already present.
               */
              function add(UintSet storage set, uint256 value) internal returns (bool) {
                  return _add(set._inner, bytes32(value));
              }
              /**
               * @dev Removes a value from a set. O(1).
               *
               * Returns true if the value was removed from the set, that is if it was
               * present.
               */
              function remove(UintSet storage set, uint256 value) internal returns (bool) {
                  return _remove(set._inner, bytes32(value));
              }
              /**
               * @dev Returns true if the value is in the set. O(1).
               */
              function contains(UintSet storage set, uint256 value) internal view returns (bool) {
                  return _contains(set._inner, bytes32(value));
              }
              /**
               * @dev Returns the number of values on the set. O(1).
               */
              function length(UintSet storage set) internal view returns (uint256) {
                  return _length(set._inner);
              }
             /**
              * @dev Returns the value stored at position `index` in the set. O(1).
              *
              * Note that there are no guarantees on the ordering of values inside the
              * array, and it may change when more values are added or removed.
              *
              * Requirements:
              *
              * - `index` must be strictly less than {length}.
              */
              function at(UintSet storage set, uint256 index) internal view returns (uint256) {
                  return uint256(_at(set._inner, index));
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.0;
          /*
           * @dev Provides information about the current execution context, including the
           * sender of the transaction and its data. While these are generally available
           * via msg.sender and msg.data, they should not be accessed in such a direct
           * manner, since when dealing with GSN meta-transactions the account sending and
           * paying for execution may not be the actual sender (as far as an application
           * is concerned).
           *
           * This contract is only required for intermediate, library-like contracts.
           */
          abstract contract Context {
              function _msgSender() internal view virtual returns (address payable) {
                  return msg.sender;
              }
              function _msgData() internal view virtual returns (bytes memory) {
                  this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                  return msg.data;
              }
          }