ETH Price: $2,511.55 (+0.81%)

Transaction Decoder

Block:
22590609 at May-29-2025 07:53:11 PM +UTC
Transaction Fee:
0.000850574660889675 ETH $2.14
Gas Used:
187,401 Gas / 4.538794675 Gwei

Account State Difference:

  Address   Before After State Difference Code
0xbbB0CCBF...114Bf4BD2
0.007555368090381952 Eth
Nonce: 1
0.006704793429492277 Eth
Nonce: 2
0.000850574660889675
(BuilderNet)
45.697833343597581267 Eth45.698020744597581267 Eth0.000187401

Execution Trace

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

      File 3 of 3: 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));
          }
      }