ETH Price: $2,538.81 (-1.49%)

Transaction Decoder

Block:
22583782 at May-28-2025 08:56:35 PM +UTC
Transaction Fee:
0.000524335238467056 ETH $1.33
Gas Used:
234,303 Gas / 2.237851152 Gwei

Emitted Events:

548 ERC20PredicateProxy.0x9b217a401a5ddf7c4d474074aff9958a18d48690d77cc2151c4706aa7348b401( 0x9b217a401a5ddf7c4d474074aff9958a18d48690d77cc2151c4706aa7348b401, 0x000000000000000000000000ca74f404e0c7bfa35b13b511097df966d5a65597, 0x000000000000000000000000ca74f404e0c7bfa35b13b511097df966d5a65597, 0x00000000000000000000000092d6c1e31e14520e676a687f0a93788b716beff5, 000000000000000000000000000000000000000000000018f8748be4357d2000 )
549 DydxToken.DelegatedPowerChanged( user=[Sender] 0xca74f404e0c7bfa35b13b511097df966d5a65597, amount=801075617327, delegationType=0 )
550 DydxToken.DelegatedPowerChanged( user=ERC20PredicateProxy, amount=57122040970860837780626, delegationType=0 )
551 DydxToken.DelegatedPowerChanged( user=[Sender] 0xca74f404e0c7bfa35b13b511097df966d5a65597, amount=801075617327, delegationType=1 )
552 DydxToken.DelegatedPowerChanged( user=ERC20PredicateProxy, amount=57122040970860837780626, delegationType=1 )
553 DydxToken.Transfer( from=[Sender] 0xca74f404e0c7bfa35b13b511097df966d5a65597, to=ERC20PredicateProxy, value=460624946000000000000 )
554 DydxToken.Approval( owner=[Sender] 0xca74f404e0c7bfa35b13b511097df966d5a65597, spender=ERC20PredicateProxy, value=115792089237316195423570985008687907853269984665640564025682353062220486255141 )
555 StateSender.StateSynced( id=3071087, contractAddress=0xA6FA4fB5...9C5d1C0aa, data=0x87A7811F4BFEDEA3D341AD165680AE306B01AAEACC205D227629CF157DD9F821000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000A0000000000000000000000000CA74F404E0C7BFA35B13B511097DF966D5A6559700000000000000000000000092D6C1E31E14520E676A687F0A93788B716BEFF500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000018F8748BE4357D2000 )

Account State Difference:

  Address   Before After State Difference Code
0x28e4F3a7...189A5bFbE
(Polygon (Matic): State Syncer)
0x92D6C1e3...B716BEff5
(beaverbuild)
9.777738681983886772 Eth9.777765369095586772 Eth0.0000266871117
0xcA74F404...6D5a65597
6.649014716960869994 Eth
Nonce: 421435
6.648490381722402938 Eth
Nonce: 421436
0.000524335238467056

Execution Trace

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

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

          File 3 of 6: DydxToken
          // SPDX-License-Identifier: AGPL-3.0
          
          // File contracts/dependencies/open-zeppelin/Context.sol
          
          pragma solidity 0.7.5;
          
          /*
           * @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 contracts/dependencies/open-zeppelin/IERC20.sol
          
          pragma solidity 0.7.5;
          
          /**
           * @dev Interface of the ERC20 standard as defined in the EIP.
           */
          interface IERC20 {
              /**
               * @dev Returns the amount of tokens in existence.
               */
              function totalSupply() external view returns (uint256);
          
              /**
               * @dev Returns the amount of tokens owned by `account`.
               */
              function balanceOf(address account) external view returns (uint256);
          
              /**
               * @dev Moves `amount` tokens from the caller's account to `recipient`.
               *
               * Returns a boolean value indicating whether the operation succeeded.
               *
               * Emits a {Transfer} event.
               */
              function transfer(address recipient, uint256 amount) external returns (bool);
          
              /**
               * @dev Returns the remaining number of tokens that `spender` will be
               * allowed to spend on behalf of `owner` through {transferFrom}. This is
               * zero by default.
               *
               * This value changes when {approve} or {transferFrom} are called.
               */
              function allowance(address owner, address spender) external view returns (uint256);
          
              /**
               * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
               *
               * Returns a boolean value indicating whether the operation succeeded.
               *
               * IMPORTANT: Beware that changing an allowance with this method brings the risk
               * that someone may use both the old and the new allowance by unfortunate
               * transaction ordering. One possible solution to mitigate this race
               * condition is to first reduce the spender's allowance to 0 and set the
               * desired value afterwards:
               * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
               *
               * Emits an {Approval} event.
               */
              function approve(address spender, uint256 amount) external returns (bool);
          
              /**
               * @dev Moves `amount` tokens from `sender` to `recipient` using the
               * allowance mechanism. `amount` is then deducted from the caller's
               * allowance.
               *
               * Returns a boolean value indicating whether the operation succeeded.
               *
               * Emits a {Transfer} event.
               */
              function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
          
              /**
               * @dev Emitted when `value` tokens are moved from one account (`from`) to
               * another (`to`).
               *
               * Note that `value` may be zero.
               */
              event Transfer(address indexed from, address indexed to, uint256 value);
          
              /**
               * @dev Emitted when the allowance of a `spender` for an `owner` is set by
               * a call to {approve}. `value` is the new allowance.
               */
              event Approval(address indexed owner, address indexed spender, uint256 value);
          }
          
          
          // File contracts/dependencies/open-zeppelin/SafeMath.sol
          
          pragma solidity 0.7.5;
          
          /**
           * @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) {
              // Solidity only automatically asserts when dividing by 0
              require(b > 0, errorMessage);
              uint256 c = a / b;
              // assert(a == b * c + a % b); // There is no case in which this doesn't hold
          
              return c;
            }
          
            /**
             * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
             * Reverts when dividing by zero.
             *
             * Counterpart to Solidity's `%` operator. This function uses a `revert`
             * opcode (which leaves remaining gas untouched) while Solidity uses an
             * invalid opcode to revert (consuming all remaining gas).
             *
             * Requirements:
             * - The divisor cannot be zero.
             */
            function mod(uint256 a, uint256 b) internal pure returns (uint256) {
              return mod(a, b, 'SafeMath: modulo by zero');
            }
          
            /**
             * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
             * Reverts with custom message when dividing by zero.
             *
             * Counterpart to Solidity's `%` operator. This function uses a `revert`
             * opcode (which leaves remaining gas untouched) while Solidity uses an
             * invalid opcode to revert (consuming all remaining gas).
             *
             * Requirements:
             * - The divisor cannot be zero.
             */
            function mod(
              uint256 a,
              uint256 b,
              string memory errorMessage
            ) internal pure returns (uint256) {
              require(b != 0, errorMessage);
              return a % b;
            }
          }
          
          
          // File contracts/dependencies/open-zeppelin/Address.sol
          
          pragma solidity 0.7.5;
          
          /**
           * @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');
            }
          }
          
          
          // File contracts/dependencies/open-zeppelin/ERC20.sol
          
          pragma solidity ^0.7.5;
          
          
          
          
          /**
           * @dev Implementation of the {IERC20} interface.
           *
           * This implementation is agnostic to the way tokens are created. This means
           * that a supply mechanism has to be added in a derived contract using {_mint}.
           * For a generic mechanism see {ERC20PresetMinterPauser}.
           *
           * TIP: For a detailed writeup see our guide
           * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
           * to implement supply mechanisms].
           *
           * We have followed general OpenZeppelin guidelines: functions revert instead
           * of returning `false` on failure. This behavior is nonetheless conventional
           * and does not conflict with the expectations of ERC20 applications.
           *
           * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
           * This allows applications to reconstruct the allowance for all accounts just
           * by listening to said events. Other implementations of the EIP may not emit
           * these events, as it isn't required by the specification.
           *
           * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
           * functions have been added to mitigate the well-known issues around setting
           * allowances. See {IERC20-approve}.
           */
          contract ERC20 is Context, IERC20 {
              using SafeMath for uint256;
              using Address for address;
          
              mapping (address => uint256) private _balances;
          
              mapping (address => mapping (address => uint256)) private _allowances;
          
              uint256 private _totalSupply;
          
              string internal _name;
              string internal _symbol;
              uint8 private _decimals;
          
              /**
               * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
               * a default value of 18.
               *
               * To select a different value for {decimals}, use {_setupDecimals}.
               *
               * All three of these values are immutable: they can only be set once during
               * construction.
               */
              constructor (string memory name, string memory symbol) public {
                  _name = name;
                  _symbol = symbol;
                  _decimals = 18;
              }
          
              /**
               * @dev Returns the name of the token.
               */
              function name() virtual public view returns (string memory) {
                  return _name;
              }
          
              /**
               * @dev Returns the symbol of the token, usually a shorter version of the
               * name.
               */
              function symbol() virtual public view returns (string memory) {
                  return _symbol;
              }
          
              /**
               * @dev Returns the number of decimals used to get its user representation.
               * For example, if `decimals` equals `2`, a balance of `505` tokens should
               * be displayed to a user as `5,05` (`505 / 10 ** 2`).
               *
               * Tokens usually opt for a value of 18, imitating the relationship between
               * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
               * called.
               *
               * NOTE: This information is only used for _display_ purposes: it in
               * no way affects any of the arithmetic of the contract, including
               * {IERC20-balanceOf} and {IERC20-transfer}.
               */
              function decimals() virtual public view returns (uint8) {
                  return _decimals;
              }
          
              /**
               * @dev See {IERC20-totalSupply}.
               */
              function totalSupply() public view override returns (uint256) {
                  return _totalSupply;
              }
          
              /**
               * @dev See {IERC20-balanceOf}.
               */
              function balanceOf(address account) public view override returns (uint256) {
                  return _balances[account];
              }
          
              /**
               * @dev See {IERC20-transfer}.
               *
               * Requirements:
               *
               * - `recipient` cannot be the zero address.
               * - the caller must have a balance of at least `amount`.
               */
              function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
                  _transfer(_msgSender(), recipient, amount);
                  return true;
              }
          
              /**
               * @dev See {IERC20-allowance}.
               */
              function allowance(address owner, address spender) public view virtual override returns (uint256) {
                  return _allowances[owner][spender];
              }
          
              /**
               * @dev See {IERC20-approve}.
               *
               * Requirements:
               *
               * - `spender` cannot be the zero address.
               */
              function approve(address spender, uint256 amount) public virtual override returns (bool) {
                  _approve(_msgSender(), spender, amount);
                  return true;
              }
          
              /**
               * @dev See {IERC20-transferFrom}.
               *
               * Emits an {Approval} event indicating the updated allowance. This is not
               * required by the EIP. See the note at the beginning of {ERC20};
               *
               * Requirements:
               * - `sender` and `recipient` cannot be the zero address.
               * - `sender` must have a balance of at least `amount`.
               * - the caller must have allowance for ``sender``'s tokens of at least
               * `amount`.
               */
              function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
                  _transfer(sender, recipient, amount);
                  _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
                  return true;
              }
          
              /**
               * @dev Atomically increases the allowance granted to `spender` by the caller.
               *
               * This is an alternative to {approve} that can be used as a mitigation for
               * problems described in {IERC20-approve}.
               *
               * Emits an {Approval} event indicating the updated allowance.
               *
               * Requirements:
               *
               * - `spender` cannot be the zero address.
               */
              function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
                  _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
                  return true;
              }
          
              /**
               * @dev Atomically decreases the allowance granted to `spender` by the caller.
               *
               * This is an alternative to {approve} that can be used as a mitigation for
               * problems described in {IERC20-approve}.
               *
               * Emits an {Approval} event indicating the updated allowance.
               *
               * Requirements:
               *
               * - `spender` cannot be the zero address.
               * - `spender` must have allowance for the caller of at least
               * `subtractedValue`.
               */
              function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
                  _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
                  return true;
              }
          
              /**
               * @dev Moves tokens `amount` from `sender` to `recipient`.
               *
               * This is internal function is equivalent to {transfer}, and can be used to
               * e.g. implement automatic token fees, slashing mechanisms, etc.
               *
               * Emits a {Transfer} event.
               *
               * Requirements:
               *
               * - `sender` cannot be the zero address.
               * - `recipient` cannot be the zero address.
               * - `sender` must have a balance of at least `amount`.
               */
              function _transfer(address sender, address recipient, uint256 amount) internal virtual {
                  require(sender != address(0), "ERC20: transfer from the zero address");
                  require(recipient != address(0), "ERC20: transfer to the zero address");
          
                  _beforeTokenTransfer(sender, recipient, amount);
          
                  _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
                  _balances[recipient] = _balances[recipient].add(amount);
                  emit Transfer(sender, recipient, amount);
              }
          
              /** @dev Creates `amount` tokens and assigns them to `account`, increasing
               * the total supply.
               *
               * Emits a {Transfer} event with `from` set to the zero address.
               *
               * Requirements
               *
               * - `to` cannot be the zero address.
               */
              function _mint(address account, uint256 amount) internal virtual {
                  require(account != address(0), "ERC20: mint to the zero address");
          
                  _beforeTokenTransfer(address(0), account, amount);
          
                  _totalSupply = _totalSupply.add(amount);
                  _balances[account] = _balances[account].add(amount);
                  emit Transfer(address(0), account, amount);
              }
          
              /**
               * @dev Destroys `amount` tokens from `account`, reducing the
               * total supply.
               *
               * Emits a {Transfer} event with `to` set to the zero address.
               *
               * Requirements
               *
               * - `account` cannot be the zero address.
               * - `account` must have at least `amount` tokens.
               */
              function _burn(address account, uint256 amount) internal virtual {
                  require(account != address(0), "ERC20: burn from the zero address");
          
                  _beforeTokenTransfer(account, address(0), amount);
          
                  _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
                  _totalSupply = _totalSupply.sub(amount);
                  emit Transfer(account, address(0), amount);
              }
          
              /**
               * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
               *
               * This is internal function is equivalent to `approve`, and can be used to
               * e.g. set automatic allowances for certain subsystems, etc.
               *
               * Emits an {Approval} event.
               *
               * Requirements:
               *
               * - `owner` cannot be the zero address.
               * - `spender` cannot be the zero address.
               */
              function _approve(address owner, address spender, uint256 amount) internal virtual {
                  require(owner != address(0), "ERC20: approve from the zero address");
                  require(spender != address(0), "ERC20: approve to the zero address");
          
                  _allowances[owner][spender] = amount;
                  emit Approval(owner, spender, amount);
              }
          
              /**
               * @dev Sets {decimals} to a value other than the default one of 18.
               *
               * WARNING: This function should only be called from the constructor. Most
               * applications that interact with token contracts will not expect
               * {decimals} to ever change, and may work incorrectly if it does.
               */
              function _setupDecimals(uint8 decimals_) internal {
                  _decimals = decimals_;
              }
          
              /**
               * @dev Hook that is called before any transfer of tokens. This includes
               * minting and burning.
               *
               * Calling conditions:
               *
               * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
               * will be to transferred to `to`.
               * - when `from` is zero, `amount` tokens will be minted for `to`.
               * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
               * - `from` and `to` are never both zero.
               *
               * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
               */
              function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
          }
          
          
          // File contracts/dependencies/open-zeppelin/Ownable.sol
          
          pragma solidity 0.7.5;
          
          /**
           * @dev Contract module which provides a basic access control mechanism, where
           * there is an account (an owner) that can be granted exclusive access to
           * specific functions.
           *
           * By default, the owner account will be the one that deploys the contract. This
           * can later be changed with {transferOwnership}.
           *
           * This module is used through inheritance. It will make available the modifier
           * `onlyOwner`, which can be applied to your functions to restrict their use to
           * the owner.
           */
          contract Ownable is Context {
            address private _owner;
          
            event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          
            /**
             * @dev Initializes the contract setting the deployer as the initial owner.
             */
            constructor() {
              address msgSender = _msgSender();
              _owner = msgSender;
              emit OwnershipTransferred(address(0), msgSender);
            }
          
            /**
             * @dev Returns the address of the current owner.
             */
            function owner() public view returns (address) {
              return _owner;
            }
          
            /**
             * @dev Throws if called by any account other than the owner.
             */
            modifier onlyOwner() {
              require(_owner == _msgSender(), 'Ownable: caller is not the owner');
              _;
            }
          
            /**
             * @dev Leaves the contract without owner. It will not be possible to call
             * `onlyOwner` functions anymore. Can only be called by the current owner.
             *
             * NOTE: Renouncing ownership will leave the contract without an owner,
             * thereby removing any functionality that is only available to the owner.
             */
            function renounceOwnership() public virtual onlyOwner {
              emit OwnershipTransferred(_owner, address(0));
              _owner = address(0);
            }
          
            /**
             * @dev Transfers ownership of the contract to a new account (`newOwner`).
             * Can only be called by the current owner.
             */
            function transferOwnership(address newOwner) public virtual onlyOwner {
              require(newOwner != address(0), 'Ownable: new owner is the zero address');
              emit OwnershipTransferred(_owner, newOwner);
              _owner = newOwner;
            }
          }
          
          
          // File contracts/interfaces/IGovernancePowerDelegationERC20.sol
          
          pragma solidity 0.7.5;
          
          interface IGovernancePowerDelegationERC20 {
          
            enum DelegationType {
              VOTING_POWER,
              PROPOSITION_POWER
            }
          
            /**
             * @dev Emitted when a user delegates governance power to another user.
             *
             * @param  delegator       The delegator.
             * @param  delegatee       The delegatee.
             * @param  delegationType  The type of delegation (VOTING_POWER, PROPOSITION_POWER).
             */
            event DelegateChanged(
              address indexed delegator,
              address indexed delegatee,
              DelegationType delegationType
            );
          
            /**
             * @dev Emitted when an action changes the delegated power of a user.
             *
             * @param  user            The user whose delegated power has changed.
             * @param  amount          The new amount of delegated power for the user.
             * @param  delegationType  The type of delegation (VOTING_POWER, PROPOSITION_POWER).
             */
            event DelegatedPowerChanged(address indexed user, uint256 amount, DelegationType delegationType);
          
            /**
             * @dev Delegates a specific governance power to a delegatee.
             *
             * @param  delegatee       The address to delegate power to.
             * @param  delegationType  The type of delegation (VOTING_POWER, PROPOSITION_POWER).
             */
            function delegateByType(address delegatee, DelegationType delegationType) external virtual;
          
            /**
             * @dev Delegates all governance powers to a delegatee.
             *
             * @param  delegatee  The user to which the power will be delegated.
             */
            function delegate(address delegatee) external virtual;
          
            /**
             * @dev Returns the delegatee of an user.
             *
             * @param  delegator       The address of the delegator.
             * @param  delegationType  The type of delegation (VOTING_POWER, PROPOSITION_POWER).
             */
            function getDelegateeByType(address delegator, DelegationType delegationType)
              external
              view
              virtual
              returns (address);
          
            /**
             * @dev Returns the current delegated power of a user. The current power is the power delegated
             *  at the time of the last snapshot.
             *
             * @param  user            The user whose power to query.
             * @param  delegationType  The type of power (VOTING_POWER, PROPOSITION_POWER).
             */
            function getPowerCurrent(address user, DelegationType delegationType)
              external
              view
              virtual
              returns (uint256);
          
            /**
             * @dev Returns the delegated power of a user at a certain block.
             *
             * @param  user            The user whose power to query.
             * @param  blockNumber     The block number at which to get the user's power.
             * @param  delegationType  The type of power (VOTING_POWER, PROPOSITION_POWER).
             */
            function getPowerAtBlock(
              address user,
              uint256 blockNumber,
              DelegationType delegationType
            )
              external
              view
              virtual
              returns (uint256);
          }
          
          
          // File contracts/governance/token/GovernancePowerDelegationERC20Mixin.sol
          
          pragma solidity 0.7.5;
          
          
          
          /**
           * @title GovernancePowerDelegationERC20Mixin
           * @author dYdX
           *
           * @dev Provides support for two types of governance powers, both endowed by the governance
           *  token, and separately delegatable. Provides functions for delegation and for querying a user's
           *  power at a certain block number.
           */
          abstract contract GovernancePowerDelegationERC20Mixin is
            ERC20,
            IGovernancePowerDelegationERC20
          {
            using SafeMath for uint256;
          
            // ============ Constants ============
          
            /// @notice EIP-712 typehash for delegation by signature of a specific governance power type.
            bytes32 public constant DELEGATE_BY_TYPE_TYPEHASH = keccak256(
              'DelegateByType(address delegatee,uint256 type,uint256 nonce,uint256 expiry)'
            );
          
            /// @notice EIP-712 typehash for delegation by signature of all governance powers.
            bytes32 public constant DELEGATE_TYPEHASH = keccak256(
              'Delegate(address delegatee,uint256 nonce,uint256 expiry)'
            );
          
            // ============ Structs ============
          
            /// @dev Snapshot of a value on a specific block, used to track voting power for proposals.
            struct Snapshot {
              uint128 blockNumber;
              uint128 value;
            }
          
            // ============ External Functions ============
          
            /**
             * @notice Delegates a specific governance power to a delegatee.
             *
             * @param  delegatee       The address to delegate power to.
             * @param  delegationType  The type of delegation (VOTING_POWER, PROPOSITION_POWER).
             */
            function delegateByType(
              address delegatee,
              DelegationType delegationType
            )
              external
              override
            {
              _delegateByType(msg.sender, delegatee, delegationType);
            }
          
            /**
             * @notice Delegates all governance powers to a delegatee.
             *
             * @param  delegatee  The address to delegate power to.
             */
            function delegate(
              address delegatee
            )
              external
              override
            {
              _delegateByType(msg.sender, delegatee, DelegationType.VOTING_POWER);
              _delegateByType(msg.sender, delegatee, DelegationType.PROPOSITION_POWER);
            }
          
            /**
             * @notice Returns the delegatee of a user.
             *
             * @param  delegator       The address of the delegator.
             * @param  delegationType  The type of delegation (VOTING_POWER, PROPOSITION_POWER).
             */
            function getDelegateeByType(
              address delegator,
              DelegationType delegationType
            )
              external
              override
              view
              returns (address)
            {
              (, , mapping(address => address) storage delegates) = _getDelegationDataByType(delegationType);
          
              return _getDelegatee(delegator, delegates);
            }
          
            /**
             * @notice Returns the current power of a user. The current power is the power delegated
             *  at the time of the last snapshot.
             *
             * @param  user            The user whose power to query.
             * @param  delegationType  The type of power (VOTING_POWER, PROPOSITION_POWER).
             */
            function getPowerCurrent(
              address user,
              DelegationType delegationType
            )
              external
              override
              view
              returns (uint256)
            {
              (
                mapping(address => mapping(uint256 => Snapshot)) storage snapshots,
                mapping(address => uint256) storage snapshotsCounts,
                // delegates
              ) = _getDelegationDataByType(delegationType);
          
              return _searchByBlockNumber(snapshots, snapshotsCounts, user, block.number);
            }
          
            /**
             * @notice Returns the power of a user at a certain block.
             *
             * @param  user            The user whose power to query.
             * @param  blockNumber     The block number at which to get the user's power.
             * @param  delegationType  The type of power (VOTING_POWER, PROPOSITION_POWER).
             */
            function getPowerAtBlock(
              address user,
              uint256 blockNumber,
              DelegationType delegationType
            )
              external
              override
              view
              returns (uint256)
            {
              (
                mapping(address => mapping(uint256 => Snapshot)) storage snapshots,
                mapping(address => uint256) storage snapshotsCounts,
                // delegates
              ) = _getDelegationDataByType(delegationType);
          
              return _searchByBlockNumber(snapshots, snapshotsCounts, user, blockNumber);
            }
          
            // ============ Internal Functions ============
          
            /**
             * @dev Delegates one specific power to a delegatee.
             *
             * @param  delegator       The user whose power to delegate.
             * @param  delegatee       The address to delegate power to.
             * @param  delegationType  The type of power (VOTING_POWER, PROPOSITION_POWER).
             */
            function _delegateByType(
              address delegator,
              address delegatee,
              DelegationType delegationType
            )
              internal
            {
              require(
                delegatee != address(0),
                'INVALID_DELEGATEE'
              );
          
              (, , mapping(address => address) storage delegates) = _getDelegationDataByType(delegationType);
          
              uint256 delegatorBalance = balanceOf(delegator);
          
              address previousDelegatee = _getDelegatee(delegator, delegates);
          
              delegates[delegator] = delegatee;
          
              _moveDelegatesByType(previousDelegatee, delegatee, delegatorBalance, delegationType);
              emit DelegateChanged(delegator, delegatee, delegationType);
            }
          
            /**
             * @dev Moves power from one user to another.
             *
             * @param  from            The user from which delegated power is moved.
             * @param  to              The user that will receive the delegated power.
             * @param  amount          The amount of power to be moved.
             * @param  delegationType  The type of power (VOTING_POWER, PROPOSITION_POWER).
             */
            function _moveDelegatesByType(
              address from,
              address to,
              uint256 amount,
              DelegationType delegationType
            )
              internal
            {
              if (from == to) {
                return;
              }
          
              (
                mapping(address => mapping(uint256 => Snapshot)) storage snapshots,
                mapping(address => uint256) storage snapshotsCounts,
                // delegates
              ) = _getDelegationDataByType(delegationType);
          
              if (from != address(0)) {
                uint256 previous = 0;
                uint256 fromSnapshotsCount = snapshotsCounts[from];
          
                if (fromSnapshotsCount != 0) {
                  previous = snapshots[from][fromSnapshotsCount - 1].value;
                } else {
                  previous = balanceOf(from);
                }
          
                uint256 newAmount = previous.sub(amount);
                _writeSnapshot(
                  snapshots,
                  snapshotsCounts,
                  from,
                  uint128(newAmount)
                );
          
                emit DelegatedPowerChanged(from, newAmount, delegationType);
              }
          
              if (to != address(0)) {
                uint256 previous = 0;
                uint256 toSnapshotsCount = snapshotsCounts[to];
                if (toSnapshotsCount != 0) {
                  previous = snapshots[to][toSnapshotsCount - 1].value;
                } else {
                  previous = balanceOf(to);
                }
          
                uint256 newAmount = previous.add(amount);
                _writeSnapshot(
                  snapshots,
                  snapshotsCounts,
                  to,
                  uint128(newAmount)
                );
          
                emit DelegatedPowerChanged(to, newAmount, delegationType);
              }
            }
          
            /**
             * @dev Searches for a balance snapshot by block number using binary search.
             *
             * @param  snapshots        The mapping of snapshots by user.
             * @param  snapshotsCounts  The mapping of the number of snapshots by user.
             * @param  user             The user for which the snapshot is being searched.
             * @param  blockNumber      The block number being searched.
             */
            function _searchByBlockNumber(
              mapping(address => mapping(uint256 => Snapshot)) storage snapshots,
              mapping(address => uint256) storage snapshotsCounts,
              address user,
              uint256 blockNumber
            )
              internal
              view
              returns (uint256)
            {
              require(
                blockNumber <= block.number,
                'INVALID_BLOCK_NUMBER'
              );
          
              uint256 snapshotsCount = snapshotsCounts[user];
          
              if (snapshotsCount == 0) {
                return balanceOf(user);
              }
          
              // First check most recent balance
              if (snapshots[user][snapshotsCount - 1].blockNumber <= blockNumber) {
                return snapshots[user][snapshotsCount - 1].value;
              }
          
              // Next check implicit zero balance
              if (snapshots[user][0].blockNumber > blockNumber) {
                return 0;
              }
          
              uint256 lower = 0;
              uint256 upper = snapshotsCount - 1;
              while (upper > lower) {
                uint256 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
                Snapshot memory snapshot = snapshots[user][center];
                if (snapshot.blockNumber == blockNumber) {
                  return snapshot.value;
                } else if (snapshot.blockNumber < blockNumber) {
                  lower = center;
                } else {
                  upper = center - 1;
                }
              }
              return snapshots[user][lower].value;
            }
          
            /**
             * @dev Returns delegation data (snapshot, snapshotsCount, delegates) by delegation type.
             *
             *  Note: This mixin contract does not itself define any storage, and we require the inheriting
             *  contract to implement this method to provide access to the relevant mappings in storage.
             *  This pattern was implemented by Aave for legacy reasons and we have decided not to change it.
             *
             * @param  delegationType  The type of power (VOTING_POWER, PROPOSITION_POWER).
             */
            function _getDelegationDataByType(
              DelegationType delegationType
            )
              internal
              virtual
              view
              returns (
                mapping(address => mapping(uint256 => Snapshot)) storage, // snapshots
                mapping(address => uint256) storage, // snapshotsCount
                mapping(address => address) storage // delegates
              );
          
            /**
             * @dev Writes a snapshot of a user's token/power balance.
             *
             * @param  snapshots        The mapping of snapshots by user.
             * @param  snapshotsCounts  The mapping of the number of snapshots by user.
             * @param  owner            The user whose power to snapshot.
             * @param  newValue         The new balance to snapshot at the current block.
             */
            function _writeSnapshot(
              mapping(address => mapping(uint256 => Snapshot)) storage snapshots,
              mapping(address => uint256) storage snapshotsCounts,
              address owner,
              uint128 newValue
            )
              internal
            {
              uint128 currentBlock = uint128(block.number);
          
              uint256 ownerSnapshotsCount = snapshotsCounts[owner];
              mapping(uint256 => Snapshot) storage ownerSnapshots = snapshots[owner];
          
              if (
                ownerSnapshotsCount != 0 &&
                ownerSnapshots[ownerSnapshotsCount - 1].blockNumber == currentBlock
              ) {
                // Doing multiple operations in the same block
                ownerSnapshots[ownerSnapshotsCount - 1].value = newValue;
              } else {
                ownerSnapshots[ownerSnapshotsCount] = Snapshot(currentBlock, newValue);
                snapshotsCounts[owner] = ownerSnapshotsCount + 1;
              }
            }
          
            /**
             * @dev Returns the delegatee of a user. If a user never performed any delegation, their
             *  delegated address will be 0x0, in which case we return the user's own address.
             *
             * @param  delegator  The address of the user for which return the delegatee.
             * @param  delegates  The mapping of delegates for a particular type of delegation.
             */
            function _getDelegatee(
              address delegator,
              mapping(address => address) storage delegates
            )
              internal
              view
              returns (address)
            {
              address previousDelegatee = delegates[delegator];
          
              if (previousDelegatee == address(0)) {
                return delegator;
              }
          
              return previousDelegatee;
            }
          }
          
          
          // File contracts/governance/token/DydxToken.sol
          
          pragma solidity 0.7.5;
          
          
          
          
          /**
           * @title DydxToken
           * @author dYdX
           *
           * @notice The dYdX governance token.
           */
          contract DydxToken is
            GovernancePowerDelegationERC20Mixin,
            Ownable
          {
            using SafeMath for uint256;
          
            // ============ Events ============
          
            /**
             * @dev Emitted when an address has been added to or removed from the token transfer allowlist.
             *
             * @param  account    Address that was added to or removed from the token transfer allowlist.
             * @param  isAllowed  True if the address was added to the allowlist, false if removed.
             */
            event TransferAllowlistUpdated(
              address account,
              bool isAllowed
            );
          
            /**
             * @dev Emitted when the transfer restriction timestamp is reassigned.
             *
             * @param  transfersRestrictedBefore  The new timestamp on and after which non-allowlisted
             *                                    transfers may occur.
             */
            event TransfersRestrictedBeforeUpdated(
              uint256 transfersRestrictedBefore
            );
          
            // ============ Constants ============
          
            string internal constant NAME = 'dYdX';
            string internal constant SYMBOL = 'DYDX';
          
            uint256 public constant INITIAL_SUPPLY = 1_000_000_000 ether;
          
            bytes32 public immutable DOMAIN_SEPARATOR;
            bytes public constant EIP712_VERSION = '1';
            bytes32 public constant EIP712_DOMAIN = keccak256(
              'EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'
            );
            bytes32 public constant PERMIT_TYPEHASH = keccak256(
              'Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)'
            );
          
            /// @notice Minimum time between mints.
            uint256 public constant MINT_MIN_INTERVAL = 365 days;
          
            /// @notice Cap on the percentage of the total supply that can be minted at each mint.
            ///  Denominated in percentage points (units out of 100).
            uint256 public immutable MINT_MAX_PERCENT;
          
            /// @notice The timestamp on and after which the transfer restriction must be lifted.
            uint256 public immutable TRANSFER_RESTRICTION_LIFTED_NO_LATER_THAN;
          
            // ============ Storage ============
          
            /// @dev Mapping from (owner) => (next valid nonce) for EIP-712 signatures.
            mapping(address => uint256) internal _nonces;
          
            mapping(address => mapping(uint256 => Snapshot)) public _votingSnapshots;
            mapping(address => uint256) public _votingSnapshotsCounts;
            mapping(address => address) public _votingDelegates;
          
            mapping(address => mapping(uint256 => Snapshot)) public _propositionPowerSnapshots;
            mapping(address => uint256) public _propositionPowerSnapshotsCounts;
            mapping(address => address) public _propositionPowerDelegates;
          
            /// @notice Snapshots of the token total supply, at each block where the total supply has changed.
            mapping(uint256 => Snapshot) public _totalSupplySnapshots;
          
            /// @notice Number of snapshots of the token total supply.
            uint256 public _totalSupplySnapshotsCount;
          
            /// @notice Allowlist of addresses which may send or receive tokens while transfers are
            ///  otherwise restricted.
            mapping(address => bool) public _tokenTransferAllowlist;
          
            /// @notice The timestamp on and after which minting may occur.
            uint256 public _mintingRestrictedBefore;
          
            /// @notice The timestamp on and after which non-allowlisted transfers may occur.
            uint256 public _transfersRestrictedBefore;
          
            // ============ Constructor ============
          
            /**
             * @notice Constructor.
             *
             * @param  distributor                           The address which will receive the initial supply of tokens.
             * @param  transfersRestrictedBefore             Timestamp, before which transfers are restricted unless the
             *                                               origin or destination address is in the allowlist.
             * @param  transferRestrictionLiftedNoLaterThan  Timestamp, which is the maximum timestamp that transfer
             *                                               restrictions can be extended to.
             * @param  mintingRestrictedBefore               Timestamp, before which minting is not allowed.
             * @param  mintMaxPercent                        Cap on the percentage of the total supply that can be minted at
             *                                               each mint.
             */
            constructor(
              address distributor,
              uint256 transfersRestrictedBefore,
              uint256 transferRestrictionLiftedNoLaterThan,
              uint256 mintingRestrictedBefore,
              uint256 mintMaxPercent
            )
              ERC20(NAME, SYMBOL)
            {
              uint256 chainId;
          
              // solium-disable-next-line
              assembly {
                chainId := chainid()
              }
          
              DOMAIN_SEPARATOR = keccak256(
                abi.encode(
                  EIP712_DOMAIN,
                  keccak256(bytes(NAME)),
                  keccak256(bytes(EIP712_VERSION)),
                  chainId,
                  address(this)
                )
              );
          
              // Validate and set parameters.
              require(
                transfersRestrictedBefore > block.timestamp,
                'TRANSFERS_RESTRICTED_BEFORE_TOO_EARLY'
              );
              require(
                transfersRestrictedBefore <= transferRestrictionLiftedNoLaterThan,
                'MAX_TRANSFER_RESTRICTION_TOO_EARLY'
              );
              require(
                mintingRestrictedBefore > block.timestamp,
                'MINTING_RESTRICTED_BEFORE_TOO_EARLY'
              );
              _transfersRestrictedBefore = transfersRestrictedBefore;
              TRANSFER_RESTRICTION_LIFTED_NO_LATER_THAN = transferRestrictionLiftedNoLaterThan;
              _mintingRestrictedBefore = mintingRestrictedBefore;
              MINT_MAX_PERCENT = mintMaxPercent;
          
              // Mint the initial supply.
              _mint(distributor, INITIAL_SUPPLY);
          
              emit TransfersRestrictedBeforeUpdated(transfersRestrictedBefore);
            }
          
            // ============ Other Functions ============
          
            /**
             * @notice Adds addresses to the token transfer allowlist. Reverts if any of the addresses
             *  already exist in the allowlist. Only callable by owner.
             *
             * @param  addressesToAdd  Addresses to add to the token transfer allowlist.
             */
            function addToTokenTransferAllowlist(
              address[] calldata addressesToAdd
            )
              external
              onlyOwner
            {
              for (uint256 i = 0; i < addressesToAdd.length; i++) {
                require(
                  !_tokenTransferAllowlist[addressesToAdd[i]],
                  'ADDRESS_EXISTS_IN_TRANSFER_ALLOWLIST'
                );
                _tokenTransferAllowlist[addressesToAdd[i]] = true;
                emit TransferAllowlistUpdated(addressesToAdd[i], true);
              }
            }
          
            /**
             * @notice Removes addresses from the token transfer allowlist. Reverts if any of the addresses
             *  don't exist in the allowlist. Only callable by owner.
             *
             * @param  addressesToRemove  Addresses to remove from the token transfer allowlist.
             */
            function removeFromTokenTransferAllowlist(
              address[] calldata addressesToRemove
            )
              external
              onlyOwner
            {
              for (uint256 i = 0; i < addressesToRemove.length; i++) {
                require(
                  _tokenTransferAllowlist[addressesToRemove[i]],
                  'ADDRESS_DOES_NOT_EXIST_IN_TRANSFER_ALLOWLIST'
                );
                _tokenTransferAllowlist[addressesToRemove[i]] = false;
                emit TransferAllowlistUpdated(addressesToRemove[i], false);
              }
            }
          
            /**
             * @notice Updates the transfer restriction. Reverts if the transfer restriction has already passed,
             *  the new transfer restriction is earlier than the previous one, or the new transfer restriction is
             *  after the maximum transfer restriction.
             *
             * @param  transfersRestrictedBefore  The timestamp on and after which non-allowlisted transfers may occur.
             */
            function updateTransfersRestrictedBefore(
              uint256 transfersRestrictedBefore
            )
              external
              onlyOwner
            {
              uint256 previousTransfersRestrictedBefore = _transfersRestrictedBefore;
              require(
                block.timestamp < previousTransfersRestrictedBefore,
                'TRANSFER_RESTRICTION_ENDED'
              );
              require(
                previousTransfersRestrictedBefore <= transfersRestrictedBefore,
                'NEW_TRANSFER_RESTRICTION_TOO_EARLY'
              );
              require(
                transfersRestrictedBefore <= TRANSFER_RESTRICTION_LIFTED_NO_LATER_THAN,
                'AFTER_MAX_TRANSFER_RESTRICTION'
              );
          
              _transfersRestrictedBefore = transfersRestrictedBefore;
          
              emit TransfersRestrictedBeforeUpdated(transfersRestrictedBefore);
            }
          
            /**
             * @notice Mint new tokens. Only callable by owner after the required time period has elapsed.
             *
             * @param  recipient  The address to receive minted tokens.
             * @param  amount     The number of tokens to mint.
             */
            function mint(
              address recipient,
              uint256 amount
            )
              external
              onlyOwner
            {
              require(
                block.timestamp >= _mintingRestrictedBefore,
                'MINT_TOO_EARLY'
              );
              require(
                amount <= totalSupply().mul(MINT_MAX_PERCENT).div(100),
                'MAX_MINT_EXCEEDED'
              );
          
              // Update the next allowed minting time.
              _mintingRestrictedBefore = block.timestamp.add(MINT_MIN_INTERVAL);
          
              // Mint the amount.
              _mint(recipient, amount);
            }
          
            /**
             * @notice Implements the permit function as specified in EIP-2612.
             *
             * @param  owner     Address of the token owner.
             * @param  spender   Address of the spender.
             * @param  value     Amount of allowance.
             * @param  deadline  Expiration timestamp for the signature.
             * @param  v         Signature param.
             * @param  r         Signature param.
             * @param  s         Signature param.
             */
            function permit(
              address owner,
              address spender,
              uint256 value,
              uint256 deadline,
              uint8 v,
              bytes32 r,
              bytes32 s
            )
              external
            {
              require(
                owner != address(0),
                'INVALID_OWNER'
              );
              require(
                block.timestamp <= deadline,
                'INVALID_EXPIRATION'
              );
              uint256 currentValidNonce = _nonces[owner];
              bytes32 digest = keccak256(
                abi.encodePacked(
                  '\x19\x01',
                  DOMAIN_SEPARATOR,
                  keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, currentValidNonce, deadline))
                )
              );
          
              require(
                owner == ecrecover(digest, v, r, s),
                'INVALID_SIGNATURE'
              );
              _nonces[owner] = currentValidNonce.add(1);
              _approve(owner, spender, value);
            }
          
            /**
             * @notice Get the next valid nonce for EIP-712 signatures.
             *
             *  This nonce should be used when signing for any of the following functions:
             *   - permit()
             *   - delegateByTypeBySig()
             *   - delegateBySig()
             */
            function nonces(
              address owner
            )
              external
              view
              returns (uint256)
            {
              return _nonces[owner];
            }
          
            function transfer(
              address recipient,
              uint256 amount
            )
              public
              override
              returns (bool)
            {
              _requireTransferAllowed(_msgSender(), recipient);
              return super.transfer(recipient, amount);
            }
          
            function transferFrom(
              address sender,
              address recipient,
              uint256 amount
            )
              public
              override
              returns (bool)
            {
              _requireTransferAllowed(sender, recipient);
              return super.transferFrom(sender, recipient, amount);
            }
          
            /**
             * @dev Override _mint() to write a snapshot whenever the total supply changes.
             *
             *  These snapshots are intended to be used by the governance strategy.
             *
             *  Note that the ERC20 _burn() function is never used. If desired, an official burn mechanism
             *  could be implemented external to this contract, and accounted for in the governance strategy.
             */
            function _mint(
              address account,
              uint256 amount
            )
              internal
              override
            {
              super._mint(account, amount);
          
              uint256 snapshotsCount = _totalSupplySnapshotsCount;
              uint128 currentBlock = uint128(block.number);
              uint128 newValue = uint128(totalSupply());
          
              // Note: There is no special case for the total supply being updated multiple times in the same
              // block. That should never occur.
              _totalSupplySnapshots[snapshotsCount] = Snapshot(currentBlock, newValue);
              _totalSupplySnapshotsCount = snapshotsCount.add(1);
            }
          
            function _requireTransferAllowed(
              address sender,
              address recipient
            )
              view
              internal
            {
              // Compare against the constant `TRANSFER_RESTRICTION_LIFTED_NO_LATER_THAN` first
              // to avoid additional gas costs from reading from storage.
              if (
                block.timestamp < TRANSFER_RESTRICTION_LIFTED_NO_LATER_THAN &&
                block.timestamp < _transfersRestrictedBefore
              ) {
                // While transfers are restricted, a transfer is permitted if either the sender or the
                // recipient is on the allowlist.
                require(
                  _tokenTransferAllowlist[sender] || _tokenTransferAllowlist[recipient],
                  'NON_ALLOWLIST_TRANSFERS_DISABLED'
                );
              }
            }
          
            /**
             * @dev Writes a snapshot before any transfer operation, including: _transfer, _mint and _burn.
             *  - On _transfer, it writes snapshots for both 'from' and 'to'.
             *  - On _mint, only for `to`.
             *  - On _burn, only for `from`.
             *
             * @param  from    The sender.
             * @param  to      The recipient.
             * @param  amount  The amount being transfered.
             */
            function _beforeTokenTransfer(
              address from,
              address to,
              uint256 amount
            )
              internal
              override
            {
              address votingFromDelegatee = _getDelegatee(from, _votingDelegates);
              address votingToDelegatee = _getDelegatee(to, _votingDelegates);
          
              _moveDelegatesByType(
                votingFromDelegatee,
                votingToDelegatee,
                amount,
                DelegationType.VOTING_POWER
              );
          
              address propPowerFromDelegatee = _getDelegatee(from, _propositionPowerDelegates);
              address propPowerToDelegatee = _getDelegatee(to, _propositionPowerDelegates);
          
              _moveDelegatesByType(
                propPowerFromDelegatee,
                propPowerToDelegatee,
                amount,
                DelegationType.PROPOSITION_POWER
              );
            }
          
            function _getDelegationDataByType(
              DelegationType delegationType
            )
              internal
              override
              view
              returns (
                mapping(address => mapping(uint256 => Snapshot)) storage, // snapshots
                mapping(address => uint256) storage, // snapshots count
                mapping(address => address) storage // delegatees list
              )
            {
              if (delegationType == DelegationType.VOTING_POWER) {
                return (_votingSnapshots, _votingSnapshotsCounts, _votingDelegates);
              } else {
                return (
                  _propositionPowerSnapshots,
                  _propositionPowerSnapshotsCounts,
                  _propositionPowerDelegates
                );
              }
            }
          
            /**
             * @dev Delegates specific governance power from signer to `delegatee` using an EIP-712 signature.
             *
             * @param  delegatee       The address to delegate votes to.
             * @param  delegationType  The type of delegation (VOTING_POWER, PROPOSITION_POWER).
             * @param  nonce           The signer's nonce for EIP-712 signatures on this contract.
             * @param  expiry          Expiration timestamp for the signature.
             * @param  v               Signature param.
             * @param  r               Signature param.
             * @param  s               Signature param.
             */
            function delegateByTypeBySig(
              address delegatee,
              DelegationType delegationType,
              uint256 nonce,
              uint256 expiry,
              uint8 v,
              bytes32 r,
              bytes32 s
            )
              public
            {
              bytes32 structHash = keccak256(
                abi.encode(DELEGATE_BY_TYPE_TYPEHASH, delegatee, uint256(delegationType), nonce, expiry)
              );
              bytes32 digest = keccak256(abi.encodePacked('\x19\x01', DOMAIN_SEPARATOR, structHash));
              address signer = ecrecover(digest, v, r, s);
              require(
                signer != address(0),
                'INVALID_SIGNATURE'
              );
              require(
                nonce == _nonces[signer]++,
                'INVALID_NONCE'
              );
              require(
                block.timestamp <= expiry,
                'INVALID_EXPIRATION'
              );
              _delegateByType(signer, delegatee, delegationType);
            }
          
            /**
             * @dev Delegates both governance powers from signer to `delegatee` using an EIP-712 signature.
             *
             * @param  delegatee  The address to delegate votes to.
             * @param  nonce      The signer's nonce for EIP-712 signatures on this contract.
             * @param  expiry     Expiration timestamp for the signature.
             * @param  v          Signature param.
             * @param  r          Signature param.
             * @param  s          Signature param.
             */
            function delegateBySig(
              address delegatee,
              uint256 nonce,
              uint256 expiry,
              uint8 v,
              bytes32 r,
              bytes32 s
            )
              public
            {
              bytes32 structHash = keccak256(abi.encode(DELEGATE_TYPEHASH, delegatee, nonce, expiry));
              bytes32 digest = keccak256(abi.encodePacked('\x19\x01', DOMAIN_SEPARATOR, structHash));
              address signer = ecrecover(digest, v, r, s);
              require(
                signer != address(0),
                'INVALID_SIGNATURE'
              );
              require(
                nonce == _nonces[signer]++,
                'INVALID_NONCE'
              );
              require(
                block.timestamp <= expiry,
                'INVALID_EXPIRATION'
              );
              _delegateByType(signer, delegatee, DelegationType.VOTING_POWER);
              _delegateByType(signer, delegatee, DelegationType.PROPOSITION_POWER);
            }
          }

          File 4 of 6: StateSender
          /**
          Matic network contracts
          */
          
          pragma solidity ^0.5.2;
          
          
          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;
              }
          }
          
          library SafeMath {
              /**
               * @dev Multiplies two unsigned integers, reverts on 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-solidity/pull/522
                  if (a == 0) {
                      return 0;
                  }
          
                  uint256 c = a * b;
                  require(c / a == b);
          
                  return c;
              }
          
              /**
               * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
               */
              function div(uint256 a, uint256 b) internal pure returns (uint256) {
                  // Solidity only automatically asserts when dividing by 0
                  require(b > 0);
                  uint256 c = a / b;
                  // assert(a == b * c + a % b); // There is no case in which this doesn't hold
          
                  return c;
              }
          
              /**
               * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
               */
              function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                  require(b <= a);
                  uint256 c = a - b;
          
                  return c;
              }
          
              /**
               * @dev Adds two unsigned integers, reverts on overflow.
               */
              function add(uint256 a, uint256 b) internal pure returns (uint256) {
                  uint256 c = a + b;
                  require(c >= a);
          
                  return c;
              }
          
              /**
               * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
               * reverts when dividing by zero.
               */
              function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                  require(b != 0);
                  return a % b;
              }
          }
          
          contract StateSender is Ownable {
              using SafeMath for uint256;
          
              uint256 public counter;
              mapping(address => address) public registrations;
          
              event NewRegistration(
                  address indexed user,
                  address indexed sender,
                  address indexed receiver
              );
              event RegistrationUpdated(
                  address indexed user,
                  address indexed sender,
                  address indexed receiver
              );
              event StateSynced(
                  uint256 indexed id,
                  address indexed contractAddress,
                  bytes data
              );
          
              modifier onlyRegistered(address receiver) {
                  require(registrations[receiver] == msg.sender, "Invalid sender");
                  _;
              }
          
              function syncState(address receiver, bytes calldata data)
                  external
                  onlyRegistered(receiver)
              {
                  counter = counter.add(1);
                  emit StateSynced(counter, receiver, data);
              }
          
              // register new contract for state sync
              function register(address sender, address receiver) public {
                  require(
                      isOwner() || registrations[receiver] == msg.sender,
                      "StateSender.register: Not authorized to register"
                  );
                  registrations[receiver] = sender;
                  if (registrations[receiver] == address(0)) {
                      emit NewRegistration(msg.sender, sender, receiver);
                  } else {
                      emit RegistrationUpdated(msg.sender, sender, receiver);
                  }
              }
          }

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

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