ETH Price: $2,620.87 (+1.14%)

Transaction Decoder

Block:
22583550 at May-28-2025 08:09:35 PM +UTC
Transaction Fee:
0.001301615330048696 ETH $3.41
Gas Used:
265,252 Gas / 4.907089598 Gwei

Emitted Events:

323 ERC1967Proxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000009923263fa127b3d1484cfd649df8f1831c2a74e4, 0x00000000000000000000000012948955847fd30f5d9bea99e15afa468cfcf559, 00000000000000000000000000000000000000000000043c33c1937564800000 )
324 MintableERC20PredicateProxy.0x42315cb7471194a6f162099cd1052b95b750612a46472e887f7784b95aa2c4c3( 0x42315cb7471194a6f162099cd1052b95b750612a46472e887f7784b95aa2c4c3, 0x00000000000000000000000012948955847fd30f5d9bea99e15afa468cfcf559, 0x0000000000000000000000005fab9761d60419c9eeebe3915a8fa1ed7e8d2e1b, 00000000000000000000000000000000000000000000043c33c1937564800000 )

Account State Difference:

  Address   Before After State Difference Code
0x12948955...68cfCf559
0.015958852763774585 Eth
Nonce: 300
0.014657237433725889 Eth
Nonce: 301
0.001301615330048696
(Titan Builder)
11.390013377156118964 Eth11.390278629156118964 Eth0.000265252
0x5fab9761...d7e8d2E1B
0xA0c68C63...1bFc77C77
(Polygon (Matic): Bridge)

Execution Trace

RootChainManagerProxy.3805550f( )
  • RootChainManager.exit( inputData=0xF90A678431210E90B9012013957EEBF275D2BA8137963A8A4933757F02ADB95C9524CAD100CDDD4448E235BC96AB6F5C83DEF80368FB08ACC149E7BBE469FC80AB281D0FB2AF16C306CC0DB672ED371F44F64F845CAF9894383E7D473CC1E8C5F9D2CF2820E062819591CCBDD08F15A13306FBBD386055C95DC7087777E6A7DF2552690307970864B4555907AE9D9423EBC1F7CBCF9C497D2F30EE7B3EEDFFE05CF42C891DC792576C140D6F7CCCF525FBA564D7D6253A61EB49F97F91408BF42B4B357958161C000D208E165B9F0A91B42A4F52EDE4F592E7D63D768CBC19E9A9978853A7AE5B79DE685DE2ACDD5DB9402C43212E329B2C2A240312C3F6F416C77EC482A6B3DC0E1773A83EE0519073661674A7233A113F10DCD3E6B32AC788DC3B05497158F3FAB0030C84044B94FD846836BFCDA018292E5D93A2CEE8F7E9D04025544682A6DCB9BD3C47E1D6C2FC78F25EEBC1CBA0837DE74F340AA2AC84A3F7FA86EBD8A5AC811815479D2C27B60F4D003DFB0187B902EC02F902E801835F741DB9010020000000000000000000000000000000000000000000100000000000020000000000000000000000000000000000000000008000000000000000000000000000000000000000008000000008000000800000000000000000000100000000000000000000020000000000000000000800000000000800100080000010000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000100000000000000000000000004000000002000000000001000002000020000000000000000000100000000020000000000000000000000000000000000000000000000000000000000000100000F901DDF89B94E261D618A959AFFFD53168CD07D12E37B26761DBF863A0DDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EFA000000000000000000000000012948955847FD30F5D9BEA99E15AFA468CFCF559A00000000000000000000000000000000000000000000000000000000000000000A000000000000000000000000000000000000000000000043C33C1937564800000F9013D940000000000000000000000000000000000001010F884A04DFE1BBBCF077DDC3E01291EEA2D5C70C2B422B415D95645B9ADCFD678CB1D63A00000000000000000000000000000000000000000000000000000000000001010A000000000000000000000000012948955847FD30F5D9BEA99E15AFA468CFCF559A000000000000000000000000083D69448F88BF9C701C1B93F43E1F753D39B2632B8A0000000000000000000000000000000000000000000000000000A9B4D6C4C785A00000000000000000000000000000000000000000000000208F44E1226C6E94A0000000000000000000000000000000000000000000018FD2C6C9207D89CA08600000000000000000000000000000000000000000000000208E9B2C4BA7A70F00000000000000000000000000000000000000000000018FD2C772D5544E918E0B905FDF905FAF8F1A08977A0A0A835E0EE6A7DB95B0D2425A2DD56B38CC387B08B5CCEF8344039CBC8A0DB3E0BA2145EA3106EF41D275FCB7B278E0678B554D1F78FCA25C7E4134F1EC0A01BC87110E378286C1AEA321AA14B209130E8DB09566FEE3DA880E21D5E93E6F5A0DF8BF8EF01A2F00BB9BCAF1E94F7DF91CF0839B37DDEA479589EDA6E970AAD55A08AB7019A98094920982FAF6F27F74FBE5E327C1E412F43A441696E8DED8AF4D6A05E54C6FDBC696A8E8F9D3448500132919AA85DCBD80D84E439EBB94E9BA6510F8080A0F398FD81CDBB82CD60A7A8E14765A2D7D842B1439EF5D2885E8D89CC7D844F1A8080808080808080F90211A08617650F7926C265C96BCECB53FB7E422C10034E144468B7DC19760FC051D2BBA0E46E6024C84B94450B026C199009024F8BC1B5CCB31C62F08E801D1FC2B5CCD3A0D5A9CF8CCCE1A8D9886AFEF2A929F42453C5CC9FFC9F57FF15EE9B0E7E219D8CA0148FF439ACF0BB570DF93AF508A0964BCD00CD2AA233D16DFE99EB51F9246580A0E8EB168E07EDFB9623987A63323C99176068A77F5F4463274C02C1D72F1DF7B8A00D4B42BA66ED38C37F33E52031FBB40465AF252DF320D1EE1F69F7CA36155684A0DC8F8C80755D718CA5CC000815941C5A6DD3E5C6B1876E59C37555AE45E0FE66A0A9DA229A983020A8E8FB0A77F706095AA589F1D82C94079B0F8F721B5EFA08FEA0770CEA571FC3FCFE3CAF735F84D7D0203B2350A2FB9D627D325B17D39CA6F806A05FBC2B23F2325C00F64A9B9349F78AA7FC3F31F80E8F45128A93CC0291A3542FA0B9B2D14D770E68D051FBD186EEB0F1B8E020CB002654009C0D6323692558B3B0A0D59110FB15CB59037E74C897376849A22E4149D3D09E7011E86DD29D463C80ABA094EB1DA76D0B041E52A09D8AA1E4D19234E679C1310D43F66FF26526E6132D05A0DB1F99D8D8A7EB13C64CE597AE6604CD003E84EAFD9E30F575E2C420CFFA0BBCA04D17F337F09825A4721BD350B3FC9F337582D4CDC237346B0F78A634913E97D2A08076018C3FA730D592F8572F3B2F5C41839A5A6A77008214FA771841426C1ABC80F902F020B902EC02F902E801835F741DB9010020000000000000000000000000000000000000000000100000000000020000000000000000000000000000000000000000008000000000000000000000000000000000000000008000000008000000800000000000000000000100000000000000000000020000000000000000000800000000000800100080000010000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000100000000000000000000000004000000002000000000001000002000020000000000000000000100000000020000000000000000000000000000000000000000000000000000000000000100000F901DDF89B94E261D618A959AFFFD53168CD07D12E37B26761DBF863A0DDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EFA000000000000000000000000012948955847FD30F5D9BEA99E15AFA468CFCF559A00000000000000000000000000000000000000000000000000000000000000000A000000000000000000000000000000000000000000000043C33C1937564800000F9013D940000000000000000000000000000000000001010F884A04DFE1BBBCF077DDC3E01291EEA2D5C70C2B422B415D95645B9ADCFD678CB1D63A00000000000000000000000000000000000000000000000000000000000001010A000000000000000000000000012948955847FD30F5D9BEA99E15AFA468CFCF559A000000000000000000000000083D69448F88BF9C701C1B93F43E1F753D39B2632B8A0000000000000000000000000000000000000000000000000000A9B4D6C4C785A00000000000000000000000000000000000000000000000208F44E1226C6E94A0000000000000000000000000000000000000000000018FD2C6C9207D89CA08600000000000000000000000000000000000000000000000208E9B2C4BA7A70F00000000000000000000000000000000000000000000018FD2C772D5544E918E082002580 )
    • RootChainProxy.headerBlocks( 824250000 ) => ( root=20F8FAF70DB524068C55478425474984AFDB8AEB570F8B445B903B9CF78A82C8, start=72061862, end=72062373, createdAt=1748420591, proposer=0xb9EDE6f94D192073D8eaF85f8db677133d483249 )
    • MintableERC20PredicateProxy.8274664f( )
      • MintableERC20Predicate.exitTokens( 0x12948955847fD30f5D9beA99E15AfA468cfCf559, rootToken=0x5fab9761d60419C9eeEbe3915A8FA1ed7e8d2E1B, log=0xF89B94E261D618A959AFFFD53168CD07D12E37B26761DBF863A0DDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EFA000000000000000000000000012948955847FD30F5D9BEA99E15AFA468CFCF559A00000000000000000000000000000000000000000000000000000000000000000A000000000000000000000000000000000000000000000043C33C1937564800000 )
        • ERC1967Proxy.70a08231( )
          • Dimo.balanceOf( account=0x9923263fA127b3d1484cFD649df8f1831c2A74e4 ) => ( 764787508409562324666847797 )
          • ERC1967Proxy.a9059cbb( )
            • Dimo.transfer( to=0x12948955847fD30f5D9beA99E15AfA468cfCf559, amount=20000000000000000000000 ) => ( True )
              exit[RootChainManager (ln:313)]
              File 1 of 7: 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 7: ERC1967Proxy
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              
              /**
               * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
               * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
               * be specified by overriding the virtual {_implementation} function.
               *
               * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
               * different contract through the {_delegate} function.
               *
               * The success and return data of the delegated call will be returned back to the caller of the proxy.
               */
              abstract contract Proxy {
                  /**
                   * @dev Delegates the current call to `implementation`.
                   *
                   * This function does not return to its internall call site, it will return directly to the external caller.
                   */
                  function _delegate(address implementation) internal virtual {
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                      // Copy msg.data. We take full control of memory in this inline assembly
                      // block because it will not return to Solidity code. We overwrite the
                      // Solidity scratch pad at memory position 0.
                          calldatacopy(0, 0, calldatasize())
              
                      // Call the implementation.
                      // out and outsize are 0 because we don't know the size yet.
                          let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
              
                      // Copy the returned data.
                          returndatacopy(0, 0, returndatasize())
              
                          switch result
                          // delegatecall returns 0 on error.
                          case 0 { revert(0, returndatasize()) }
                          default { return(0, returndatasize()) }
                      }
                  }
              
                  /**
                   * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
                   * and {_fallback} should delegate.
                   */
                  function _implementation() internal view virtual returns (address);
              
                  /**
                   * @dev Delegates the current call to the address returned by `_implementation()`.
                   *
                   * This function does not return to its internall call site, it will return directly to the external caller.
                   */
                  function _fallback() internal virtual {
                      _beforeFallback();
                      _delegate(_implementation());
                  }
              
                  /**
                   * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
                   * function in the contract matches the call data.
                   */
                  fallback () external payable virtual {
                      _fallback();
                  }
              
                  /**
                   * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
                   * is empty.
                   */
                  receive () external payable virtual {
                      _fallback();
                  }
              
                  /**
                   * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
                   * call, or as part of the Solidity `fallback` or `receive` functions.
                   *
                   * If overriden should call `super._beforeFallback()`.
                   */
                  function _beforeFallback() internal virtual {
                  }
              }
              
              
              /**
               * @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 ERC1967Upgrade {
                  // 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 Emitted when the implementation is upgraded.
                   */
                  event Upgraded(address indexed implementation);
              
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _getImplementation() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                  }
              
                  /**
                   * @dev Stores a new address in the EIP1967 implementation slot.
                   */
                  function _setImplementation(address newImplementation) private {
                      require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                      StorageSlot.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 {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                      if (data.length > 0 || forceCall) {
                          Address.functionDelegateCall(newImplementation, data);
                      }
                  }
              
                  /**
                   * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeToAndCallSecure(address newImplementation, bytes memory data, bool forceCall) internal {
                      address oldImplementation = _getImplementation();
              
                      // Initial upgrade and setup call
                      _setImplementation(newImplementation);
                      if (data.length > 0 || forceCall) {
                          Address.functionDelegateCall(newImplementation, data);
                      }
              
                      // Perform rollback test if not already in progress
                      StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT);
                      if (!rollbackTesting.value) {
                          // Trigger rollback using upgradeTo from the new implementation
                          rollbackTesting.value = true;
                          Address.functionDelegateCall(
                              newImplementation,
                              abi.encodeWithSignature(
                                  "upgradeTo(address)",
                                  oldImplementation
                              )
                          );
                          rollbackTesting.value = false;
                          // Check rollback was effective
                          require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");
                          // Finally reset to the new implementation and log the upgrade
                          _setImplementation(newImplementation);
                          emit Upgraded(newImplementation);
                      }
                  }
              
                  /**
                   * @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) {
                          Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                      }
                  }
              
                  /**
                   * @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 Emitted when the admin account has changed.
                   */
                  event AdminChanged(address previousAdmin, address newAdmin);
              
                  /**
                   * @dev Returns the current admin.
                   */
                  function _getAdmin() internal view returns (address) {
                      return StorageSlot.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");
                      StorageSlot.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 Emitted when the beacon is upgraded.
                   */
                  event BeaconUpgraded(address indexed beacon);
              
                  /**
                   * @dev Returns the current beacon.
                   */
                  function _getBeacon() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
                  }
              
                  /**
                   * @dev Stores a new beacon in the EIP1967 beacon slot.
                   */
                  function _setBeacon(address newBeacon) private {
                      require(
                          Address.isContract(newBeacon),
                          "ERC1967: new beacon is not a contract"
                      );
                      require(
                          Address.isContract(IBeacon(newBeacon).implementation()),
                          "ERC1967: beacon implementation is not a contract"
                      );
                      StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
                  }
              }
              
              /**
               * @dev This is the interface that {BeaconProxy} expects of its beacon.
               */
              interface IBeacon {
                  /**
                   * @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);
              }
              
              /**
               * @dev Collection of functions related to the address type
               */
              library Address {
                  /**
                   * @dev Returns true if `account` is a contract.
                   *
                   * [IMPORTANT]
                   * ====
                   * It is unsafe to assume that an address for which this function returns
                   * false is an externally-owned account (EOA) and not a contract.
                   *
                   * Among others, `isContract` will return false for the following
                   * types of addresses:
                   *
                   *  - an externally-owned account
                   *  - a contract in construction
                   *  - an address where a contract will be created
                   *  - an address where a contract lived, but was destroyed
                   * ====
                   */
                  function isContract(address account) internal view returns (bool) {
                      // This method relies on extcodesize, which returns 0 for contracts in
                      // construction, since the code is only stored at the end of the
                      // constructor execution.
              
                      uint256 size;
                      // solhint-disable-next-line no-inline-assembly
                      assembly { size := extcodesize(account) }
                      return size > 0;
                  }
              
                  /**
                   * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                   * `recipient`, forwarding all available gas and reverting on errors.
                   *
                   * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                   * of certain opcodes, possibly making contracts go over the 2300 gas limit
                   * imposed by `transfer`, making them unable to receive funds via
                   * `transfer`. {sendValue} removes this limitation.
                   *
                   * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                   *
                   * IMPORTANT: because control is transferred to `recipient`, care must be
                   * taken to not create reentrancy vulnerabilities. Consider using
                   * {ReentrancyGuard} or the
                   * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                   */
                  function sendValue(address payable recipient, uint256 amount) internal {
                      require(address(this).balance >= amount, "Address: insufficient balance");
              
                      // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                      (bool success, ) = recipient.call{ value: amount }("");
                      require(success, "Address: unable to send value, recipient may have reverted");
                  }
              
                  /**
                   * @dev Performs a Solidity function call using a low level `call`. A
                   * plain`call` is an unsafe replacement for a function call: use this
                   * function instead.
                   *
                   * If `target` reverts with a revert reason, it is bubbled up by this
                   * function (like regular Solidity function calls).
                   *
                   * Returns the raw returned data. To convert to the expected return value,
                   * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                   *
                   * Requirements:
                   *
                   * - `target` must be a contract.
                   * - calling `target` with `data` must not revert.
                   *
                   * _Available since v3.1._
                   */
                  function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionCall(target, data, "Address: low-level call failed");
                  }
              
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                   * `errorMessage` as a fallback revert reason when `target` reverts.
                   *
                   * _Available since v3.1._
                   */
                  function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, 0, errorMessage);
                  }
              
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but also transferring `value` wei to `target`.
                   *
                   * Requirements:
                   *
                   * - the calling contract must have an ETH balance of at least `value`.
                   * - the called Solidity function must be `payable`.
                   *
                   * _Available since v3.1._
                   */
                  function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                  }
              
                  /**
                   * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                   * with `errorMessage` as a fallback revert reason when `target` reverts.
                   *
                   * _Available since v3.1._
                   */
                  function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
                      require(address(this).balance >= value, "Address: insufficient balance for call");
                      require(isContract(target), "Address: call to non-contract");
              
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory returndata) = target.call{ value: value }(data);
                      return _verifyCallResult(success, returndata, errorMessage);
                  }
              
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a static call.
                   *
                   * _Available since v3.3._
                   */
                  function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                      return functionStaticCall(target, data, "Address: low-level static call failed");
                  }
              
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                   * but performing a static call.
                   *
                   * _Available since v3.3._
                   */
                  function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
                      require(isContract(target), "Address: static call to non-contract");
              
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory returndata) = target.staticcall(data);
                      return _verifyCallResult(success, returndata, errorMessage);
                  }
              
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a delegate call.
                   *
                   * _Available since v3.4._
                   */
                  function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                  }
              
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                   * but performing a delegate call.
                   *
                   * _Available since v3.4._
                   */
                  function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                      require(isContract(target), "Address: delegate call to non-contract");
              
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory returndata) = target.delegatecall(data);
                      return _verifyCallResult(success, returndata, errorMessage);
                  }
              
                  function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
                      if (success) {
                          return returndata;
                      } else {
                          // Look for revert reason and bubble it up if present
                          if (returndata.length > 0) {
                              // The easiest way to bubble the revert reason is using memory via assembly
              
                              // solhint-disable-next-line no-inline-assembly
                              assembly {
                                  let returndata_size := mload(returndata)
                                  revert(add(32, returndata), returndata_size)
                              }
                          } else {
                              revert(errorMessage);
                          }
                      }
                  }
              }
              
              /**
               * @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:
               * ```
               * 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`, and `uint256`._
               */
              library StorageSlot {
                  struct AddressSlot {
                      address value;
                  }
              
                  struct BooleanSlot {
                      bool value;
                  }
              
                  struct Bytes32Slot {
                      bytes32 value;
                  }
              
                  struct Uint256Slot {
                      uint256 value;
                  }
              
                  /**
                   * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                   */
                  function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
              
                  /**
                   * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                   */
                  function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
              
                  /**
                   * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                   */
                  function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
              
                  /**
                   * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                   */
                  function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
              }
              
              /*
               * @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) {
                      this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                      return msg.data;
                  }
              }
              
              /**
               * @dev Contract module which provides a basic access control mechanism, where
               * there is an account (an owner) that can be granted exclusive access to
               * specific functions.
               *
               * By default, the owner account will be the one that deploys the contract. This
               * can later be changed with {transferOwnership}.
               *
               * This module is used through inheritance. It will make available the modifier
               * `onlyOwner`, which can be applied to your functions to restrict their use to
               * the owner.
               */
              abstract contract Ownable is Context {
                  address private _owner;
              
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
              
                  /**
                   * @dev Initializes the contract setting the deployer as the initial owner.
                   */
                  constructor () {
                      address msgSender = _msgSender();
                      _owner = msgSender;
                      emit OwnershipTransferred(address(0), msgSender);
                  }
              
                  /**
                   * @dev Returns the address of the current owner.
                   */
                  function owner() public view virtual returns (address) {
                      return _owner;
                  }
              
                  /**
                   * @dev Throws if called by any account other than the owner.
                   */
                  modifier onlyOwner() {
                      require(owner() == _msgSender(), "Ownable: caller is not the owner");
                      _;
                  }
              
                  /**
                   * @dev Leaves the contract without owner. It will not be possible to call
                   * `onlyOwner` functions anymore. Can only be called by the current owner.
                   *
                   * NOTE: Renouncing ownership will leave the contract without an owner,
                   * thereby removing any functionality that is only available to the owner.
                   */
                  function renounceOwnership() public virtual onlyOwner {
                      emit OwnershipTransferred(_owner, address(0));
                      _owner = address(0);
                  }
              
                  /**
                   * @dev Transfers ownership of the contract to a new account (`newOwner`).
                   * Can only be called by the current owner.
                   */
                  function transferOwnership(address newOwner) public virtual onlyOwner {
                      require(newOwner != address(0), "Ownable: new owner is the zero address");
                      emit OwnershipTransferred(_owner, newOwner);
                      _owner = newOwner;
                  }
              }
              
              /**
               * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an
               * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.
               */
              contract ProxyAdmin is Ownable {
              
                  /**
                   * @dev Returns the current implementation of `proxy`.
                   *
                   * Requirements:
                   *
                   * - This contract must be the admin of `proxy`.
                   */
                  function getProxyImplementation(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
                      // We need to manually run the static call since the getter cannot be flagged as view
                      // bytes4(keccak256("implementation()")) == 0x5c60da1b
                      (bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b");
                      require(success);
                      return abi.decode(returndata, (address));
                  }
              
                  /**
                   * @dev Returns the current admin of `proxy`.
                   *
                   * Requirements:
                   *
                   * - This contract must be the admin of `proxy`.
                   */
                  function getProxyAdmin(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
                      // We need to manually run the static call since the getter cannot be flagged as view
                      // bytes4(keccak256("admin()")) == 0xf851a440
                      (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440");
                      require(success);
                      return abi.decode(returndata, (address));
                  }
              
                  /**
                   * @dev Changes the admin of `proxy` to `newAdmin`.
                   *
                   * Requirements:
                   *
                   * - This contract must be the current admin of `proxy`.
                   */
                  function changeProxyAdmin(TransparentUpgradeableProxy proxy, address newAdmin) public virtual onlyOwner {
                      proxy.changeAdmin(newAdmin);
                  }
              
                  /**
                   * @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}.
                   *
                   * Requirements:
                   *
                   * - This contract must be the admin of `proxy`.
                   */
                  function upgrade(TransparentUpgradeableProxy proxy, address implementation) public virtual onlyOwner {
                      proxy.upgradeTo(implementation);
                  }
              
                  /**
                   * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See
                   * {TransparentUpgradeableProxy-upgradeToAndCall}.
                   *
                   * Requirements:
                   *
                   * - This contract must be the admin of `proxy`.
                   */
                  function upgradeAndCall(TransparentUpgradeableProxy proxy, address implementation, bytes memory data) public payable virtual onlyOwner {
                      proxy.upgradeToAndCall{value: msg.value}(implementation, data);
                  }
              }
              
              
              /**
               * @dev Base contract for building openzeppelin-upgrades compatible implementations for the {ERC1967Proxy}. It includes
               * publicly available upgrade functions that are called by the plugin and by the secure upgrade mechanism to verify
               * continuation of the upgradability.
               *
               * The {_authorizeUpgrade} function MUST be overridden to include access restriction to the upgrade mechanism.
               *
               * _Available since v4.1._
               */
              abstract contract UUPSUpgradeable is ERC1967Upgrade {
                  function upgradeTo(address newImplementation) external virtual {
                      _authorizeUpgrade(newImplementation);
                      _upgradeToAndCallSecure(newImplementation, bytes(""), false);
                  }
              
                  function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual {
                      _authorizeUpgrade(newImplementation);
                      _upgradeToAndCallSecure(newImplementation, data, true);
                  }
              
                  function _authorizeUpgrade(address newImplementation) internal virtual;
              }
              
              
              abstract contract Proxiable is UUPSUpgradeable {
                  function _authorizeUpgrade(address newImplementation) internal override {
                      _beforeUpgrade(newImplementation);
                  }
              
                  function _beforeUpgrade(address newImplementation) internal virtual;
              }
              
              contract ChildOfProxiable is Proxiable {
                  function _beforeUpgrade(address newImplementation) internal virtual override {}
              }
              
              
              /**
               * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
               * implementation address that can be changed. This address is stored in storage in the location specified by
               * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
               * implementation behind the proxy.
               */
              contract ERC1967Proxy is Proxy, ERC1967Upgrade {
                  /**
                   * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
                   *
                   * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
                   * function call, and allows initializating the storage of the proxy like a Solidity constructor.
                   */
                  constructor(address _logic, bytes memory _data) payable {
                      assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
                      _upgradeToAndCall(_logic, _data, false);
                  }
              
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _implementation() internal view virtual override returns (address impl) {
                      return ERC1967Upgrade._getImplementation();
                  }
              }
              
              /**
               * @dev This contract implements a proxy that is upgradeable by an admin.
               *
               * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
               * clashing], which can potentially be used in an attack, this contract uses the
               * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
               * things that go hand in hand:
               *
               * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
               * that call matches one of the admin functions exposed by the proxy itself.
               * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
               * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
               * "admin cannot fallback to proxy target".
               *
               * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
               * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
               * to sudden errors when trying to call a function from the proxy implementation.
               *
               * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
               * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
               */
              contract TransparentUpgradeableProxy is ERC1967Proxy {
                  /**
                   * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
                   * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
                   */
                  constructor(address _logic, address admin_, bytes memory _data) payable ERC1967Proxy(_logic, _data) {
                      assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
                      _changeAdmin(admin_);
                  }
              
                  /**
                   * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
                   */
                  modifier ifAdmin() {
                      if (msg.sender == _getAdmin()) {
                          _;
                      } else {
                          _fallback();
                      }
                  }
              
                  /**
                   * @dev Returns the current admin.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
                   */
                  function admin() external ifAdmin returns (address admin_) {
                      admin_ = _getAdmin();
                  }
              
                  /**
                   * @dev Returns the current implementation.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
                   */
                  function implementation() external ifAdmin returns (address implementation_) {
                      implementation_ = _implementation();
                  }
              
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {AdminChanged} event.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
                   */
                  function changeAdmin(address newAdmin) external virtual ifAdmin {
                      _changeAdmin(newAdmin);
                  }
              
                  /**
                   * @dev Upgrade the implementation of the proxy.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
                   */
                  function upgradeTo(address newImplementation) external ifAdmin {
                      _upgradeToAndCall(newImplementation, bytes(""), false);
                  }
              
                  /**
                   * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
                   * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
                   * proxied contract.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
                   */
                  function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
                      _upgradeToAndCall(newImplementation, data, true);
                  }
              
                  /**
                   * @dev Returns the current admin.
                   */
                  function _admin() internal view virtual returns (address) {
                      return _getAdmin();
                  }
              
                  /**
                   * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
                   */
                  function _beforeFallback() internal virtual override {
                      require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
                      super._beforeFallback();
                  }
              }
              
              
              // Kept for backwards compatibility with older versions of Hardhat and Truffle plugins.
              contract AdminUpgradeabilityProxy is TransparentUpgradeableProxy {
                  constructor(address logic, address admin, bytes memory data) payable TransparentUpgradeableProxy(logic, admin, data) {}
              }

              File 3 of 7: MintableERC20PredicateProxy
              // 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/MintableERC20PredicateProxy.sol
              
              pragma solidity 0.6.6;
              
              
              contract MintableERC20PredicateProxy is UpgradableProxy {
                  constructor(address _proxyTo)
                      public
                      UpgradableProxy(_proxyTo)
                  {}
              }

              File 4 of 7: 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 7: 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 7: MintableERC20Predicate
              pragma solidity 0.6.6;
              import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
              import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
              import {IMintableERC20} from "../RootToken/IMintableERC20.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 MintableERC20Predicate 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("MintableERC20");
                  bytes32 public constant TRANSFER_EVENT_SIG = keccak256(
                      "Transfer(address,address,uint256)"
                  );
                  event LockedMintableERC20(
                      address indexed depositor,
                      address indexed depositReceiver,
                      address indexed rootToken,
                      uint256 amount
                  );
                  event ExitedMintableERC20(
                      address indexed exitor,
                      address indexed rootToken,
                      uint256 amount
                  );
                  constructor() public {
                      // Disable initializer on implementation contract
                      _disableInitializer();
                  }
                  function initialize(address _owner) external initializer {
                      _setupContractId("MintableERC20Predicate");
                      _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 LockedMintableERC20(depositor, depositReceiver, rootToken, amount);
                      // Attempt to perform safe transfer from i.e. check function return value
                      // using low-level call & revert if didn't succeed
                      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 `Transfer` event sig
                          "MintableERC20Predicate: INVALID_SIGNATURE"
                      );
                      address withdrawer = address(logTopicRLPList[1].toUint()); // topic1 is `from` address
                      require(
                          address(logTopicRLPList[2].toUint()) == address(0), // topic2 is `to` address
                          "MintableERC20Predicate: INVALID_RECEIVER"
                      );
                      IMintableERC20 token = IMintableERC20(rootToken);
                      uint256 tokenBalance = token.balanceOf(address(this));
                      uint256 amount = logRLPList[2].toUint();
                      // Checking whether MintableERC20Predicate has enough balance
                      // to transfer `amount` to withdrawer or not
                      //
                      // If no, it'll mint those extra tokens & transfer `amount`
                      // to withdrawer
                      if (tokenBalance < amount) {
                          token.mint(address(this), amount - tokenBalance);
                      }
                      // Attempt to perform safe transfer i.e. check function return value
                      // using low-level call & revert if didn't succeed
                      IERC20(rootToken).safeTransfer(withdrawer, amount);
                      emit ExitedMintableERC20(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");
                      }
                  }
              }
              import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
              pragma solidity 0.6.6;
              interface IMintableERC20 is IERC20 {
                  /**
                   * @notice called by predicate contract to mint tokens while withdrawing
                   * @dev Should be callable only by MintableERC20Predicate
                   * Make sure minting is done only by this function
                   * @param user user address for whom token is being minted
                   * @param amount amount of token being minted
                   */
                  function mint(address user, uint256 amount) external;
              }
              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;
                  }
              }
              

              File 7 of 7: Dimo
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.10;
              import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol";
              import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
              import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
              import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20VotesUpgradeable.sol";
              import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
              import "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol";
              import "./Storage.sol";
              contract Dimo is
                  ERC20BurnableUpgradeable,
                  SnapshotStorage,
                  AccessControlUpgradeable,
                  PausableUpgradeable,
                  ERC20VotesUpgradeable,
                  UUPSUpgradeable
              {
                  bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
                  bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
                  bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");
                  function pause() external onlyRole(PAUSER_ROLE) {
                      _pause();
                  }
                  function unpause() external onlyRole(PAUSER_ROLE) {
                      _unpause();
                  }
                  function mint(address to, uint256 amount) external onlyRole(MINTER_ROLE) {
                      _mint(to, amount);
                  }
                  function _mint(
                      address to,
                      uint256 amount
                  ) internal override(ERC20Upgradeable, ERC20VotesUpgradeable) {
                      super._mint(to, amount);
                  }
                  function _burn(
                      address account,
                      uint256 amount
                  ) internal override(ERC20Upgradeable, ERC20VotesUpgradeable) {
                      super._burn(account, amount);
                  }
                  function _afterTokenTransfer(
                      address from,
                      address to,
                      uint256 amount
                  ) internal override(ERC20Upgradeable, ERC20VotesUpgradeable) {
                      super._afterTokenTransfer(from, to, amount);
                  }
                  function _beforeTokenTransfer(
                      address from,
                      address to,
                      uint256 amount
                  ) internal override whenNotPaused {
                      super._beforeTokenTransfer(from, to, amount);
                  }
                  function _authorizeUpgrade(
                      address newImplementation
                  ) internal override onlyRole(UPGRADER_ROLE) {}
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.10;
              import "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol";
              struct Snapshots {
                  uint256[] ids;
                  uint256[] values;
              }
              contract SnapshotStorage {
                  using CountersUpgradeable for CountersUpgradeable.Counter;
                  mapping(address => Snapshots) private _accountBalanceSnapshots;
                  Snapshots private _totalSupplySnapshots;
                  CountersUpgradeable.Counter private _currentSnapshotId;
                  uint256[46] private __gap;
              }// SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.0-rc.1) (access/AccessControl.sol)
              pragma solidity ^0.8.0;
              import "./IAccessControlUpgradeable.sol";
              import "../utils/ContextUpgradeable.sol";
              import "../utils/StringsUpgradeable.sol";
              import "../utils/introspection/ERC165Upgradeable.sol";
              import "../proxy/utils/Initializable.sol";
              /**
               * @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:
               *
               * ```
               * 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 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;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)
              pragma solidity ^0.8.0;
              /**
               * @title Counters
               * @author Matt Condon (@shrugs)
               * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
               * of elements in a mapping, issuing ERC721 ids, or counting request ids.
               *
               * Include with `using Counters for Counters.Counter;`
               */
              library CountersUpgradeable {
                  struct Counter {
                      // This variable should never be directly accessed by users of the library: interactions must be restricted to
                      // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
                      // this feature: see https://github.com/ethereum/solidity/issues/4637
                      uint256 _value; // default: 0
                  }
                  function current(Counter storage counter) internal view returns (uint256) {
                      return counter._value;
                  }
                  function increment(Counter storage counter) internal {
                      unchecked {
                          counter._value += 1;
                      }
                  }
                  function decrement(Counter storage counter) internal {
                      uint256 value = counter._value;
                      require(value > 0, "Counter: decrement overflow");
                      unchecked {
                          counter._value = value - 1;
                      }
                  }
                  function reset(Counter storage counter) internal {
                      counter._value = 0;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
              pragma solidity ^0.8.0;
              import "../utils/ContextUpgradeable.sol";
              import "../proxy/utils/Initializable.sol";
              /**
               * @dev Contract module which allows children to implement an emergency stop
               * mechanism that can be triggered by an authorized account.
               *
               * This module is used through inheritance. It will make available the
               * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
               * the functions of your contract. Note that they will not be pausable by
               * simply including this module, only once the modifiers are put in place.
               */
              abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
                  /**
                   * @dev Emitted when the pause is triggered by `account`.
                   */
                  event Paused(address account);
                  /**
                   * @dev Emitted when the pause is lifted by `account`.
                   */
                  event Unpaused(address account);
                  bool private _paused;
                  /**
                   * @dev Initializes the contract in unpaused state.
                   */
                  function __Pausable_init() internal onlyInitializing {
                      __Pausable_init_unchained();
                  }
                  function __Pausable_init_unchained() internal onlyInitializing {
                      _paused = false;
                  }
                  /**
                   * @dev Modifier to make a function callable only when the contract is not paused.
                   *
                   * Requirements:
                   *
                   * - The contract must not be paused.
                   */
                  modifier whenNotPaused() {
                      _requireNotPaused();
                      _;
                  }
                  /**
                   * @dev Modifier to make a function callable only when the contract is paused.
                   *
                   * Requirements:
                   *
                   * - The contract must be paused.
                   */
                  modifier whenPaused() {
                      _requirePaused();
                      _;
                  }
                  /**
                   * @dev Returns true if the contract is paused, and false otherwise.
                   */
                  function paused() public view virtual returns (bool) {
                      return _paused;
                  }
                  /**
                   * @dev Throws if the contract is paused.
                   */
                  function _requireNotPaused() internal view virtual {
                      require(!paused(), "Pausable: paused");
                  }
                  /**
                   * @dev Throws if the contract is not paused.
                   */
                  function _requirePaused() internal view virtual {
                      require(paused(), "Pausable: not paused");
                  }
                  /**
                   * @dev Triggers stopped state.
                   *
                   * Requirements:
                   *
                   * - The contract must not be paused.
                   */
                  function _pause() internal virtual whenNotPaused {
                      _paused = true;
                      emit Paused(_msgSender());
                  }
                  /**
                   * @dev Returns to normal state.
                   *
                   * Requirements:
                   *
                   * - The contract must be paused.
                   */
                  function _unpause() internal virtual whenPaused {
                      _paused = false;
                      emit Unpaused(_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;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Burnable.sol)
              pragma solidity ^0.8.0;
              import "../ERC20Upgradeable.sol";
              import "../../../utils/ContextUpgradeable.sol";
              import "../../../proxy/utils/Initializable.sol";
              /**
               * @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 ERC20BurnableUpgradeable is Initializable, ContextUpgradeable, ERC20Upgradeable {
                  function __ERC20Burnable_init() internal onlyInitializing {
                  }
                  function __ERC20Burnable_init_unchained() internal onlyInitializing {
                  }
                  /**
                   * @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);
                  }
                  /**
                   * @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;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.0-rc.1) (proxy/utils/UUPSUpgradeable.sol)
              pragma solidity ^0.8.0;
              import "../../interfaces/draft-IERC1822Upgradeable.sol";
              import "../ERC1967/ERC1967UpgradeUpgradeable.sol";
              import "./Initializable.sol";
              /**
               * @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.
                   */
                  function upgradeTo(address newImplementation) external 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.
                   */
                  function upgradeToAndCall(address newImplementation, bytes memory data) external 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;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.0-rc.1) (token/ERC20/extensions/ERC20Votes.sol)
              pragma solidity ^0.8.0;
              import "./draft-ERC20PermitUpgradeable.sol";
              import "../../../utils/math/MathUpgradeable.sol";
              import "../../../governance/utils/IVotesUpgradeable.sol";
              import "../../../utils/math/SafeCastUpgradeable.sol";
              import "../../../utils/cryptography/ECDSAUpgradeable.sol";
              import "../../../proxy/utils/Initializable.sol";
              /**
               * @dev Extension of ERC20 to support Compound-like voting and delegation. This version is more generic than Compound's,
               * and supports token supply up to 2^224^ - 1, while COMP is limited to 2^96^ - 1.
               *
               * NOTE: If exact COMP compatibility is required, use the {ERC20VotesComp} variant of this module.
               *
               * This extension keeps a history (checkpoints) of each account's vote power. Vote power can be delegated either
               * by calling the {delegate} function directly, or by providing a signature to be used with {delegateBySig}. Voting
               * power can be queried through the public accessors {getVotes} and {getPastVotes}.
               *
               * By default, token balance does not account for voting power. This makes transfers cheaper. The downside is that it
               * requires users to delegate to themselves in order to activate checkpoints and have their voting power tracked.
               *
               * _Available since v4.2._
               */
              abstract contract ERC20VotesUpgradeable is Initializable, IVotesUpgradeable, ERC20PermitUpgradeable {
                  function __ERC20Votes_init() internal onlyInitializing {
                  }
                  function __ERC20Votes_init_unchained() internal onlyInitializing {
                  }
                  struct Checkpoint {
                      uint32 fromBlock;
                      uint224 votes;
                  }
                  bytes32 private constant _DELEGATION_TYPEHASH =
                      keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
                  mapping(address => address) private _delegates;
                  mapping(address => Checkpoint[]) private _checkpoints;
                  Checkpoint[] private _totalSupplyCheckpoints;
                  /**
                   * @dev Get the `pos`-th checkpoint for `account`.
                   */
                  function checkpoints(address account, uint32 pos) public view virtual returns (Checkpoint memory) {
                      return _checkpoints[account][pos];
                  }
                  /**
                   * @dev Get number of checkpoints for `account`.
                   */
                  function numCheckpoints(address account) public view virtual returns (uint32) {
                      return SafeCastUpgradeable.toUint32(_checkpoints[account].length);
                  }
                  /**
                   * @dev Get the address `account` is currently delegating to.
                   */
                  function delegates(address account) public view virtual override returns (address) {
                      return _delegates[account];
                  }
                  /**
                   * @dev Gets the current votes balance for `account`
                   */
                  function getVotes(address account) public view virtual override returns (uint256) {
                      uint256 pos = _checkpoints[account].length;
                      return pos == 0 ? 0 : _checkpoints[account][pos - 1].votes;
                  }
                  /**
                   * @dev Retrieve the number of votes for `account` at the end of `blockNumber`.
                   *
                   * Requirements:
                   *
                   * - `blockNumber` must have been already mined
                   */
                  function getPastVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) {
                      require(blockNumber < block.number, "ERC20Votes: block not yet mined");
                      return _checkpointsLookup(_checkpoints[account], blockNumber);
                  }
                  /**
                   * @dev Retrieve the `totalSupply` at the end of `blockNumber`. Note, this value is the sum of all balances.
                   * It is but NOT the sum of all the delegated votes!
                   *
                   * Requirements:
                   *
                   * - `blockNumber` must have been already mined
                   */
                  function getPastTotalSupply(uint256 blockNumber) public view virtual override returns (uint256) {
                      require(blockNumber < block.number, "ERC20Votes: block not yet mined");
                      return _checkpointsLookup(_totalSupplyCheckpoints, blockNumber);
                  }
                  /**
                   * @dev Lookup a value in a list of (sorted) checkpoints.
                   */
                  function _checkpointsLookup(Checkpoint[] storage ckpts, uint256 blockNumber) private view returns (uint256) {
                      // We run a binary search to look for the earliest checkpoint taken after `blockNumber`.
                      //
                      // Initially we check if the block is recent to narrow the search range.
                      // During the loop, the index of the wanted checkpoint remains in the range [low-1, high).
                      // With each iteration, either `low` or `high` is moved towards the middle of the range to maintain the invariant.
                      // - If the middle checkpoint is after `blockNumber`, we look in [low, mid)
                      // - If the middle checkpoint is before or equal to `blockNumber`, we look in [mid+1, high)
                      // Once we reach a single value (when low == high), we've found the right checkpoint at the index high-1, if not
                      // out of bounds (in which case we're looking too far in the past and the result is 0).
                      // Note that if the latest checkpoint available is exactly for `blockNumber`, we end up with an index that is
                      // past the end of the array, so we technically don't find a checkpoint after `blockNumber`, but it works out
                      // the same.
                      uint256 length = ckpts.length;
                      uint256 low = 0;
                      uint256 high = length;
                      if (length > 5) {
                          uint256 mid = length - MathUpgradeable.sqrt(length);
                          if (_unsafeAccess(ckpts, mid).fromBlock > blockNumber) {
                              high = mid;
                          } else {
                              low = mid + 1;
                          }
                      }
                      while (low < high) {
                          uint256 mid = MathUpgradeable.average(low, high);
                          if (_unsafeAccess(ckpts, mid).fromBlock > blockNumber) {
                              high = mid;
                          } else {
                              low = mid + 1;
                          }
                      }
                      return high == 0 ? 0 : _unsafeAccess(ckpts, high - 1).votes;
                  }
                  /**
                   * @dev Delegate votes from the sender to `delegatee`.
                   */
                  function delegate(address delegatee) public virtual override {
                      _delegate(_msgSender(), delegatee);
                  }
                  /**
                   * @dev Delegates votes from signer to `delegatee`
                   */
                  function delegateBySig(
                      address delegatee,
                      uint256 nonce,
                      uint256 expiry,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                  ) public virtual override {
                      require(block.timestamp <= expiry, "ERC20Votes: signature expired");
                      address signer = ECDSAUpgradeable.recover(
                          _hashTypedDataV4(keccak256(abi.encode(_DELEGATION_TYPEHASH, delegatee, nonce, expiry))),
                          v,
                          r,
                          s
                      );
                      require(nonce == _useNonce(signer), "ERC20Votes: invalid nonce");
                      _delegate(signer, delegatee);
                  }
                  /**
                   * @dev Maximum token supply. Defaults to `type(uint224).max` (2^224^ - 1).
                   */
                  function _maxSupply() internal view virtual returns (uint224) {
                      return type(uint224).max;
                  }
                  /**
                   * @dev Snapshots the totalSupply after it has been increased.
                   */
                  function _mint(address account, uint256 amount) internal virtual override {
                      super._mint(account, amount);
                      require(totalSupply() <= _maxSupply(), "ERC20Votes: total supply risks overflowing votes");
                      _writeCheckpoint(_totalSupplyCheckpoints, _add, amount);
                  }
                  /**
                   * @dev Snapshots the totalSupply after it has been decreased.
                   */
                  function _burn(address account, uint256 amount) internal virtual override {
                      super._burn(account, amount);
                      _writeCheckpoint(_totalSupplyCheckpoints, _subtract, amount);
                  }
                  /**
                   * @dev Move voting power when tokens are transferred.
                   *
                   * Emits a {DelegateVotesChanged} event.
                   */
                  function _afterTokenTransfer(
                      address from,
                      address to,
                      uint256 amount
                  ) internal virtual override {
                      super._afterTokenTransfer(from, to, amount);
                      _moveVotingPower(delegates(from), delegates(to), amount);
                  }
                  /**
                   * @dev Change delegation for `delegator` to `delegatee`.
                   *
                   * Emits events {DelegateChanged} and {DelegateVotesChanged}.
                   */
                  function _delegate(address delegator, address delegatee) internal virtual {
                      address currentDelegate = delegates(delegator);
                      uint256 delegatorBalance = balanceOf(delegator);
                      _delegates[delegator] = delegatee;
                      emit DelegateChanged(delegator, currentDelegate, delegatee);
                      _moveVotingPower(currentDelegate, delegatee, delegatorBalance);
                  }
                  function _moveVotingPower(
                      address src,
                      address dst,
                      uint256 amount
                  ) private {
                      if (src != dst && amount > 0) {
                          if (src != address(0)) {
                              (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[src], _subtract, amount);
                              emit DelegateVotesChanged(src, oldWeight, newWeight);
                          }
                          if (dst != address(0)) {
                              (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[dst], _add, amount);
                              emit DelegateVotesChanged(dst, oldWeight, newWeight);
                          }
                      }
                  }
                  function _writeCheckpoint(
                      Checkpoint[] storage ckpts,
                      function(uint256, uint256) view returns (uint256) op,
                      uint256 delta
                  ) private returns (uint256 oldWeight, uint256 newWeight) {
                      uint256 pos = ckpts.length;
                      Checkpoint memory oldCkpt = pos == 0 ? Checkpoint(0, 0) : _unsafeAccess(ckpts, pos - 1);
                      oldWeight = oldCkpt.votes;
                      newWeight = op(oldWeight, delta);
                      if (pos > 0 && oldCkpt.fromBlock == block.number) {
                          _unsafeAccess(ckpts, pos - 1).votes = SafeCastUpgradeable.toUint224(newWeight);
                      } else {
                          ckpts.push(Checkpoint({fromBlock: SafeCastUpgradeable.toUint32(block.number), votes: SafeCastUpgradeable.toUint224(newWeight)}));
                      }
                  }
                  function _add(uint256 a, uint256 b) private pure returns (uint256) {
                      return a + b;
                  }
                  function _subtract(uint256 a, uint256 b) private pure returns (uint256) {
                      return a - b;
                  }
                  function _unsafeAccess(Checkpoint[] storage ckpts, uint256 pos) private pure returns (Checkpoint storage result) {
                      assembly {
                          mstore(0, ckpts.slot)
                          result.slot := add(keccak256(0, 0x20), pos)
                      }
                  }
                  /**
                   * @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[47] private __gap;
              }
              // 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;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
              pragma solidity ^0.8.0;
              import "../proxy/utils/Initializable.sol";
              /**
               * @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;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.0-rc.1) (utils/Strings.sol)
              pragma solidity ^0.8.0;
              import "./math/MathUpgradeable.sol";
              /**
               * @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 `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);
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.0-rc.1) (proxy/utils/Initializable.sol)
              pragma solidity ^0.8.2;
              import "../../utils/AddressUpgradeable.sol";
              /**
               * @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]
               * ```
               * 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 Internal function that returns the initialized version. Returns `_initialized`
                   */
                  function _getInitializedVersion() internal view returns (uint8) {
                      return _initialized;
                  }
                  /**
                   * @dev Internal function that returns the initialized version. Returns `_initializing`
                   */
                  function _isInitializing() internal view returns (bool) {
                      return _initializing;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
              pragma solidity ^0.8.0;
              import "./IERC165Upgradeable.sol";
              import "../../proxy/utils/Initializable.sol";
              /**
               * @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;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.0-rc.1) (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
                   * ====
                   *
                   * [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://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");
                      (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 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);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.0-rc.1) (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) {
                              return prod0 / denominator;
                          }
                          // Make sure the result is less than 2^256. Also prevents denominator == 0.
                          require(denominator > prod1);
                          ///////////////////////////////////////////////
                          // 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 10, 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 * 8) < value ? 1 : 0);
                      }
                  }
              }
              // 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);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.0-rc.1) (token/ERC20/ERC20.sol)
              pragma solidity ^0.8.0;
              import "./IERC20Upgradeable.sol";
              import "./extensions/IERC20MetadataUpgradeable.sol";
              import "../../utils/ContextUpgradeable.sol";
              import "../../proxy/utils/Initializable.sol";
              /**
               * @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].
               *
               * 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 ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable, IERC20MetadataUpgradeable {
                  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}.
                   *
                   * The default value of {decimals} is 18. To select a different value for
                   * {decimals} you should overload it.
                   *
                   * All two of these values are immutable: they can only be set once during
                   * construction.
                   */
                  function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {
                      __ERC20_init_unchained(name_, symbol_);
                  }
                  function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
                      _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 value {ERC20} uses, unless this function is
                   * 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 {}
                  /**
                   * @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[45] private __gap;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Interface of the ERC20 standard as defined in the EIP.
               */
              interface IERC20Upgradeable {
                  /**
                   * @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);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
              pragma solidity ^0.8.0;
              import "../IERC20Upgradeable.sol";
              /**
               * @dev Interface for the optional metadata functions from the ERC20 standard.
               *
               * _Available since v4.1._
               */
              interface IERC20MetadataUpgradeable is IERC20Upgradeable {
                  /**
                   * @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);
              }
              // 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);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol)
              pragma solidity ^0.8.2;
              import "../beacon/IBeaconUpgradeable.sol";
              import "../../interfaces/draft-IERC1822Upgradeable.sol";
              import "../../utils/AddressUpgradeable.sol";
              import "../../utils/StorageSlotUpgradeable.sol";
              import "../utils/Initializable.sol";
              /**
               * @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._
               *
               * @custom:oz-upgrades-unsafe-allow delegatecall
               */
              abstract contract ERC1967UpgradeUpgradeable is Initializable {
                  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 Emitted when the implementation is upgraded.
                   */
                  event Upgraded(address indexed implementation);
                  /**
                   * @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) {
                          _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 Emitted when the admin account has changed.
                   */
                  event AdminChanged(address previousAdmin, address newAdmin);
                  /**
                   * @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 Emitted when the beacon is upgraded.
                   */
                  event BeaconUpgraded(address indexed beacon);
                  /**
                   * @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) {
                          _functionDelegateCall(IBeaconUpgradeable(newBeacon).implementation(), data);
                      }
                  }
                  /**
                   * @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) private returns (bytes memory) {
                      require(AddressUpgradeable.isContract(target), "Address: delegate call to non-contract");
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory returndata) = target.delegatecall(data);
                      return AddressUpgradeable.verifyCallResult(success, returndata, "Address: low-level delegate call failed");
                  }
                  /**
                   * @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;
              }
              // 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);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol)
              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:
               * ```
               * 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`, and `uint256`._
               */
              library StorageSlotUpgradeable {
                  struct AddressSlot {
                      address value;
                  }
                  struct BooleanSlot {
                      bool value;
                  }
                  struct Bytes32Slot {
                      bytes32 value;
                  }
                  struct Uint256Slot {
                      uint256 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
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.0-rc.1) (utils/math/SafeCast.sol)
              // This file was procedurally generated from scripts/generate/templates/SafeCast.js.
              pragma solidity ^0.8.0;
              /**
               * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
               * checks.
               *
               * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
               * easily result in undesired exploitation or bugs, since developers usually
               * assume that overflows raise errors. `SafeCast` restores this intuition by
               * reverting the transaction when such 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.
               *
               * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
               * all math on `uint256` and `int256` and then downcasting.
               */
              library SafeCastUpgradeable {
                  /**
                   * @dev Returns the downcasted uint248 from uint256, reverting on
                   * overflow (when the input is greater than largest uint248).
                   *
                   * Counterpart to Solidity's `uint248` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 248 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint248(uint256 value) internal pure returns (uint248) {
                      require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
                      return uint248(value);
                  }
                  /**
                   * @dev Returns the downcasted uint240 from uint256, reverting on
                   * overflow (when the input is greater than largest uint240).
                   *
                   * Counterpart to Solidity's `uint240` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 240 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint240(uint256 value) internal pure returns (uint240) {
                      require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
                      return uint240(value);
                  }
                  /**
                   * @dev Returns the downcasted uint232 from uint256, reverting on
                   * overflow (when the input is greater than largest uint232).
                   *
                   * Counterpart to Solidity's `uint232` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 232 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint232(uint256 value) internal pure returns (uint232) {
                      require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
                      return uint232(value);
                  }
                  /**
                   * @dev Returns the downcasted uint224 from uint256, reverting on
                   * overflow (when the input is greater than largest uint224).
                   *
                   * Counterpart to Solidity's `uint224` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 224 bits
                   *
                   * _Available since v4.2._
                   */
                  function toUint224(uint256 value) internal pure returns (uint224) {
                      require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
                      return uint224(value);
                  }
                  /**
                   * @dev Returns the downcasted uint216 from uint256, reverting on
                   * overflow (when the input is greater than largest uint216).
                   *
                   * Counterpart to Solidity's `uint216` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 216 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint216(uint256 value) internal pure returns (uint216) {
                      require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
                      return uint216(value);
                  }
                  /**
                   * @dev Returns the downcasted uint208 from uint256, reverting on
                   * overflow (when the input is greater than largest uint208).
                   *
                   * Counterpart to Solidity's `uint208` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 208 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint208(uint256 value) internal pure returns (uint208) {
                      require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
                      return uint208(value);
                  }
                  /**
                   * @dev Returns the downcasted uint200 from uint256, reverting on
                   * overflow (when the input is greater than largest uint200).
                   *
                   * Counterpart to Solidity's `uint200` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 200 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint200(uint256 value) internal pure returns (uint200) {
                      require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
                      return uint200(value);
                  }
                  /**
                   * @dev Returns the downcasted uint192 from uint256, reverting on
                   * overflow (when the input is greater than largest uint192).
                   *
                   * Counterpart to Solidity's `uint192` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 192 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint192(uint256 value) internal pure returns (uint192) {
                      require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
                      return uint192(value);
                  }
                  /**
                   * @dev Returns the downcasted uint184 from uint256, reverting on
                   * overflow (when the input is greater than largest uint184).
                   *
                   * Counterpart to Solidity's `uint184` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 184 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint184(uint256 value) internal pure returns (uint184) {
                      require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
                      return uint184(value);
                  }
                  /**
                   * @dev Returns the downcasted uint176 from uint256, reverting on
                   * overflow (when the input is greater than largest uint176).
                   *
                   * Counterpart to Solidity's `uint176` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 176 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint176(uint256 value) internal pure returns (uint176) {
                      require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
                      return uint176(value);
                  }
                  /**
                   * @dev Returns the downcasted uint168 from uint256, reverting on
                   * overflow (when the input is greater than largest uint168).
                   *
                   * Counterpart to Solidity's `uint168` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 168 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint168(uint256 value) internal pure returns (uint168) {
                      require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
                      return uint168(value);
                  }
                  /**
                   * @dev Returns the downcasted uint160 from uint256, reverting on
                   * overflow (when the input is greater than largest uint160).
                   *
                   * Counterpart to Solidity's `uint160` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 160 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint160(uint256 value) internal pure returns (uint160) {
                      require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
                      return uint160(value);
                  }
                  /**
                   * @dev Returns the downcasted uint152 from uint256, reverting on
                   * overflow (when the input is greater than largest uint152).
                   *
                   * Counterpart to Solidity's `uint152` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 152 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint152(uint256 value) internal pure returns (uint152) {
                      require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
                      return uint152(value);
                  }
                  /**
                   * @dev Returns the downcasted uint144 from uint256, reverting on
                   * overflow (when the input is greater than largest uint144).
                   *
                   * Counterpart to Solidity's `uint144` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 144 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint144(uint256 value) internal pure returns (uint144) {
                      require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
                      return uint144(value);
                  }
                  /**
                   * @dev Returns the downcasted uint136 from uint256, reverting on
                   * overflow (when the input is greater than largest uint136).
                   *
                   * Counterpart to Solidity's `uint136` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 136 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint136(uint256 value) internal pure returns (uint136) {
                      require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
                      return uint136(value);
                  }
                  /**
                   * @dev Returns the downcasted uint128 from uint256, reverting on
                   * overflow (when the input is greater than largest uint128).
                   *
                   * Counterpart to Solidity's `uint128` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 128 bits
                   *
                   * _Available since v2.5._
                   */
                  function toUint128(uint256 value) internal pure returns (uint128) {
                      require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
                      return uint128(value);
                  }
                  /**
                   * @dev Returns the downcasted uint120 from uint256, reverting on
                   * overflow (when the input is greater than largest uint120).
                   *
                   * Counterpart to Solidity's `uint120` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 120 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint120(uint256 value) internal pure returns (uint120) {
                      require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
                      return uint120(value);
                  }
                  /**
                   * @dev Returns the downcasted uint112 from uint256, reverting on
                   * overflow (when the input is greater than largest uint112).
                   *
                   * Counterpart to Solidity's `uint112` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 112 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint112(uint256 value) internal pure returns (uint112) {
                      require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
                      return uint112(value);
                  }
                  /**
                   * @dev Returns the downcasted uint104 from uint256, reverting on
                   * overflow (when the input is greater than largest uint104).
                   *
                   * Counterpart to Solidity's `uint104` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 104 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint104(uint256 value) internal pure returns (uint104) {
                      require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
                      return uint104(value);
                  }
                  /**
                   * @dev Returns the downcasted uint96 from uint256, reverting on
                   * overflow (when the input is greater than largest uint96).
                   *
                   * Counterpart to Solidity's `uint96` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 96 bits
                   *
                   * _Available since v4.2._
                   */
                  function toUint96(uint256 value) internal pure returns (uint96) {
                      require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
                      return uint96(value);
                  }
                  /**
                   * @dev Returns the downcasted uint88 from uint256, reverting on
                   * overflow (when the input is greater than largest uint88).
                   *
                   * Counterpart to Solidity's `uint88` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 88 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint88(uint256 value) internal pure returns (uint88) {
                      require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
                      return uint88(value);
                  }
                  /**
                   * @dev Returns the downcasted uint80 from uint256, reverting on
                   * overflow (when the input is greater than largest uint80).
                   *
                   * Counterpart to Solidity's `uint80` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 80 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint80(uint256 value) internal pure returns (uint80) {
                      require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
                      return uint80(value);
                  }
                  /**
                   * @dev Returns the downcasted uint72 from uint256, reverting on
                   * overflow (when the input is greater than largest uint72).
                   *
                   * Counterpart to Solidity's `uint72` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 72 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint72(uint256 value) internal pure returns (uint72) {
                      require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
                      return uint72(value);
                  }
                  /**
                   * @dev Returns the downcasted uint64 from uint256, reverting on
                   * overflow (when the input is greater than largest uint64).
                   *
                   * Counterpart to Solidity's `uint64` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 64 bits
                   *
                   * _Available since v2.5._
                   */
                  function toUint64(uint256 value) internal pure returns (uint64) {
                      require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
                      return uint64(value);
                  }
                  /**
                   * @dev Returns the downcasted uint56 from uint256, reverting on
                   * overflow (when the input is greater than largest uint56).
                   *
                   * Counterpart to Solidity's `uint56` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 56 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint56(uint256 value) internal pure returns (uint56) {
                      require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
                      return uint56(value);
                  }
                  /**
                   * @dev Returns the downcasted uint48 from uint256, reverting on
                   * overflow (when the input is greater than largest uint48).
                   *
                   * Counterpart to Solidity's `uint48` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 48 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint48(uint256 value) internal pure returns (uint48) {
                      require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
                      return uint48(value);
                  }
                  /**
                   * @dev Returns the downcasted uint40 from uint256, reverting on
                   * overflow (when the input is greater than largest uint40).
                   *
                   * Counterpart to Solidity's `uint40` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 40 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint40(uint256 value) internal pure returns (uint40) {
                      require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
                      return uint40(value);
                  }
                  /**
                   * @dev Returns the downcasted uint32 from uint256, reverting on
                   * overflow (when the input is greater than largest uint32).
                   *
                   * Counterpart to Solidity's `uint32` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 32 bits
                   *
                   * _Available since v2.5._
                   */
                  function toUint32(uint256 value) internal pure returns (uint32) {
                      require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
                      return uint32(value);
                  }
                  /**
                   * @dev Returns the downcasted uint24 from uint256, reverting on
                   * overflow (when the input is greater than largest uint24).
                   *
                   * Counterpart to Solidity's `uint24` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 24 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint24(uint256 value) internal pure returns (uint24) {
                      require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
                      return uint24(value);
                  }
                  /**
                   * @dev Returns the downcasted uint16 from uint256, reverting on
                   * overflow (when the input is greater than largest uint16).
                   *
                   * Counterpart to Solidity's `uint16` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 16 bits
                   *
                   * _Available since v2.5._
                   */
                  function toUint16(uint256 value) internal pure returns (uint16) {
                      require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
                      return uint16(value);
                  }
                  /**
                   * @dev Returns the downcasted uint8 from uint256, reverting on
                   * overflow (when the input is greater than largest uint8).
                   *
                   * Counterpart to Solidity's `uint8` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 8 bits
                   *
                   * _Available since v2.5._
                   */
                  function toUint8(uint256 value) internal pure returns (uint8) {
                      require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
                      return uint8(value);
                  }
                  /**
                   * @dev Converts a signed int256 into an unsigned uint256.
                   *
                   * Requirements:
                   *
                   * - input must be greater than or equal to 0.
                   *
                   * _Available since v3.0._
                   */
                  function toUint256(int256 value) internal pure returns (uint256) {
                      require(value >= 0, "SafeCast: value must be positive");
                      return uint256(value);
                  }
                  /**
                   * @dev Returns the downcasted int248 from int256, reverting on
                   * overflow (when the input is less than smallest int248 or
                   * greater than largest int248).
                   *
                   * Counterpart to Solidity's `int248` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 248 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt248(int256 value) internal pure returns (int248 downcasted) {
                      downcasted = int248(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 248 bits");
                  }
                  /**
                   * @dev Returns the downcasted int240 from int256, reverting on
                   * overflow (when the input is less than smallest int240 or
                   * greater than largest int240).
                   *
                   * Counterpart to Solidity's `int240` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 240 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt240(int256 value) internal pure returns (int240 downcasted) {
                      downcasted = int240(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 240 bits");
                  }
                  /**
                   * @dev Returns the downcasted int232 from int256, reverting on
                   * overflow (when the input is less than smallest int232 or
                   * greater than largest int232).
                   *
                   * Counterpart to Solidity's `int232` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 232 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt232(int256 value) internal pure returns (int232 downcasted) {
                      downcasted = int232(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 232 bits");
                  }
                  /**
                   * @dev Returns the downcasted int224 from int256, reverting on
                   * overflow (when the input is less than smallest int224 or
                   * greater than largest int224).
                   *
                   * Counterpart to Solidity's `int224` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 224 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt224(int256 value) internal pure returns (int224 downcasted) {
                      downcasted = int224(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 224 bits");
                  }
                  /**
                   * @dev Returns the downcasted int216 from int256, reverting on
                   * overflow (when the input is less than smallest int216 or
                   * greater than largest int216).
                   *
                   * Counterpart to Solidity's `int216` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 216 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt216(int256 value) internal pure returns (int216 downcasted) {
                      downcasted = int216(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 216 bits");
                  }
                  /**
                   * @dev Returns the downcasted int208 from int256, reverting on
                   * overflow (when the input is less than smallest int208 or
                   * greater than largest int208).
                   *
                   * Counterpart to Solidity's `int208` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 208 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt208(int256 value) internal pure returns (int208 downcasted) {
                      downcasted = int208(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 208 bits");
                  }
                  /**
                   * @dev Returns the downcasted int200 from int256, reverting on
                   * overflow (when the input is less than smallest int200 or
                   * greater than largest int200).
                   *
                   * Counterpart to Solidity's `int200` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 200 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt200(int256 value) internal pure returns (int200 downcasted) {
                      downcasted = int200(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 200 bits");
                  }
                  /**
                   * @dev Returns the downcasted int192 from int256, reverting on
                   * overflow (when the input is less than smallest int192 or
                   * greater than largest int192).
                   *
                   * Counterpart to Solidity's `int192` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 192 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt192(int256 value) internal pure returns (int192 downcasted) {
                      downcasted = int192(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 192 bits");
                  }
                  /**
                   * @dev Returns the downcasted int184 from int256, reverting on
                   * overflow (when the input is less than smallest int184 or
                   * greater than largest int184).
                   *
                   * Counterpart to Solidity's `int184` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 184 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt184(int256 value) internal pure returns (int184 downcasted) {
                      downcasted = int184(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 184 bits");
                  }
                  /**
                   * @dev Returns the downcasted int176 from int256, reverting on
                   * overflow (when the input is less than smallest int176 or
                   * greater than largest int176).
                   *
                   * Counterpart to Solidity's `int176` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 176 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt176(int256 value) internal pure returns (int176 downcasted) {
                      downcasted = int176(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 176 bits");
                  }
                  /**
                   * @dev Returns the downcasted int168 from int256, reverting on
                   * overflow (when the input is less than smallest int168 or
                   * greater than largest int168).
                   *
                   * Counterpart to Solidity's `int168` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 168 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt168(int256 value) internal pure returns (int168 downcasted) {
                      downcasted = int168(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 168 bits");
                  }
                  /**
                   * @dev Returns the downcasted int160 from int256, reverting on
                   * overflow (when the input is less than smallest int160 or
                   * greater than largest int160).
                   *
                   * Counterpart to Solidity's `int160` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 160 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt160(int256 value) internal pure returns (int160 downcasted) {
                      downcasted = int160(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 160 bits");
                  }
                  /**
                   * @dev Returns the downcasted int152 from int256, reverting on
                   * overflow (when the input is less than smallest int152 or
                   * greater than largest int152).
                   *
                   * Counterpart to Solidity's `int152` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 152 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt152(int256 value) internal pure returns (int152 downcasted) {
                      downcasted = int152(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 152 bits");
                  }
                  /**
                   * @dev Returns the downcasted int144 from int256, reverting on
                   * overflow (when the input is less than smallest int144 or
                   * greater than largest int144).
                   *
                   * Counterpart to Solidity's `int144` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 144 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt144(int256 value) internal pure returns (int144 downcasted) {
                      downcasted = int144(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 144 bits");
                  }
                  /**
                   * @dev Returns the downcasted int136 from int256, reverting on
                   * overflow (when the input is less than smallest int136 or
                   * greater than largest int136).
                   *
                   * Counterpart to Solidity's `int136` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 136 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt136(int256 value) internal pure returns (int136 downcasted) {
                      downcasted = int136(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 136 bits");
                  }
                  /**
                   * @dev Returns the downcasted int128 from int256, reverting on
                   * overflow (when the input is less than smallest int128 or
                   * greater than largest int128).
                   *
                   * Counterpart to Solidity's `int128` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 128 bits
                   *
                   * _Available since v3.1._
                   */
                  function toInt128(int256 value) internal pure returns (int128 downcasted) {
                      downcasted = int128(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 128 bits");
                  }
                  /**
                   * @dev Returns the downcasted int120 from int256, reverting on
                   * overflow (when the input is less than smallest int120 or
                   * greater than largest int120).
                   *
                   * Counterpart to Solidity's `int120` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 120 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt120(int256 value) internal pure returns (int120 downcasted) {
                      downcasted = int120(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 120 bits");
                  }
                  /**
                   * @dev Returns the downcasted int112 from int256, reverting on
                   * overflow (when the input is less than smallest int112 or
                   * greater than largest int112).
                   *
                   * Counterpart to Solidity's `int112` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 112 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt112(int256 value) internal pure returns (int112 downcasted) {
                      downcasted = int112(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 112 bits");
                  }
                  /**
                   * @dev Returns the downcasted int104 from int256, reverting on
                   * overflow (when the input is less than smallest int104 or
                   * greater than largest int104).
                   *
                   * Counterpart to Solidity's `int104` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 104 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt104(int256 value) internal pure returns (int104 downcasted) {
                      downcasted = int104(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 104 bits");
                  }
                  /**
                   * @dev Returns the downcasted int96 from int256, reverting on
                   * overflow (when the input is less than smallest int96 or
                   * greater than largest int96).
                   *
                   * Counterpart to Solidity's `int96` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 96 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt96(int256 value) internal pure returns (int96 downcasted) {
                      downcasted = int96(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 96 bits");
                  }
                  /**
                   * @dev Returns the downcasted int88 from int256, reverting on
                   * overflow (when the input is less than smallest int88 or
                   * greater than largest int88).
                   *
                   * Counterpart to Solidity's `int88` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 88 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt88(int256 value) internal pure returns (int88 downcasted) {
                      downcasted = int88(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 88 bits");
                  }
                  /**
                   * @dev Returns the downcasted int80 from int256, reverting on
                   * overflow (when the input is less than smallest int80 or
                   * greater than largest int80).
                   *
                   * Counterpart to Solidity's `int80` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 80 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt80(int256 value) internal pure returns (int80 downcasted) {
                      downcasted = int80(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 80 bits");
                  }
                  /**
                   * @dev Returns the downcasted int72 from int256, reverting on
                   * overflow (when the input is less than smallest int72 or
                   * greater than largest int72).
                   *
                   * Counterpart to Solidity's `int72` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 72 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt72(int256 value) internal pure returns (int72 downcasted) {
                      downcasted = int72(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 72 bits");
                  }
                  /**
                   * @dev Returns the downcasted int64 from int256, reverting on
                   * overflow (when the input is less than smallest int64 or
                   * greater than largest int64).
                   *
                   * Counterpart to Solidity's `int64` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 64 bits
                   *
                   * _Available since v3.1._
                   */
                  function toInt64(int256 value) internal pure returns (int64 downcasted) {
                      downcasted = int64(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 64 bits");
                  }
                  /**
                   * @dev Returns the downcasted int56 from int256, reverting on
                   * overflow (when the input is less than smallest int56 or
                   * greater than largest int56).
                   *
                   * Counterpart to Solidity's `int56` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 56 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt56(int256 value) internal pure returns (int56 downcasted) {
                      downcasted = int56(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 56 bits");
                  }
                  /**
                   * @dev Returns the downcasted int48 from int256, reverting on
                   * overflow (when the input is less than smallest int48 or
                   * greater than largest int48).
                   *
                   * Counterpart to Solidity's `int48` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 48 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt48(int256 value) internal pure returns (int48 downcasted) {
                      downcasted = int48(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 48 bits");
                  }
                  /**
                   * @dev Returns the downcasted int40 from int256, reverting on
                   * overflow (when the input is less than smallest int40 or
                   * greater than largest int40).
                   *
                   * Counterpart to Solidity's `int40` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 40 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt40(int256 value) internal pure returns (int40 downcasted) {
                      downcasted = int40(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 40 bits");
                  }
                  /**
                   * @dev Returns the downcasted int32 from int256, reverting on
                   * overflow (when the input is less than smallest int32 or
                   * greater than largest int32).
                   *
                   * Counterpart to Solidity's `int32` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 32 bits
                   *
                   * _Available since v3.1._
                   */
                  function toInt32(int256 value) internal pure returns (int32 downcasted) {
                      downcasted = int32(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 32 bits");
                  }
                  /**
                   * @dev Returns the downcasted int24 from int256, reverting on
                   * overflow (when the input is less than smallest int24 or
                   * greater than largest int24).
                   *
                   * Counterpart to Solidity's `int24` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 24 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt24(int256 value) internal pure returns (int24 downcasted) {
                      downcasted = int24(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 24 bits");
                  }
                  /**
                   * @dev Returns the downcasted int16 from int256, reverting on
                   * overflow (when the input is less than smallest int16 or
                   * greater than largest int16).
                   *
                   * Counterpart to Solidity's `int16` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 16 bits
                   *
                   * _Available since v3.1._
                   */
                  function toInt16(int256 value) internal pure returns (int16 downcasted) {
                      downcasted = int16(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 16 bits");
                  }
                  /**
                   * @dev Returns the downcasted int8 from int256, reverting on
                   * overflow (when the input is less than smallest int8 or
                   * greater than largest int8).
                   *
                   * Counterpart to Solidity's `int8` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 8 bits
                   *
                   * _Available since v3.1._
                   */
                  function toInt8(int256 value) internal pure returns (int8 downcasted) {
                      downcasted = int8(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 8 bits");
                  }
                  /**
                   * @dev Converts an unsigned uint256 into a signed int256.
                   *
                   * Requirements:
                   *
                   * - input must be less than or equal to maxInt256.
                   *
                   * _Available since v3.0._
                   */
                  function toInt256(uint256 value) internal pure returns (int256) {
                      // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
                      require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
                      return int256(value);
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (governance/utils/IVotes.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Common interface for {ERC20Votes}, {ERC721Votes}, and other {Votes}-enabled contracts.
               *
               * _Available since v4.5._
               */
              interface IVotesUpgradeable {
                  /**
                   * @dev Emitted when an account changes their delegate.
                   */
                  event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
                  /**
                   * @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of votes.
                   */
                  event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance);
                  /**
                   * @dev Returns the current amount of votes that `account` has.
                   */
                  function getVotes(address account) external view returns (uint256);
                  /**
                   * @dev Returns the amount of votes that `account` had at the end of a past block (`blockNumber`).
                   */
                  function getPastVotes(address account, uint256 blockNumber) external view returns (uint256);
                  /**
                   * @dev Returns the total supply of votes available at the end of a past block (`blockNumber`).
                   *
                   * NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes.
                   * Votes that have not been delegated are still part of total supply, even though they would not participate in a
                   * vote.
                   */
                  function getPastTotalSupply(uint256 blockNumber) external view returns (uint256);
                  /**
                   * @dev Returns the delegate that `account` has chosen.
                   */
                  function delegates(address account) external view returns (address);
                  /**
                   * @dev Delegates votes from the sender to `delegatee`.
                   */
                  function delegate(address delegatee) external;
                  /**
                   * @dev Delegates votes from signer to `delegatee`.
                   */
                  function delegateBySig(
                      address delegatee,
                      uint256 nonce,
                      uint256 expiry,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                  ) external;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.0-rc.1) (utils/cryptography/ECDSA.sol)
              pragma solidity ^0.8.0;
              import "../StringsUpgradeable.sol";
              /**
               * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
               *
               * These functions can be used to verify that a message was signed by the holder
               * of the private keys of a given address.
               */
              library ECDSAUpgradeable {
                  enum RecoverError {
                      NoError,
                      InvalidSignature,
                      InvalidSignatureLength,
                      InvalidSignatureS,
                      InvalidSignatureV // Deprecated in v4.8
                  }
                  function _throwError(RecoverError error) private pure {
                      if (error == RecoverError.NoError) {
                          return; // no error: do nothing
                      } else if (error == RecoverError.InvalidSignature) {
                          revert("ECDSA: invalid signature");
                      } else if (error == RecoverError.InvalidSignatureLength) {
                          revert("ECDSA: invalid signature length");
                      } else if (error == RecoverError.InvalidSignatureS) {
                          revert("ECDSA: invalid signature 's' value");
                      }
                  }
                  /**
                   * @dev Returns the address that signed a hashed message (`hash`) with
                   * `signature` or error string. This address can then be used for verification purposes.
                   *
                   * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
                   * this function rejects them by requiring the `s` value to be in the lower
                   * half order, and the `v` value to be either 27 or 28.
                   *
                   * IMPORTANT: `hash` _must_ be the result of a hash operation for the
                   * verification to be secure: it is possible to craft signatures that
                   * recover to arbitrary addresses for non-hashed data. A safe way to ensure
                   * this is by receiving a hash of the original message (which may otherwise
                   * be too long), and then calling {toEthSignedMessageHash} on it.
                   *
                   * Documentation for signature generation:
                   * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
                   * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
                   *
                   * _Available since v4.3._
                   */
                  function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
                      if (signature.length == 65) {
                          bytes32 r;
                          bytes32 s;
                          uint8 v;
                          // ecrecover takes the signature parameters, and the only way to get them
                          // currently is to use assembly.
                          /// @solidity memory-safe-assembly
                          assembly {
                              r := mload(add(signature, 0x20))
                              s := mload(add(signature, 0x40))
                              v := byte(0, mload(add(signature, 0x60)))
                          }
                          return tryRecover(hash, v, r, s);
                      } else {
                          return (address(0), RecoverError.InvalidSignatureLength);
                      }
                  }
                  /**
                   * @dev Returns the address that signed a hashed message (`hash`) with
                   * `signature`. This address can then be used for verification purposes.
                   *
                   * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
                   * this function rejects them by requiring the `s` value to be in the lower
                   * half order, and the `v` value to be either 27 or 28.
                   *
                   * IMPORTANT: `hash` _must_ be the result of a hash operation for the
                   * verification to be secure: it is possible to craft signatures that
                   * recover to arbitrary addresses for non-hashed data. A safe way to ensure
                   * this is by receiving a hash of the original message (which may otherwise
                   * be too long), and then calling {toEthSignedMessageHash} on it.
                   */
                  function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
                      (address recovered, RecoverError error) = tryRecover(hash, signature);
                      _throwError(error);
                      return recovered;
                  }
                  /**
                   * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
                   *
                   * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
                   *
                   * _Available since v4.3._
                   */
                  function tryRecover(
                      bytes32 hash,
                      bytes32 r,
                      bytes32 vs
                  ) internal pure returns (address, RecoverError) {
                      bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
                      uint8 v = uint8((uint256(vs) >> 255) + 27);
                      return tryRecover(hash, v, r, s);
                  }
                  /**
                   * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
                   *
                   * _Available since v4.2._
                   */
                  function recover(
                      bytes32 hash,
                      bytes32 r,
                      bytes32 vs
                  ) internal pure returns (address) {
                      (address recovered, RecoverError error) = tryRecover(hash, r, vs);
                      _throwError(error);
                      return recovered;
                  }
                  /**
                   * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
                   * `r` and `s` signature fields separately.
                   *
                   * _Available since v4.3._
                   */
                  function tryRecover(
                      bytes32 hash,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                  ) internal pure returns (address, RecoverError) {
                      // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
                      // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
                      // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
                      // signatures from current libraries generate a unique signature with an s-value in the lower half order.
                      //
                      // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
                      // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
                      // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
                      // these malleable signatures as well.
                      if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                          return (address(0), RecoverError.InvalidSignatureS);
                      }
                      // If the signature is valid (and not malleable), return the signer address
                      address signer = ecrecover(hash, v, r, s);
                      if (signer == address(0)) {
                          return (address(0), RecoverError.InvalidSignature);
                      }
                      return (signer, RecoverError.NoError);
                  }
                  /**
                   * @dev Overload of {ECDSA-recover} that receives the `v`,
                   * `r` and `s` signature fields separately.
                   */
                  function recover(
                      bytes32 hash,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                  ) internal pure returns (address) {
                      (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
                      _throwError(error);
                      return recovered;
                  }
                  /**
                   * @dev Returns an Ethereum Signed Message, created from a `hash`. This
                   * produces hash corresponding to the one signed with the
                   * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
                   * JSON-RPC method as part of EIP-191.
                   *
                   * See {recover}.
                   */
                  function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
                      // 32 is the length in bytes of hash,
                      // enforced by the type signature above
                      return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
              32", hash));
                  }
                  /**
                   * @dev Returns an Ethereum Signed Message, created from `s`. This
                   * produces hash corresponding to the one signed with the
                   * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
                   * JSON-RPC method as part of EIP-191.
                   *
                   * See {recover}.
                   */
                  function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
                      return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
              ", StringsUpgradeable.toString(s.length), s));
                  }
                  /**
                   * @dev Returns an Ethereum Signed Typed Data, created from a
                   * `domainSeparator` and a `structHash`. This produces hash corresponding
                   * to the one signed with the
                   * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
                   * JSON-RPC method as part of EIP-712.
                   *
                   * See {recover}.
                   */
                  function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
                      return keccak256(abi.encodePacked("\\x19\\x01", domainSeparator, structHash));
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.0-rc.1) (token/ERC20/extensions/draft-ERC20Permit.sol)
              pragma solidity ^0.8.0;
              import "./draft-IERC20PermitUpgradeable.sol";
              import "../ERC20Upgradeable.sol";
              import "../../../utils/cryptography/ECDSAUpgradeable.sol";
              import "../../../utils/cryptography/EIP712Upgradeable.sol";
              import "../../../utils/CountersUpgradeable.sol";
              import "../../../proxy/utils/Initializable.sol";
              /**
               * @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
               * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
               *
               * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
               * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't
               * need to send a transaction, and thus is not required to hold Ether at all.
               *
               * _Available since v3.4._
               *
               * @custom:storage-size 51
               */
              abstract contract ERC20PermitUpgradeable is Initializable, ERC20Upgradeable, IERC20PermitUpgradeable, EIP712Upgradeable {
                  using CountersUpgradeable for CountersUpgradeable.Counter;
                  mapping(address => CountersUpgradeable.Counter) private _nonces;
                  // solhint-disable-next-line var-name-mixedcase
                  bytes32 private constant _PERMIT_TYPEHASH =
                      keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
                  /**
                   * @dev In previous versions `_PERMIT_TYPEHASH` was declared as `immutable`.
                   * However, to ensure consistency with the upgradeable transpiler, we will continue
                   * to reserve a slot.
                   */
                  // solhint-disable-next-line var-name-mixedcase
                  bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT;
                  /**
                   * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
                   *
                   * It's a good idea to use the same `name` that is defined as the ERC20 token name.
                   */
                  function __ERC20Permit_init(string memory name) internal onlyInitializing {
                      __EIP712_init_unchained(name, "1");
                  }
                  function __ERC20Permit_init_unchained(string memory) internal onlyInitializing {}
                  /**
                   * @dev See {IERC20Permit-permit}.
                   */
                  function permit(
                      address owner,
                      address spender,
                      uint256 value,
                      uint256 deadline,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                  ) public virtual override {
                      require(block.timestamp <= deadline, "ERC20Permit: expired deadline");
                      bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));
                      bytes32 hash = _hashTypedDataV4(structHash);
                      address signer = ECDSAUpgradeable.recover(hash, v, r, s);
                      require(signer == owner, "ERC20Permit: invalid signature");
                      _approve(owner, spender, value);
                  }
                  /**
                   * @dev See {IERC20Permit-nonces}.
                   */
                  function nonces(address owner) public view virtual override returns (uint256) {
                      return _nonces[owner].current();
                  }
                  /**
                   * @dev See {IERC20Permit-DOMAIN_SEPARATOR}.
                   */
                  // solhint-disable-next-line func-name-mixedcase
                  function DOMAIN_SEPARATOR() external view override returns (bytes32) {
                      return _domainSeparatorV4();
                  }
                  /**
                   * @dev "Consume a nonce": return the current value and increment.
                   *
                   * _Available since v4.1._
                   */
                  function _useNonce(address owner) internal virtual returns (uint256 current) {
                      CountersUpgradeable.Counter storage nonce = _nonces[owner];
                      current = nonce.current();
                      nonce.increment();
                  }
                  /**
                   * @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;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.0-rc.1) (utils/cryptography/EIP712.sol)
              pragma solidity ^0.8.0;
              import "./ECDSAUpgradeable.sol";
              import "../../proxy/utils/Initializable.sol";
              /**
               * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
               *
               * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
               * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
               * they need in their contracts using a combination of `abi.encode` and `keccak256`.
               *
               * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
               * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
               * ({_hashTypedDataV4}).
               *
               * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
               * the chain id to protect against replay attacks on an eventual fork of the chain.
               *
               * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
               * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
               *
               * _Available since v3.4._
               *
               * @custom:storage-size 52
               */
              abstract contract EIP712Upgradeable is Initializable {
                  /* solhint-disable var-name-mixedcase */
                  bytes32 private _HASHED_NAME;
                  bytes32 private _HASHED_VERSION;
                  bytes32 private constant _TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
                  /* solhint-enable var-name-mixedcase */
                  /**
                   * @dev Initializes the domain separator and parameter caches.
                   *
                   * The meaning of `name` and `version` is specified in
                   * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
                   *
                   * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
                   * - `version`: the current major version of the signing domain.
                   *
                   * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
                   * contract upgrade].
                   */
                  function __EIP712_init(string memory name, string memory version) internal onlyInitializing {
                      __EIP712_init_unchained(name, version);
                  }
                  function __EIP712_init_unchained(string memory name, string memory version) internal onlyInitializing {
                      bytes32 hashedName = keccak256(bytes(name));
                      bytes32 hashedVersion = keccak256(bytes(version));
                      _HASHED_NAME = hashedName;
                      _HASHED_VERSION = hashedVersion;
                  }
                  /**
                   * @dev Returns the domain separator for the current chain.
                   */
                  function _domainSeparatorV4() internal view returns (bytes32) {
                      return _buildDomainSeparator(_TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash());
                  }
                  function _buildDomainSeparator(
                      bytes32 typeHash,
                      bytes32 nameHash,
                      bytes32 versionHash
                  ) private view returns (bytes32) {
                      return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
                  }
                  /**
                   * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
                   * function returns the hash of the fully encoded EIP712 message for this domain.
                   *
                   * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
                   *
                   * ```solidity
                   * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
                   *     keccak256("Mail(address to,string contents)"),
                   *     mailTo,
                   *     keccak256(bytes(mailContents))
                   * )));
                   * address signer = ECDSA.recover(digest, signature);
                   * ```
                   */
                  function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
                      return ECDSAUpgradeable.toTypedDataHash(_domainSeparatorV4(), structHash);
                  }
                  /**
                   * @dev The hash of the name parameter for the EIP712 domain.
                   *
                   * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
                   * are a concern.
                   */
                  function _EIP712NameHash() internal virtual view returns (bytes32) {
                      return _HASHED_NAME;
                  }
                  /**
                   * @dev The hash of the version parameter for the EIP712 domain.
                   *
                   * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
                   * are a concern.
                   */
                  function _EIP712VersionHash() internal virtual view returns (bytes32) {
                      return _HASHED_VERSION;
                  }
                  /**
                   * @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;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
               * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
               *
               * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
               * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
               * need to send a transaction, and thus is not required to hold Ether at all.
               */
              interface IERC20PermitUpgradeable {
                  /**
                   * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
                   * given ``owner``'s signed approval.
                   *
                   * IMPORTANT: The same issues {IERC20-approve} has related to transaction
                   * ordering also apply here.
                   *
                   * Emits an {Approval} event.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   * - `deadline` must be a timestamp in the future.
                   * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
                   * over the EIP712-formatted function arguments.
                   * - the signature must use ``owner``'s current nonce (see {nonces}).
                   *
                   * For more information on the signature format, see the
                   * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
                   * section].
                   */
                  function permit(
                      address owner,
                      address spender,
                      uint256 value,
                      uint256 deadline,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                  ) external;
                  /**
                   * @dev Returns the current nonce for `owner`. This value must be
                   * included whenever a signature is generated for {permit}.
                   *
                   * Every successful call to {permit} increases ``owner``'s nonce by one. This
                   * prevents a signature from being used multiple times.
                   */
                  function nonces(address owner) external view returns (uint256);
                  /**
                   * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
                   */
                  // solhint-disable-next-line func-name-mixedcase
                  function DOMAIN_SEPARATOR() external view returns (bytes32);
              }