ETH Price: $1,874.44 (-0.19%)

Transaction Decoder

Block:
12077660 at Mar-20-2021 07:42:17 PM +UTC
Transaction Fee:
0.0255160053 ETH $47.83
Gas Used:
231,753 Gas / 110.1 Gwei

Emitted Events:

169 MomijiToken.TransferSingle( operator=ERC1155PredicateProxy, from=ERC1155PredicateProxy, to=[Sender] 0x587a533ae239db6d5efad5b9906a4a4b2803951b, id=4591616165509, value=15 )

Account State Difference:

  Address   Before After State Difference Code
0x587A533a...B2803951b
1.827297820467638069 Eth
Nonce: 3991
1.801781815167638069 Eth
Nonce: 3992
0.0255160053
0x7CdC0421...840Ac8931
0xA0c68C63...1bFc77C77
(Polygon (Matic): Bridge)
(Ethermine)
869.984973345907000309 Eth870.010489351207000309 Eth0.0255160053

Execution Trace

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

          File 2 of 6: MomijiToken
          pragma solidity ^0.7.0;
          
          
          /**
           * @dev Collection of functions related to the address type
           */
          library Address {
              /**
               * @dev Returns true if `account` is a contract.
               *
               * [IMPORTANT]
               * ====
               * It is unsafe to assume that an address for which this function returns
               * false is an externally-owned account (EOA) and not a contract.
               *
               * Among others, `isContract` will return false for the following
               * types of addresses:
               *
               *  - an externally-owned account
               *  - a contract in construction
               *  - an address where a contract will be created
               *  - an address where a contract lived, but was destroyed
               * ====
               */
              function isContract(address account) internal view returns (bool) {
                  // This method relies on extcodesize, which returns 0 for contracts in
                  // construction, since the code is only stored at the end of the
                  // constructor execution.
          
                  uint256 size;
                  // solhint-disable-next-line no-inline-assembly
                  assembly { size := extcodesize(account) }
                  return size > 0;
              }
          
              /**
               * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
               * `recipient`, forwarding all available gas and reverting on errors.
               *
               * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
               * of certain opcodes, possibly making contracts go over the 2300 gas limit
               * imposed by `transfer`, making them unable to receive funds via
               * `transfer`. {sendValue} removes this limitation.
               *
               * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
               *
               * IMPORTANT: because control is transferred to `recipient`, care must be
               * taken to not create reentrancy vulnerabilities. Consider using
               * {ReentrancyGuard} or the
               * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
               */
              function sendValue(address payable recipient, uint256 amount) internal {
                  require(address(this).balance >= amount, "Address: insufficient balance");
          
                  // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                  (bool success, ) = recipient.call{ value: amount }("");
                  require(success, "Address: unable to send value, recipient may have reverted");
              }
          
              /**
               * @dev Performs a Solidity function call using a low level `call`. A
               * plain`call` is an unsafe replacement for a function call: use this
               * function instead.
               *
               * If `target` reverts with a revert reason, it is bubbled up by this
               * function (like regular Solidity function calls).
               *
               * Returns the raw returned data. To convert to the expected return value,
               * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
               *
               * Requirements:
               *
               * - `target` must be a contract.
               * - calling `target` with `data` must not revert.
               *
               * _Available since v3.1._
               */
              function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                return functionCall(target, data, "Address: low-level call failed");
              }
          
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
               * `errorMessage` as a fallback revert reason when `target` reverts.
               *
               * _Available since v3.1._
               */
              function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                  return functionCallWithValue(target, data, 0, errorMessage);
              }
          
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
               * but also transferring `value` wei to `target`.
               *
               * Requirements:
               *
               * - the calling contract must have an ETH balance of at least `value`.
               * - the called Solidity function must be `payable`.
               *
               * _Available since v3.1._
               */
              function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                  return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
              }
          
              /**
               * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
               * with `errorMessage` as a fallback revert reason when `target` reverts.
               *
               * _Available since v3.1._
               */
              function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
                  require(address(this).balance >= value, "Address: insufficient balance for call");
                  require(isContract(target), "Address: call to non-contract");
          
                  // solhint-disable-next-line avoid-low-level-calls
                  (bool success, bytes memory returndata) = target.call{ value: value }(data);
                  return _verifyCallResult(success, returndata, errorMessage);
              }
          
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
               * but performing a static call.
               *
               * _Available since v3.3._
               */
              function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                  return functionStaticCall(target, data, "Address: low-level static call failed");
              }
          
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
               * but performing a static call.
               *
               * _Available since v3.3._
               */
              function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
                  require(isContract(target), "Address: static call to non-contract");
          
                  // solhint-disable-next-line avoid-low-level-calls
                  (bool success, bytes memory returndata) = target.staticcall(data);
                  return _verifyCallResult(success, returndata, errorMessage);
              }
          
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
               * but performing a delegate call.
               *
               * _Available since v3.3._
               */
              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.3._
               */
              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 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;
              }
          }
          
          /*
           * @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;
              }
          }
          
          /**
           * @dev Interface of the ERC165 standard, as defined in the
           * https://eips.ethereum.org/EIPS/eip-165[EIP].
           *
           * Implementers can declare support of contract interfaces, which can then be
           * queried by others ({ERC165Checker}).
           *
           * For an implementation, see {ERC165}.
           */
          interface IERC165 {
              /**
               * @dev Returns true if this contract implements the interface defined by
               * `interfaceId`. See the corresponding
               * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
               * to learn more about how these ids are created.
               *
               * This function call must use less than 30 000 gas.
               */
              function supportsInterface(bytes4 interfaceId) external view returns (bool);
          }
          
          
          /**
           * @dev Implementation of the {IERC165} interface.
           *
           * Contracts may inherit from this and call {_registerInterface} to declare
           * their support of an interface.
           */
          abstract contract ERC165 is IERC165 {
              /*
               * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
               */
              bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
          
              /**
               * @dev Mapping of interface ids to whether or not it's supported.
               */
              mapping(bytes4 => bool) private _supportedInterfaces;
          
              constructor () internal {
                  // Derived contracts need only register support for their own interfaces,
                  // we register support for ERC165 itself here
                  _registerInterface(_INTERFACE_ID_ERC165);
              }
          
              /**
               * @dev See {IERC165-supportsInterface}.
               *
               * Time complexity O(1), guaranteed to always use less than 30 000 gas.
               */
              function supportsInterface(bytes4 interfaceId) public view override returns (bool) {
                  return _supportedInterfaces[interfaceId];
              }
          
              /**
               * @dev Registers the contract as an implementer of the interface defined by
               * `interfaceId`. Support of the actual ERC165 interface is automatic and
               * registering its interface id is not required.
               *
               * See {IERC165-supportsInterface}.
               *
               * Requirements:
               *
               * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
               */
              function _registerInterface(bytes4 interfaceId) internal virtual {
                  require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
                  _supportedInterfaces[interfaceId] = true;
              }
          }
          
          
          /**
           * _Available since v3.1._
           */
          interface IERC1155Receiver is IERC165 {
          
              /**
                  @dev Handles the receipt of a single ERC1155 token type. This function is
                  called at the end of a `safeTransferFrom` after the balance has been updated.
                  To accept the transfer, this must return
                  `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
                  (i.e. 0xf23a6e61, or its own function selector).
                  @param operator The address which initiated the transfer (i.e. msg.sender)
                  @param from The address which previously owned the token
                  @param id The ID of the token being transferred
                  @param value The amount of tokens being transferred
                  @param data Additional data with no specified format
                  @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
              */
              function onERC1155Received(
                  address operator,
                  address from,
                  uint256 id,
                  uint256 value,
                  bytes calldata data
              )
                  external
                  returns(bytes4);
          
              /**
                  @dev Handles the receipt of a multiple ERC1155 token types. This function
                  is called at the end of a `safeBatchTransferFrom` after the balances have
                  been updated. To accept the transfer(s), this must return
                  `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
                  (i.e. 0xbc197c81, or its own function selector).
                  @param operator The address which initiated the batch transfer (i.e. msg.sender)
                  @param from The address which previously owned the token
                  @param ids An array containing ids of each token being transferred (order and length must match values array)
                  @param values An array containing amounts of each token being transferred (order and length must match ids array)
                  @param data Additional data with no specified format
                  @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
              */
              function onERC1155BatchReceived(
                  address operator,
                  address from,
                  uint256[] calldata ids,
                  uint256[] calldata values,
                  bytes calldata data
              )
                  external
                  returns(bytes4);
          }
          
          
          /**
           * @dev Required interface of an ERC1155 compliant contract, as defined in the
           * https://eips.ethereum.org/EIPS/eip-1155[EIP].
           *
           * _Available since v3.1._
           */
          interface IERC1155 is IERC165 {
              /**
               * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
               */
              event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
          
              /**
               * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
               * transfers.
               */
              event TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values);
          
              /**
               * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
               * `approved`.
               */
              event ApprovalForAll(address indexed account, address indexed operator, bool approved);
          
              /**
               * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
               *
               * If an {URI} event was emitted for `id`, the standard
               * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
               * returned by {IERC1155MetadataURI-uri}.
               */
              event URI(string value, uint256 indexed id);
          
              /**
               * @dev Returns the amount of tokens of token type `id` owned by `account`.
               *
               * Requirements:
               *
               * - `account` cannot be the zero address.
               */
              function balanceOf(address account, uint256 id) external view returns (uint256);
          
              /**
               * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
               *
               * Requirements:
               *
               * - `accounts` and `ids` must have the same length.
               */
              function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory);
          
              /**
               * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
               *
               * Emits an {ApprovalForAll} event.
               *
               * Requirements:
               *
               * - `operator` cannot be the caller.
               */
              function setApprovalForAll(address operator, bool approved) external;
          
              /**
               * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
               *
               * See {setApprovalForAll}.
               */
              function isApprovedForAll(address account, address operator) external view returns (bool);
          
              /**
               * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
               *
               * Emits a {TransferSingle} event.
               *
               * Requirements:
               *
               * - `to` cannot be the zero address.
               * - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}.
               * - `from` must have a balance of tokens of type `id` of at least `amount`.
               * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
               * acceptance magic value.
               */
              function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;
          
              /**
               * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
               *
               * Emits a {TransferBatch} event.
               *
               * Requirements:
               *
               * - `ids` and `amounts` must have the same length.
               * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
               * acceptance magic value.
               */
              function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external;
          }
          
          
          /**
           *
           * @dev Implementation of the basic standard multi-token.
           * See https://eips.ethereum.org/EIPS/eip-1155
           * Originally based on code by Enjin: https://github.com/enjin/erc-1155
           *
           * _Available since v3.1._
           */
          contract ERC1155 is Context, ERC165, IERC1155 {
              using SafeMath for uint256;
              using Address for address;
          
              // Mapping from token ID to account balances
              mapping (uint256 => mapping(address => uint256)) private _balances;
          
              // Mapping from account to operator approvals
              mapping (address => mapping(address => bool)) private _operatorApprovals;
          
              /*
               *     bytes4(keccak256('balanceOf(address,uint256)')) == 0x00fdd58e
               *     bytes4(keccak256('balanceOfBatch(address[],uint256[])')) == 0x4e1273f4
               *     bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465
               *     bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5
               *     bytes4(keccak256('safeTransferFrom(address,address,uint256,uint256,bytes)')) == 0xf242432a
               *     bytes4(keccak256('safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)')) == 0x2eb2c2d6
               *
               *     => 0x00fdd58e ^ 0x4e1273f4 ^ 0xa22cb465 ^
               *        0xe985e9c5 ^ 0xf242432a ^ 0x2eb2c2d6 == 0xd9b67a26
               */
              bytes4 private constant _INTERFACE_ID_ERC1155 = 0xd9b67a26;
          
              /*
               *     bytes4(keccak256('uri(uint256)')) == 0x0e89341c
               */
              bytes4 private constant _INTERFACE_ID_ERC1155_METADATA_URI = 0x0e89341c;
          
              constructor () public {
                  // register the supported interfaces to conform to ERC1155 via ERC165
                  _registerInterface(_INTERFACE_ID_ERC1155);
          
                  // register the supported interfaces to conform to ERC1155MetadataURI via ERC165
                  _registerInterface(_INTERFACE_ID_ERC1155_METADATA_URI);
              }
          
              /**
               * @dev See {IERC1155-balanceOf}.
               *
               * Requirements:
               *
               * - `account` cannot be the zero address.
               */
              function balanceOf(address account, uint256 id) public view override returns (uint256) {
                  require(account != address(0), "ERC1155: balance query for the zero address");
                  return _balances[id][account];
              }
          
              /**
               * @dev See {IERC1155-balanceOfBatch}.
               *
               * Requirements:
               *
               * - `accounts` and `ids` must have the same length.
               */
              function balanceOfBatch(
                  address[] memory accounts,
                  uint256[] memory ids
              )
                  public
                  view
                  override
                  returns (uint256[] memory)
              {
                  require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch");
          
                  uint256[] memory batchBalances = new uint256[](accounts.length);
          
                  for (uint256 i = 0; i < accounts.length; ++i) {
                      require(accounts[i] != address(0), "ERC1155: batch balance query for the zero address");
                      batchBalances[i] = _balances[ids[i]][accounts[i]];
                  }
          
                  return batchBalances;
              }
          
              /**
               * @dev See {IERC1155-setApprovalForAll}.
               */
              function setApprovalForAll(address operator, bool approved) public virtual override {
                  require(_msgSender() != operator, "ERC1155: setting approval status for self");
          
                  _operatorApprovals[_msgSender()][operator] = approved;
                  emit ApprovalForAll(_msgSender(), operator, approved);
              }
          
              /**
               * @dev See {IERC1155-isApprovedForAll}.
               */
              function isApprovedForAll(address account, address operator) public view override returns (bool) {
                  return _operatorApprovals[account][operator];
              }
          
              /**
               * @dev See {IERC1155-safeTransferFrom}.
               */
              function safeTransferFrom(
                  address from,
                  address to,
                  uint256 id,
                  uint256 amount,
                  bytes memory data
              )
                  public
                  virtual
                  override
              {
                  require(to != address(0), "ERC1155: transfer to the zero address");
                  require(
                      from == _msgSender() || isApprovedForAll(from, _msgSender()),
                      "ERC1155: caller is not owner nor approved"
                  );
          
                  address operator = _msgSender();
          
                  _beforeTokenTransfer(operator, from, to, _asSingletonArray(id), _asSingletonArray(amount), data);
          
                  _balances[id][from] = _balances[id][from].sub(amount, "ERC1155: insufficient balance for transfer");
                  _balances[id][to] = _balances[id][to].add(amount);
          
                  emit TransferSingle(operator, from, to, id, amount);
          
                  _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
              }
          
              /**
               * @dev See {IERC1155-safeBatchTransferFrom}.
               */
              function safeBatchTransferFrom(
                  address from,
                  address to,
                  uint256[] memory ids,
                  uint256[] memory amounts,
                  bytes memory data
              )
                  public
                  virtual
                  override
              {
                  require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
                  require(to != address(0), "ERC1155: transfer to the zero address");
                  require(
                      from == _msgSender() || isApprovedForAll(from, _msgSender()),
                      "ERC1155: transfer caller is not owner nor approved"
                  );
          
                  address operator = _msgSender();
          
                  _beforeTokenTransfer(operator, from, to, ids, amounts, data);
          
                  for (uint256 i = 0; i < ids.length; ++i) {
                      uint256 id = ids[i];
                      uint256 amount = amounts[i];
          
                      _balances[id][from] = _balances[id][from].sub(
                          amount,
                          "ERC1155: insufficient balance for transfer"
                      );
                      _balances[id][to] = _balances[id][to].add(amount);
                  }
          
                  emit TransferBatch(operator, from, to, ids, amounts);
          
                  _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);
              }
          
              /**
               * @dev Creates `amount` tokens of token type `id`, and assigns them to `account`.
               *
               * Emits a {TransferSingle} event.
               *
               * Requirements:
               *
               * - `account` cannot be the zero address.
               * - If `account` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
               * acceptance magic value.
               */
              function _mint(address account, uint256 id, uint256 amount, bytes memory data) internal virtual {
                  require(account != address(0), "ERC1155: mint to the zero address");
          
                  address operator = _msgSender();
          
                  _beforeTokenTransfer(operator, address(0), account, _asSingletonArray(id), _asSingletonArray(amount), data);
          
                  _balances[id][account] = _balances[id][account].add(amount);
                  emit TransferSingle(operator, address(0), account, id, amount);
          
                  _doSafeTransferAcceptanceCheck(operator, address(0), account, id, amount, data);
              }
          
              /**
               * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
               *
               * Requirements:
               *
               * - `ids` and `amounts` must have the same length.
               * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
               * acceptance magic value.
               */
              function _mintBatch(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data) internal virtual {
                  require(to != address(0), "ERC1155: mint to the zero address");
                  require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
          
                  address operator = _msgSender();
          
                  _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);
          
                  for (uint i = 0; i < ids.length; i++) {
                      _balances[ids[i]][to] = amounts[i].add(_balances[ids[i]][to]);
                  }
          
                  emit TransferBatch(operator, address(0), to, ids, amounts);
          
                  _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
              }
          
              /**
               * @dev Destroys `amount` tokens of token type `id` from `account`
               *
               * Requirements:
               *
               * - `account` cannot be the zero address.
               * - `account` must have at least `amount` tokens of token type `id`.
               */
              function _burn(address account, uint256 id, uint256 amount) internal virtual {
                  require(account != address(0), "ERC1155: burn from the zero address");
          
                  address operator = _msgSender();
          
                  _beforeTokenTransfer(operator, account, address(0), _asSingletonArray(id), _asSingletonArray(amount), "");
          
                  _balances[id][account] = _balances[id][account].sub(
                      amount,
                      "ERC1155: burn amount exceeds balance"
                  );
          
                  emit TransferSingle(operator, account, address(0), id, amount);
              }
          
              /**
               * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
               *
               * Requirements:
               *
               * - `ids` and `amounts` must have the same length.
               */
              function _burnBatch(address account, uint256[] memory ids, uint256[] memory amounts) internal virtual {
                  require(account != address(0), "ERC1155: burn from the zero address");
                  require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
          
                  address operator = _msgSender();
          
                  _beforeTokenTransfer(operator, account, address(0), ids, amounts, "");
          
                  for (uint i = 0; i < ids.length; i++) {
                      _balances[ids[i]][account] = _balances[ids[i]][account].sub(
                          amounts[i],
                          "ERC1155: burn amount exceeds balance"
                      );
                  }
          
                  emit TransferBatch(operator, account, address(0), ids, amounts);
              }
          
              /**
               * @dev Hook that is called before any token transfer. This includes minting
               * and burning, as well as batched variants.
               *
               * The same hook is called on both single and batched variants. For single
               * transfers, the length of the `id` and `amount` arrays will be 1.
               *
               * Calling conditions (for each `id` and `amount` pair):
               *
               * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
               * of token type `id` will be  transferred to `to`.
               * - When `from` is zero, `amount` tokens of token type `id` will be minted
               * for `to`.
               * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
               * will be burned.
               * - `from` and `to` are never both zero.
               * - `ids` and `amounts` have the same, non-zero length.
               *
               * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
               */
              function _beforeTokenTransfer(
                  address operator,
                  address from,
                  address to,
                  uint256[] memory ids,
                  uint256[] memory amounts,
                  bytes memory data
              )
                  internal virtual
              { }
          
              function _doSafeTransferAcceptanceCheck(
                  address operator,
                  address from,
                  address to,
                  uint256 id,
                  uint256 amount,
                  bytes memory data
              )
                  private
              {
                  if (to.isContract()) {
                      try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) {
                          if (response != IERC1155Receiver(to).onERC1155Received.selector) {
                              revert("ERC1155: ERC1155Receiver rejected tokens");
                          }
                      } catch Error(string memory reason) {
                          revert(reason);
                      } catch {
                          revert("ERC1155: transfer to non ERC1155Receiver implementer");
                      }
                  }
              }
          
              function _doSafeBatchTransferAcceptanceCheck(
                  address operator,
                  address from,
                  address to,
                  uint256[] memory ids,
                  uint256[] memory amounts,
                  bytes memory data
              )
                  private
              {
                  if (to.isContract()) {
                      try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (bytes4 response) {
                          if (response != IERC1155Receiver(to).onERC1155BatchReceived.selector) {
                              revert("ERC1155: ERC1155Receiver rejected tokens");
                          }
                      } catch Error(string memory reason) {
                          revert(reason);
                      } catch {
                          revert("ERC1155: transfer to non ERC1155Receiver implementer");
                      }
                  }
              }
          
              function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) {
                  uint256[] memory array = new uint256[](1);
                  array[0] = element;
          
                  return array;
              }
          }
          
          library UintLibrary {
              function toString(uint256 _i) internal pure returns (string memory) {
                  if (_i == 0) {
                      return "0";
                  }
                  uint j = _i;
                  uint len;
                  while (j != 0) {
                      len++;
                      j /= 10;
                  }
                  bytes memory bstr = new bytes(len);
                  uint k = len - 1;
                  while (_i != 0) {
                      bstr[k--] = byte(uint8(48 + _i % 10));
                      _i /= 10;
                  }
                  return string(bstr);
              }
          }
          
          library StringLibrary {
              using UintLibrary for uint256;
          
              function append(string memory _a, string memory _b) internal pure returns (string memory) {
                  bytes memory _ba = bytes(_a);
                  bytes memory _bb = bytes(_b);
                  bytes memory bab = new bytes(_ba.length + _bb.length);
                  uint k = 0;
                  for (uint i = 0; i < _ba.length; i++) bab[k++] = _ba[i];
                  for (uint i = 0; i < _bb.length; i++) bab[k++] = _bb[i];
                  return string(bab);
              }
          
              function append(string memory _a, string memory _b, string memory _c) internal pure returns (string memory) {
                  bytes memory _ba = bytes(_a);
                  bytes memory _bb = bytes(_b);
                  bytes memory _bc = bytes(_c);
                  bytes memory bbb = new bytes(_ba.length + _bb.length + _bc.length);
                  uint k = 0;
                  for (uint i = 0; i < _ba.length; i++) bbb[k++] = _ba[i];
                  for (uint i = 0; i < _bb.length; i++) bbb[k++] = _bb[i];
                  for (uint i = 0; i < _bc.length; i++) bbb[k++] = _bc[i];
                  return string(bbb);
              }
          
              function recover(string memory message, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
                  bytes memory msgBytes = bytes(message);
                  bytes memory fullMessage = concat(
                      bytes("\x19Ethereum Signed Message:\n"),
                      bytes(msgBytes.length.toString()),
                      msgBytes,
                      new bytes(0), new bytes(0), new bytes(0), new bytes(0)
                  );
                  return ecrecover(keccak256(fullMessage), v, r, s);
              }
          
              function concat(bytes memory _ba, bytes memory _bb, bytes memory _bc, bytes memory _bd, bytes memory _be, bytes memory _bf, bytes memory _bg) internal pure returns (bytes memory) {
                  bytes memory resultBytes = new bytes(_ba.length + _bb.length + _bc.length + _bd.length + _be.length + _bf.length + _bg.length);
                  uint k = 0;
                  for (uint i = 0; i < _ba.length; i++) resultBytes[k++] = _ba[i];
                  for (uint i = 0; i < _bb.length; i++) resultBytes[k++] = _bb[i];
                  for (uint i = 0; i < _bc.length; i++) resultBytes[k++] = _bc[i];
                  for (uint i = 0; i < _bd.length; i++) resultBytes[k++] = _bd[i];
                  for (uint i = 0; i < _be.length; i++) resultBytes[k++] = _be[i];
                  for (uint i = 0; i < _bf.length; i++) resultBytes[k++] = _bf[i];
                  for (uint i = 0; i < _bg.length; i++) resultBytes[k++] = _bg[i];
                  return resultBytes;
              }
          }
          
          contract TokenURI {
              using StringLibrary for string;
          
              string public tokenBaseURI;
              mapping (uint256 => string) private _tokenURIs;
          
              constructor(string memory uri) {
                  tokenBaseURI = uri;
              }
          
              function _setBaseURI(string memory uri) internal {
                  tokenBaseURI = uri;
              }
          
              function _tokenURI(uint256 tokenId) internal view returns (string memory) {
                  return tokenBaseURI.append(_tokenURIs[tokenId]);
              }
          
              function _setTokenURI(uint256 tokenId, string memory uri) internal {
                  bytes memory tokenUri = bytes(_tokenURIs[tokenId]);
                  require(tokenUri.length == 0, "Uri has existed.");
                  _tokenURIs[tokenId] = uri;
              }
          
              function _clearTokenURI(uint256 tokenId) internal {
                  if (bytes(_tokenURIs[tokenId]).length != 0) {
                      delete _tokenURIs[tokenId];
                  }
              }
          }
          
          /**
           * @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 () internal {
                  address msgSender = _msgSender();
                  _owner = msgSender;
                  emit OwnershipTransferred(address(0), msgSender);
              }
          
              /**
               * @dev Returns the address of the current owner.
               */
              function owner() public view returns (address) {
                  return _owner;
              }
          
              /**
               * @dev Throws if called by any account other than the owner.
               */
              modifier onlyOwner() {
                  require(_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 Extension of {ERC1155} that allows token holders to destroy both their
           * own tokens and those that they have been approved to use.
           *
           * _Available since v3.1._
           */
          abstract contract ERC1155Burnable is ERC1155 {
              function burn(address account, uint256 id, uint256 value) public virtual {
                  require(
                      account == _msgSender() || isApprovedForAll(account, _msgSender()),
                      "ERC1155: caller is not owner nor approved"
                  );
          
                  _burn(account, id, value);
              }
          
              function burnBatch(address account, uint256[] memory ids, uint256[] memory values) public virtual {
                  require(
                      account == _msgSender() || isApprovedForAll(account, _msgSender()),
                      "ERC1155: caller is not owner nor approved"
                  );
          
                  _burnBatch(account, ids, values);
              }
          }
          
          contract MomijiToken is ERC1155, TokenURI, ERC1155Burnable, Ownable {
              using SafeMath for uint256;
              using Address for address;
          
              string public name;
              string public symbol;
          
              // Count of duplication of every token.
              mapping(uint256 => uint256) public tokenQuantityWithId;
              mapping(uint256 => uint256) public tokenMaxQuantityWithId;
              mapping(uint256 => uint256) public mintManuallyQuantityWithId;
          
              mapping(uint256 => address) public creators;
              mapping(uint256 => mapping(address => bool)) public minters;
              uint256 [] public tokenIds;
              uint256 public tokenIdAmount;
          
              bool public onlyWhitelist = true;
              mapping(address => bool) public whitelist;
          
              event Create(uint256 maxSupply, uint256 tokenId, string indexed uri, bytes indexed data);
              event Mint(uint256 tokenId, address to, uint256 quantity, bytes indexed data, uint256 tokenMaxQuantity, uint256 tokenCurrentQuantity);
          
              constructor(string memory tokenName, string memory tokenSymbol, string memory baseUri) TokenURI(baseUri) {
                  name = tokenName;
                  symbol = tokenSymbol;
              }
          
              function setBaseURI(string memory baseUri) public onlyOwner {
                  _setBaseURI(baseUri);
              }
          
              /**
                @dev create a new NFT token.
                @param maxSupply max supply allow
               */
              function create(uint256 tokenId, uint256 maxSupply, string memory uri, bytes calldata data) external {
                  if (onlyWhitelist) {
                      require(whitelist[msg.sender], "Open to only whitelist.");
                  }
                  tokenQuantityWithId[tokenId] = 0;
                  tokenMaxQuantityWithId[tokenId] = maxSupply;
                  tokenIds.push(tokenId);
                  tokenIdAmount = tokenIdAmount.add(1);
                  _setTokenURI(tokenId, uri);
                  creators[tokenId] = msg.sender;
                  mintManuallyQuantityWithId[tokenId] = maxSupply;
                  emit Create(maxSupply, tokenId, uri, data);
              }
          
              function mint(uint256 tokenId, address to, uint256 quantity, bytes memory data) public {
                  require(creators[tokenId] == msg.sender || minters[tokenId][msg.sender], "You are not the creator or minter of this NFT.");
                  require(_isTokenIdExist(tokenId), "Token is is not exist.");
                  require(tokenMaxQuantityWithId[tokenId] >= tokenQuantityWithId[tokenId] + quantity, "NFT quantity is greater than max supply.");
                  if (!address(msg.sender).isContract()) {
                      require(mintManuallyQuantityWithId[tokenId] >= quantity, "You mint too many cards manually" );
                      mintManuallyQuantityWithId[tokenId] = mintManuallyQuantityWithId[tokenId].sub(quantity);
                  }
                  _mint(to, tokenId, quantity, data);
                  tokenQuantityWithId[tokenId] = tokenQuantityWithId[tokenId].add(quantity);
                  emit Mint(tokenId, to, quantity, data, tokenMaxQuantityWithId[tokenId], tokenQuantityWithId[tokenId]);
              }
          
              function uri(uint256 id) public view returns(string memory) {
                  return _tokenURI(id);
              }
          
              function _isTokenIdExist(uint256 tokenId) private view returns(bool) {
                  return creators[tokenId] != address(0);
              }
          
              function addMintManuallyQuantity(uint256 tokenId, uint256 amount) public {
                  require(creators[tokenId] == msg.sender || minters[tokenId][msg.sender], "You are not the creator or minter of this NFT.");
                  mintManuallyQuantityWithId[tokenId] = mintManuallyQuantityWithId[tokenId].add(amount);
              }
          
              function removeMintManuallyQuantity(uint256 tokenId, uint256 amount) public {
                  require(creators[tokenId] == msg.sender || minters[tokenId][msg.sender], "You are not the creator or minter of this NFT.");
                  mintManuallyQuantityWithId[tokenId] = mintManuallyQuantityWithId[tokenId].sub(amount);
              }
          
              function addToWhitelist(address account) public onlyOwner {
                  whitelist[account] = true;
              }
          
              function removeFromWhitelist(address account) public onlyOwner {
                  whitelist[account] = false;
              }
          
              function openToEveryone() public onlyOwner {
                  onlyWhitelist = false;
              }
          
              function openOnlyToWhitelist() public onlyOwner {
                  onlyWhitelist = true;
              }
          
              function addMinter(uint256 id, address account) public onlyCreator(id) {
                  minters[id][account] = true;
              }
          
              function removeMinter(uint256 id, address account) public onlyCreator(id) {
                  minters[id][account] = false;
              }
          
              function transferCreator(uint256 id, address account) public onlyCreator(id) {
                  creators[id] = account;
              }
          
              modifier onlyCreator(uint256 id) {
                  require(msg.sender == creators[id], "only for creator of this NFT.");
                  _;
              }
          }

          File 3 of 6: RootChainManager
          // File: @openzeppelin/contracts/math/SafeMath.sol
          
          // 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;
              }
          }
          
          // File: contracts/root/RootChainManager/IRootChainManager.sol
          
          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;
          }
          
          // File: contracts/root/StateSender/IStateSender.sol
          
          pragma solidity 0.6.6;
          
          interface IStateSender {
              function syncState(address receiver, bytes calldata data) external;
          }
          
          // File: contracts/root/ICheckpointManager.sol
          
          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;
          }
          
          // File: contracts/root/RootChainManager/RootChainManagerStorage.sol
          
          pragma solidity 0.6.6;
          
          
          
          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;
          }
          
          // File: contracts/lib/RLPReader.sol
          
          /*
           * @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 {
                  uint256 len;
                  uint256 memPtr;
              }
          
              /*
               * @param item RLP encoded bytes
               */
              function toRlpItem(bytes memory item)
                  internal
                  pure
                  returns (RLPItem memory)
              {
                  require(item.length > 0, "RLPReader: INVALID_BYTES_LENGTH");
                  uint256 memPtr;
                  assembly {
                      memPtr := add(item, 0x20)
                  }
          
                  return RLPItem(item.length, memPtr);
              }
          
              /*
               * @param item RLP encoded list in bytes
               */
              function toList(RLPItem memory item)
                  internal
                  pure
                  returns (RLPItem[] memory)
              {
                  require(isList(item), "RLPReader: ITEM_NOT_LIST");
          
                  uint256 items = numItems(item);
                  RLPItem[] memory result = new RLPItem[](items);
                  uint256 listLength = _itemLength(item.memPtr);
                  require(listLength == item.len, "RLPReader: LIST_DECODED_LENGTH_MISMATCH");
          
                  uint256 memPtr = item.memPtr + _payloadOffset(item.memPtr);
                  uint256 dataLen;
                  for (uint256 i = 0; i < items; i++) {
                      dataLen = _itemLength(memPtr);
                      result[i] = RLPItem(dataLen, memPtr);
                      memPtr = memPtr + dataLen;
                  }
          
                  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) {
                  uint8 byte0;
                  uint256 memPtr = item.memPtr;
                  assembly {
                      byte0 := byte(0, mload(memPtr))
                  }
          
                  if (byte0 < LIST_SHORT_START) return false;
                  return true;
              }
          
              /** 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);
          
                  uint256 ptr;
                  assembly {
                      ptr := add(0x20, result)
                  }
          
                  copy(item.memPtr, ptr, item.len);
                  return result;
              }
          
              function toAddress(RLPItem memory item) internal pure returns (address) {
                  require(!isList(item), "RLPReader: DECODING_LIST_AS_ADDRESS");
                  // 1 byte for the length prefix
                  require(item.len == 21, "RLPReader: INVALID_ADDRESS_LENGTH");
          
                  return address(toUint(item));
              }
          
              function toUint(RLPItem memory item) internal pure returns (uint256) {
                  require(!isList(item), "RLPReader: DECODING_LIST_AS_UINT");
                  require(item.len <= 33, "RLPReader: INVALID_UINT_LENGTH");
          
                  uint256 itemLength = _itemLength(item.memPtr);
                  require(itemLength == item.len, "RLPReader: UINT_DECODED_LENGTH_MISMATCH");
          
                  uint256 offset = _payloadOffset(item.memPtr);
                  uint256 len = item.len - offset;
                  uint256 result;
                  uint256 memPtr = item.memPtr + offset;
                  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 (uint256) {
                  uint256 itemLength = _itemLength(item.memPtr);
                  require(itemLength == item.len, "RLPReader: UINT_STRICT_DECODED_LENGTH_MISMATCH");
                  // one byte prefix
                  require(item.len == 33, "RLPReader: INVALID_UINT_STRICT_LENGTH");
          
                  uint256 result;
                  uint256 memPtr = item.memPtr + 1;
                  assembly {
                      result := mload(memPtr)
                  }
          
                  return result;
              }
          
              function toBytes(RLPItem memory item) internal pure returns (bytes memory) {
                  uint256 listLength = _itemLength(item.memPtr);
                  require(listLength == item.len, "RLPReader: BYTES_DECODED_LENGTH_MISMATCH");
                  uint256 offset = _payloadOffset(item.memPtr);
          
                  uint256 len = item.len - offset; // data length
                  bytes memory result = new bytes(len);
          
                  uint256 destPtr;
                  assembly {
                      destPtr := add(0x20, result)
                  }
          
                  copy(item.memPtr + offset, destPtr, len);
                  return result;
              }
          
              /*
               * Private Helpers
               */
          
              // @return number of payload items inside an encoded list.
              function numItems(RLPItem memory item) private pure returns (uint256) {
                  // add `isList` check if `item` is expected to be passsed without a check from calling function
                  // require(isList(item), "RLPReader: NUM_ITEMS_NOT_LIST");
          
                  uint256 count = 0;
                  uint256 currPtr = item.memPtr + _payloadOffset(item.memPtr);
                  uint256 endPtr = item.memPtr + item.len;
                  while (currPtr < endPtr) {
                      currPtr = currPtr + _itemLength(currPtr); // skip over an item
                      require(currPtr <= endPtr, "RLPReader: NUM_ITEMS_DECODED_LENGTH_MISMATCH");
                      count++;
                  }
          
                  return count;
              }
          
              // @return entire rlp item byte length
              function _itemLength(uint256 memPtr) private pure returns (uint256) {
                  uint256 itemLen;
                  uint256 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(uint256 memPtr) private pure returns (uint256) {
                  uint256 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(
                  uint256 src,
                  uint256 dest,
                  uint256 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
                  uint256 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))
                  }
              }
          }
          
          // File: contracts/lib/MerklePatriciaProof.sol
          
          /*
           * @title MerklePatriciaVerifier
           * @author Sam Mayo ([email protected])
           *
           * @dev Library for verifing merkle patricia proofs.
           */
          pragma solidity 0.6.6;
          
          
          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) {
                          uint256 traversed = _nibblesToTraverse(
                              RLPReader.toBytes(currentNodeList[0]),
                              path,
                              pathPtr
                          );
                          if (pathPtr + traversed == path.length) {
                              //leaf node
                              if (
                                  keccak256(RLPReader.toBytes(currentNodeList[1])) ==
                                  keccak256(value)
                              ) {
                                  return true;
                              } else {
                                  return false;
                              }
                          }
          
                          //extension node
                          if (traversed == 0) {
                              return false;
                          }
          
                          pathPtr += traversed;
                          nodeKey = bytes32(RLPReader.toUintStrict(currentNodeList[1]));
                      } else {
                          return false;
                      }
                  }
              }
          
              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
                      );
              }
          }
          
          // File: contracts/lib/Merkle.sol
          
          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;
              }
          }
          
          // File: contracts/root/TokenPredicates/ITokenPredicate.sol
          
          pragma solidity 0.6.6;
          
          
          /// @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 Address
               * @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;
          }
          
          // File: contracts/common/Initializable.sol
          
          pragma solidity 0.6.6;
          
          contract Initializable {
              bool inited = false;
          
              modifier initializer() {
                  require(!inited, "already inited");
                  _;
                  inited = true;
              }
          }
          
          // File: contracts/common/EIP712Base.sol
          
          pragma solidity 0.6.6;
          
          
          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)
                      );
              }
          }
          
          // File: contracts/common/NativeMetaTransaction.sol
          
          pragma solidity 0.6.6;
          
          
          
          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 userAddress,
                  address payable 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 memory functionSignature,
                  bytes32 sigR,
                  bytes32 sigS,
                  uint8 sigV
              ) public 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] = nonces[userAddress].add(1);
          
                  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 hashMetaTransaction(MetaTransaction memory metaTx)
                  internal
                  pure
                  returns (bytes32)
              {
                  return
                      keccak256(
                          abi.encode(
                              META_TRANSACTION_TYPEHASH,
                              metaTx.nonce,
                              metaTx.from,
                              keccak256(metaTx.functionSignature)
                          )
                      );
              }
          
              function getNonce(address user) public view returns (uint256 nonce) {
                  nonce = nonces[user];
              }
          
              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
                      );
              }
          }
          
          // File: @openzeppelin/contracts/utils/EnumerableSet.sol
          
          // 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));
              }
          }
          
          // File: @openzeppelin/contracts/utils/Address.sol
          
          // 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);
                      }
                  }
              }
          }
          
          // File: @openzeppelin/contracts/GSN/Context.sol
          
          // 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: @openzeppelin/contracts/access/AccessControl.sol
          
          // SPDX-License-Identifier: MIT
          
          pragma solidity ^0.6.0;
          
          
          
          
          /**
           * @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());
                  }
              }
          }
          
          // File: contracts/common/AccessControlMixin.sol
          
          pragma solidity 0.6.6;
          
          
          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
                  );
                  _;
              }
          }
          
          // File: contracts/common/ContextMixin.sol
          
          pragma solidity 0.6.6;
          
          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;
              }
          }
          
          // File: contracts/root/RootChainManager/RootChainManager.sol
          
          pragma solidity 0.6.6;
          
          
          
          
          
          
          
          
          
          
          
          
          
          
          
          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 RLPReader for bytes;
              using RLPReader for RLPReader.RLPItem;
              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");
          
              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)
              {
                  _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)
              {
                  _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 mappers
               * @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(MAPPER_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(MAPPER_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 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 remapToken(
                  address rootToken,
                  address childToken,
                  bytes32 tokenType
              ) external override only(MAPPER_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 {
                  RLPReader.RLPItem[] memory inputDataRLPList = inputData
                      .toRlpItem()
                      .toList();
          
                  // checking if exit has already been processed
                  // unique exit is identified using hash of (blockNumber, branchMask, receiptLogIndex)
                  bytes32 exitHash = keccak256(
                      abi.encodePacked(
                          inputDataRLPList[2].toUint(), // blockNumber
                          // 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(inputDataRLPList[8].toBytes()), // branchMask
                          inputDataRLPList[9].toUint() // receiptLogIndex
                      )
                  );
                  require(
                      processedExits[exitHash] == false,
                      "RootChainManager: EXIT_ALREADY_PROCESSED"
                  );
                  processedExits[exitHash] = true;
          
                  RLPReader.RLPItem[] memory receiptRLPList = inputDataRLPList[6]
                      .toBytes()
                      .toRlpItem()
                      .toList();
                  RLPReader.RLPItem memory logRLP = receiptRLPList[3]
                      .toList()[
                          inputDataRLPList[9].toUint() // receiptLogIndex
                      ];
          
                  address childToken = RLPReader.toAddress(logRLP.toList()[0]); // log emitter address field
                  // log should be emmited only by the child token
                  address rootToken = childToRootToken[childToken];
                  require(
                      rootToken != address(0),
                      "RootChainManager: TOKEN_NOT_MAPPED"
                  );
          
                  address predicateAddress = typeToPredicate[
                      tokenToType[rootToken]
                  ];
          
                  // branch mask can be maximum 32 bits
                  require(
                      inputDataRLPList[8].toUint() &
                          0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000 ==
                          0,
                      "RootChainManager: INVALID_BRANCH_MASK"
                  );
          
                  // verify receipt inclusion
                  require(
                      MerklePatriciaProof.verify(
                          inputDataRLPList[6].toBytes(), // receipt
                          inputDataRLPList[8].toBytes(), // branchMask
                          inputDataRLPList[7].toBytes(), // receiptProof
                          bytes32(inputDataRLPList[5].toUint()) // receiptRoot
                      ),
                      "RootChainManager: INVALID_PROOF"
                  );
          
                  // verify checkpoint inclusion
                  _checkBlockMembershipInCheckpoint(
                      inputDataRLPList[2].toUint(), // blockNumber
                      inputDataRLPList[3].toUint(), // blockTime
                      bytes32(inputDataRLPList[4].toUint()), // txRoot
                      bytes32(inputDataRLPList[5].toUint()), // receiptRoot
                      inputDataRLPList[0].toUint(), // headerNumber
                      inputDataRLPList[1].toBytes() // blockProof
                  );
          
                  ITokenPredicate(predicateAddress).exitTokens(
                      _msgSender(),
                      rootToken,
                      logRLP.toRlpBytes()
                  );
              }
          
              function _checkBlockMembershipInCheckpoint(
                  uint256 blockNumber,
                  uint256 blockTime,
                  bytes32 txRoot,
                  bytes32 receiptRoot,
                  uint256 headerNumber,
                  bytes memory blockProof
              ) private view returns (uint256) {
                  (
                      bytes32 headerRoot,
                      uint256 startBlock,
                      ,
                      uint256 createdAt,
          
                  ) = _checkpointManager.headerBlocks(headerNumber);
          
                  require(
                      keccak256(
                          abi.encodePacked(blockNumber, blockTime, txRoot, receiptRoot)
                      )
                          .checkMembership(
                          blockNumber.sub(startBlock),
                          headerRoot,
                          blockProof
                      ),
                      "RootChainManager: INVALID_HEADER"
                  );
                  return createdAt;
              }
          }

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

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

          File 6 of 6: ERC1155Predicate
          // File: @openzeppelin/contracts/introspection/IERC165.sol
          
          // SPDX-License-Identifier: MIT
          
          pragma solidity ^0.6.0;
          
          /**
           * @dev Interface of the ERC165 standard, as defined in the
           * https://eips.ethereum.org/EIPS/eip-165[EIP].
           *
           * Implementers can declare support of contract interfaces, which can then be
           * queried by others ({ERC165Checker}).
           *
           * For an implementation, see {ERC165}.
           */
          interface IERC165 {
              /**
               * @dev Returns true if this contract implements the interface defined by
               * `interfaceId`. See the corresponding
               * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
               * to learn more about how these ids are created.
               *
               * This function call must use less than 30 000 gas.
               */
              function supportsInterface(bytes4 interfaceId) external view returns (bool);
          }
          
          // File: @openzeppelin/contracts/token/ERC1155/IERC1155.sol
          
          // SPDX-License-Identifier: MIT
          
          pragma solidity ^0.6.2;
          
          
          /**
           * @dev Required interface of an ERC1155 compliant contract, as defined in the
           * https://eips.ethereum.org/EIPS/eip-1155[EIP].
           *
           * _Available since v3.1._
           */
          interface IERC1155 is IERC165 {
              /**
               * @dev Emitted when `value` tokens of token type `id` are transfered from `from` to `to` by `operator`.
               */
              event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
          
              /**
               * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
               * transfers.
               */
              event TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values);
          
              /**
               * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
               * `approved`.
               */
              event ApprovalForAll(address indexed account, address indexed operator, bool approved);
          
              /**
               * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
               *
               * If an {URI} event was emitted for `id`, the standard
               * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
               * returned by {IERC1155MetadataURI-uri}.
               */
              event URI(string value, uint256 indexed id);
          
              /**
               * @dev Returns the amount of tokens of token type `id` owned by `account`.
               *
               * Requirements:
               *
               * - `account` cannot be the zero address.
               */
              function balanceOf(address account, uint256 id) external view returns (uint256);
          
              /**
               * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
               *
               * Requirements:
               *
               * - `accounts` and `ids` must have the same length.
               */
              function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory);
          
              /**
               * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
               *
               * Emits an {ApprovalForAll} event.
               *
               * Requirements:
               *
               * - `operator` cannot be the caller.
               */
              function setApprovalForAll(address operator, bool approved) external;
          
              /**
               * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
               *
               * See {setApprovalForAll}.
               */
              function isApprovedForAll(address account, address operator) external view returns (bool);
          
              /**
               * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
               *
               * Emits a {TransferSingle} event.
               *
               * Requirements:
               *
               * - `to` cannot be the zero address.
               * - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}.
               * - `from` must have a balance of tokens of type `id` of at least `amount`.
               * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
               * acceptance magic value.
               */
              function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;
          
              /**
               * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
               *
               * Emits a {TransferBatch} event.
               *
               * Requirements:
               *
               * - `ids` and `amounts` must have the same length.
               * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
               * acceptance magic value.
               */
              function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external;
          }
          
          // File: @openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol
          
          // SPDX-License-Identifier: MIT
          
          pragma solidity ^0.6.0;
          
          
          /**
           * _Available since v3.1._
           */
          interface IERC1155Receiver is IERC165 {
          
              /**
                  @dev Handles the receipt of a single ERC1155 token type. This function is
                  called at the end of a `safeTransferFrom` after the balance has been updated.
                  To accept the transfer, this must return
                  `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
                  (i.e. 0xf23a6e61, or its own function selector).
                  @param operator The address which initiated the transfer (i.e. msg.sender)
                  @param from The address which previously owned the token
                  @param id The ID of the token being transferred
                  @param value The amount of tokens being transferred
                  @param data Additional data with no specified format
                  @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
              */
              function onERC1155Received(
                  address operator,
                  address from,
                  uint256 id,
                  uint256 value,
                  bytes calldata data
              )
                  external
                  returns(bytes4);
          
              /**
                  @dev Handles the receipt of a multiple ERC1155 token types. This function
                  is called at the end of a `safeBatchTransferFrom` after the balances have
                  been updated. To accept the transfer(s), this must return
                  `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
                  (i.e. 0xbc197c81, or its own function selector).
                  @param operator The address which initiated the batch transfer (i.e. msg.sender)
                  @param from The address which previously owned the token
                  @param ids An array containing ids of each token being transferred (order and length must match values array)
                  @param values An array containing amounts of each token being transferred (order and length must match ids array)
                  @param data Additional data with no specified format
                  @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
              */
              function onERC1155BatchReceived(
                  address operator,
                  address from,
                  uint256[] calldata ids,
                  uint256[] calldata values,
                  bytes calldata data
              )
                  external
                  returns(bytes4);
          }
          
          // File: @openzeppelin/contracts/introspection/ERC165.sol
          
          // SPDX-License-Identifier: MIT
          
          pragma solidity ^0.6.0;
          
          
          /**
           * @dev Implementation of the {IERC165} interface.
           *
           * Contracts may inherit from this and call {_registerInterface} to declare
           * their support of an interface.
           */
          contract ERC165 is IERC165 {
              /*
               * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
               */
              bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
          
              /**
               * @dev Mapping of interface ids to whether or not it's supported.
               */
              mapping(bytes4 => bool) private _supportedInterfaces;
          
              constructor () internal {
                  // Derived contracts need only register support for their own interfaces,
                  // we register support for ERC165 itself here
                  _registerInterface(_INTERFACE_ID_ERC165);
              }
          
              /**
               * @dev See {IERC165-supportsInterface}.
               *
               * Time complexity O(1), guaranteed to always use less than 30 000 gas.
               */
              function supportsInterface(bytes4 interfaceId) public view override returns (bool) {
                  return _supportedInterfaces[interfaceId];
              }
          
              /**
               * @dev Registers the contract as an implementer of the interface defined by
               * `interfaceId`. Support of the actual ERC165 interface is automatic and
               * registering its interface id is not required.
               *
               * See {IERC165-supportsInterface}.
               *
               * Requirements:
               *
               * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
               */
              function _registerInterface(bytes4 interfaceId) internal virtual {
                  require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
                  _supportedInterfaces[interfaceId] = true;
              }
          }
          
          // File: @openzeppelin/contracts/token/ERC1155/ERC1155Receiver.sol
          
          // SPDX-License-Identifier: MIT
          
          pragma solidity ^0.6.0;
          
          
          
          /**
           * @dev _Available since v3.1._
           */
          abstract contract ERC1155Receiver is ERC165, IERC1155Receiver {
              constructor() public {
                  _registerInterface(
                      ERC1155Receiver(0).onERC1155Received.selector ^
                      ERC1155Receiver(0).onERC1155BatchReceived.selector
                  );
              }
          }
          
          // File: @openzeppelin/contracts/utils/EnumerableSet.sol
          
          // 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));
              }
          }
          
          // File: @openzeppelin/contracts/utils/Address.sol
          
          // 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);
                      }
                  }
              }
          }
          
          // File: @openzeppelin/contracts/GSN/Context.sol
          
          // 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: @openzeppelin/contracts/access/AccessControl.sol
          
          // SPDX-License-Identifier: MIT
          
          pragma solidity ^0.6.0;
          
          
          
          
          /**
           * @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());
                  }
              }
          }
          
          // File: contracts/common/AccessControlMixin.sol
          
          pragma solidity 0.6.6;
          
          
          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
                  );
                  _;
              }
          }
          
          // File: contracts/lib/RLPReader.sol
          
          /*
           * @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 {
                  uint256 len;
                  uint256 memPtr;
              }
          
              /*
               * @param item RLP encoded bytes
               */
              function toRlpItem(bytes memory item)
                  internal
                  pure
                  returns (RLPItem memory)
              {
                  require(item.length > 0, "RLPReader: INVALID_BYTES_LENGTH");
                  uint256 memPtr;
                  assembly {
                      memPtr := add(item, 0x20)
                  }
          
                  return RLPItem(item.length, memPtr);
              }
          
              /*
               * @param item RLP encoded list in bytes
               */
              function toList(RLPItem memory item)
                  internal
                  pure
                  returns (RLPItem[] memory)
              {
                  require(isList(item), "RLPReader: ITEM_NOT_LIST");
          
                  uint256 items = numItems(item);
                  RLPItem[] memory result = new RLPItem[](items);
                  uint256 listLength = _itemLength(item.memPtr);
                  require(listLength == item.len, "RLPReader: LIST_DECODED_LENGTH_MISMATCH");
          
                  uint256 memPtr = item.memPtr + _payloadOffset(item.memPtr);
                  uint256 dataLen;
                  for (uint256 i = 0; i < items; i++) {
                      dataLen = _itemLength(memPtr);
                      result[i] = RLPItem(dataLen, memPtr);
                      memPtr = memPtr + dataLen;
                  }
          
                  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) {
                  uint8 byte0;
                  uint256 memPtr = item.memPtr;
                  assembly {
                      byte0 := byte(0, mload(memPtr))
                  }
          
                  if (byte0 < LIST_SHORT_START) return false;
                  return true;
              }
          
              /** 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);
          
                  uint256 ptr;
                  assembly {
                      ptr := add(0x20, result)
                  }
          
                  copy(item.memPtr, ptr, item.len);
                  return result;
              }
          
              function toAddress(RLPItem memory item) internal pure returns (address) {
                  require(!isList(item), "RLPReader: DECODING_LIST_AS_ADDRESS");
                  // 1 byte for the length prefix
                  require(item.len == 21, "RLPReader: INVALID_ADDRESS_LENGTH");
          
                  return address(toUint(item));
              }
          
              function toUint(RLPItem memory item) internal pure returns (uint256) {
                  require(!isList(item), "RLPReader: DECODING_LIST_AS_UINT");
                  require(item.len <= 33, "RLPReader: INVALID_UINT_LENGTH");
          
                  uint256 itemLength = _itemLength(item.memPtr);
                  require(itemLength == item.len, "RLPReader: UINT_DECODED_LENGTH_MISMATCH");
          
                  uint256 offset = _payloadOffset(item.memPtr);
                  uint256 len = item.len - offset;
                  uint256 result;
                  uint256 memPtr = item.memPtr + offset;
                  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 (uint256) {
                  uint256 itemLength = _itemLength(item.memPtr);
                  require(itemLength == item.len, "RLPReader: UINT_STRICT_DECODED_LENGTH_MISMATCH");
                  // one byte prefix
                  require(item.len == 33, "RLPReader: INVALID_UINT_STRICT_LENGTH");
          
                  uint256 result;
                  uint256 memPtr = item.memPtr + 1;
                  assembly {
                      result := mload(memPtr)
                  }
          
                  return result;
              }
          
              function toBytes(RLPItem memory item) internal pure returns (bytes memory) {
                  uint256 listLength = _itemLength(item.memPtr);
                  require(listLength == item.len, "RLPReader: BYTES_DECODED_LENGTH_MISMATCH");
                  uint256 offset = _payloadOffset(item.memPtr);
          
                  uint256 len = item.len - offset; // data length
                  bytes memory result = new bytes(len);
          
                  uint256 destPtr;
                  assembly {
                      destPtr := add(0x20, result)
                  }
          
                  copy(item.memPtr + offset, destPtr, len);
                  return result;
              }
          
              /*
               * Private Helpers
               */
          
              // @return number of payload items inside an encoded list.
              function numItems(RLPItem memory item) private pure returns (uint256) {
                  // add `isList` check if `item` is expected to be passsed without a check from calling function
                  // require(isList(item), "RLPReader: NUM_ITEMS_NOT_LIST");
          
                  uint256 count = 0;
                  uint256 currPtr = item.memPtr + _payloadOffset(item.memPtr);
                  uint256 endPtr = item.memPtr + item.len;
                  while (currPtr < endPtr) {
                      currPtr = currPtr + _itemLength(currPtr); // skip over an item
                      require(currPtr <= endPtr, "RLPReader: NUM_ITEMS_DECODED_LENGTH_MISMATCH");
                      count++;
                  }
          
                  return count;
              }
          
              // @return entire rlp item byte length
              function _itemLength(uint256 memPtr) private pure returns (uint256) {
                  uint256 itemLen;
                  uint256 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(uint256 memPtr) private pure returns (uint256) {
                  uint256 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(
                  uint256 src,
                  uint256 dest,
                  uint256 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
                  uint256 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))
                  }
              }
          }
          
          // File: contracts/root/TokenPredicates/ITokenPredicate.sol
          
          pragma solidity 0.6.6;
          
          
          /// @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 Address
               * @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;
          }
          
          // File: contracts/common/Initializable.sol
          
          pragma solidity 0.6.6;
          
          contract Initializable {
              bool inited = false;
          
              modifier initializer() {
                  require(!inited, "already inited");
                  _;
                  inited = true;
              }
          }
          
          // File: contracts/root/TokenPredicates/ERC1155Predicate.sol
          
          pragma solidity 0.6.6;
          
          
          
          
          
          
          
          contract ERC1155Predicate is ITokenPredicate, ERC1155Receiver, AccessControlMixin, Initializable {
              using RLPReader for bytes;
              using RLPReader for RLPReader.RLPItem;
          
              bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE");
              bytes32 public constant TOKEN_TYPE = keccak256("ERC1155");
          
              // keccak256("TransferSingle(address,address,address,uint256,uint256)")
              bytes32 public constant TRANSFER_SINGLE_EVENT_SIG = 0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62;
              // keccak256("TransferBatch(address,address,address,uint256[],uint256[])")
              bytes32 public constant TRANSFER_BATCH_EVENT_SIG = 0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb;
          
              event LockedBatchERC1155(
                  address indexed depositor,
                  address indexed depositReceiver,
                  address indexed rootToken,
                  uint256[] ids,
                  uint256[] amounts
              );
          
              constructor() public {}
          
              function initialize(address _owner) external initializer {
                  _setupContractId("ERC1155Predicate");
                  _setupRole(DEFAULT_ADMIN_ROLE, _owner);
                  _setupRole(MANAGER_ROLE, _owner);
              }
          
              /**
               * @notice rejects single transfer
               */
              function onERC1155Received(
                  address,
                  address,
                  uint256,
                  uint256,
                  bytes calldata
              ) external override returns (bytes4) {
                  return 0;
              }
          
              /**
               * @notice accepts batch transfer
               */
              function onERC1155BatchReceived(
                  address,
                  address,
                  uint256[] calldata,
                  uint256[] calldata,
                  bytes calldata
              ) external override returns (bytes4) {
                  return ERC1155Receiver(0).onERC1155BatchReceived.selector;
              }
          
              /**
               * @notice Lock ERC1155 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 id array and amount array
               */
              function lockTokens(
                  address depositor,
                  address depositReceiver,
                  address rootToken,
                  bytes calldata depositData
              )
                  external
                  override
                  only(MANAGER_ROLE)
              {
                  // forcing batch deposit since supporting both single and batch deposit introduces too much complexity
                  (
                      uint256[] memory ids,
                      uint256[] memory amounts,
                      bytes memory data
                  ) = abi.decode(depositData, (uint256[], uint256[], bytes));
                  emit LockedBatchERC1155(
                      depositor,
                      depositReceiver,
                      rootToken,
                      ids,
                      amounts
                  );
                  IERC1155(rootToken).safeBatchTransferFrom(
                      depositor,
                      address(this),
                      ids,
                      amounts,
                      data
                  );
              }
          
              /**
               * @notice Validates log signature, from and to address
               * then sends the correct tokenId, amount to withdrawer
               * callable only by manager
               * @param rootToken Token which gets withdrawn
               * @param log Valid ERC1155 TransferSingle burn or TransferBatch burn log from child chain
               */
              function exitTokens(
                  address,
                  address rootToken,
                  bytes memory log
              )
                  public
                  override
                  only(MANAGER_ROLE)
              {
                  RLPReader.RLPItem[] memory logRLPList = log.toRlpItem().toList();
                  RLPReader.RLPItem[] memory logTopicRLPList = logRLPList[1].toList(); // topics
                  bytes memory logData = logRLPList[2].toBytes();
          
                  address withdrawer = address(logTopicRLPList[2].toUint()); // topic2 is from address
          
                  require(
                      address(logTopicRLPList[3].toUint()) == address(0), // topic3 is to address
                      "ERC1155Predicate: INVALID_RECEIVER"
                  );
          
                  if (bytes32(logTopicRLPList[0].toUint()) == TRANSFER_SINGLE_EVENT_SIG) { // topic0 is event sig
                      (uint256 id, uint256 amount) = abi.decode(
                          logData,
                          (uint256, uint256)
                      );
                      IERC1155(rootToken).safeTransferFrom(
                          address(this),
                          withdrawer,
                          id,
                          amount,
                          bytes("")
                      );
                  } else if (bytes32(logTopicRLPList[0].toUint()) == TRANSFER_BATCH_EVENT_SIG) {
                      (uint256[] memory ids, uint256[] memory amounts) = abi.decode(
                          logData,
                          (uint256[], uint256[])
                      );
                      IERC1155(rootToken).safeBatchTransferFrom(
                          address(this),
                          withdrawer,
                          ids,
                          amounts,
                          bytes("")
                      );
                  } else {
                      revert("ERC1155Predicate: INVALID_WITHDRAW_SIG");
                  }
              }
          }