ETH Price: $2,419.95 (-4.28%)
Gas: 0.26 Gwei

Transaction Decoder

Block:
15237290 at Jul-29-2022 12:18:32 PM +UTC
Transaction Fee:
0.00365328875402876 ETH $8.84
Gas Used:
143,990 Gas / 25.371822724 Gwei

Emitted Events:

196 ExitNFT.Transfer( from=[Sender] 0x689bcb53d875ad65cf6b8a27533d143e9b694b6e, to=0x00000000...000000000, tokenId=564561315956787272015678254936402361878273065014 )
197 MaticToken.Transfer( from=DepositManagerProxy, to=[Sender] 0x689bcb53d875ad65cf6b8a27533d143e9b694b6e, value=5609514869403656763594 )
198 WithdrawManagerProxy.Withdraw( exitId=564561315956787272015678254936402361878273065014, user=[Sender] 0x689bcb53d875ad65cf6b8a27533d143e9b694b6e, token=MaticToken, amount=5609514869403656763594 )

Account State Difference:

  Address   Before After State Difference Code
0x689bCb53...E9b694B6e
0.108209970298712094 Eth
Nonce: 28
0.104556681544683334 Eth
Nonce: 29
0.00365328875402876
0x749C1e75...1A84Aa1EC
0x7D1AfA7B...8AaCfeBB0
0xDF741564...6F9fABAca
(Polygon (Matic): Exit NFT)
(Ethermine)
525.095670296182204958 Eth525.095816189608333408 Eth0.00014589342612845

Execution Trace

WithdrawManagerProxy.0f6795f2( )
  • WithdrawManager.processExits( _token=0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0 )
    • PriorityQueue.STATICCALL( )
    • PriorityQueue.STATICCALL( )
    • PriorityQueue.CALL( )
    • ExitNFT.exists( tokenId=564561315956787272015678254936402361878273065014 ) => ( True )
    • ExitNFT.ownerOf( tokenId=564561315956787272015678254936402361878273065014 ) => ( 0x689bCb53d875AD65CF6b8a27533d143E9b694B6e )
    • ExitNFT.burn( _tokenId=564561315956787272015678254936402361878273065014 )
      • Registry.STATICCALL( )
      • ERC20PredicateBurnOnly.onFinalizeExit( data=0x00000000000000000000000062E3CE12000000000000000003BA61FA000000360000000000000000000000007D1AFA7B718FB893DB30A3ABC0CFC608AACFEBB0000000000000000000000000689BCB53D875AD65CF6B8A27533D143E9B694B6E00000000000000000000000000000000000000000000013017A8353D34B1B8CA )
        • DepositManagerProxy.49f4cc17( )
          • DepositManager.transferAssets( _token=0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0, _user=0x689bCb53d875AD65CF6b8a27533d143E9b694B6e, _amountOrNFTId=5609514869403656763594 )
            • Registry.predicates( 0x158d5fa3Ef8e4dDA8a5367deCF76b94E7efFCe95 ) => ( _type=1 )
            • Registry.STATICCALL( )
            • Registry.isERC721( 0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0 ) => ( False )
            • MaticToken.transfer( to=0x689bCb53d875AD65CF6b8a27533d143E9b694B6e, value=5609514869403656763594 ) => ( True )
            • PriorityQueue.STATICCALL( )
              File 1 of 9: WithdrawManagerProxy
              // File: contracts/common/governance/IGovernance.sol
              
              pragma solidity ^0.5.2;
              
              
              interface IGovernance {
                  function update(address target, bytes calldata data) external;
              }
              
              // File: contracts/common/governance/Governable.sol
              
              pragma solidity ^0.5.2;
              
              
              contract Governable {
                  IGovernance public governance;
              
                  constructor(address _governance) public {
                      governance = IGovernance(_governance);
                  }
              
                  modifier onlyGovernance() {
                      require(msg.sender == address(governance), "Only governance contract is authorized");
                      _;
                  }
              }
              
              // File: contracts/root/withdrawManager/IWithdrawManager.sol
              
              pragma solidity ^0.5.2;
              
              
              contract IWithdrawManager {
                  function createExitQueue(address token) external;
              
                  function verifyInclusion(
                      bytes calldata data,
                      uint8 offset,
                      bool verifyTxInclusion
                  ) external view returns (uint256 age);
              
                  function addExitToQueue(
                      address exitor,
                      address childToken,
                      address rootToken,
                      uint256 exitAmountOrTokenId,
                      bytes32 txHash,
                      bool isRegularExit,
                      uint256 priority
                  ) external;
              
                  function addInput(
                      uint256 exitId,
                      uint256 age,
                      address utxoOwner,
                      address token
                  ) external;
              
                  function challengeExit(
                      uint256 exitId,
                      uint256 inputId,
                      bytes calldata challengeData,
                      address adjudicatorPredicate
                  ) external;
              }
              
              // File: contracts/common/Registry.sol
              
              pragma solidity ^0.5.2;
              
              
              contract Registry is Governable {
                  // @todo hardcode constants
                  bytes32 private constant WETH_TOKEN = keccak256("wethToken");
                  bytes32 private constant DEPOSIT_MANAGER = keccak256("depositManager");
                  bytes32 private constant STAKE_MANAGER = keccak256("stakeManager");
                  bytes32 private constant VALIDATOR_SHARE = keccak256("validatorShare");
                  bytes32 private constant WITHDRAW_MANAGER = keccak256("withdrawManager");
                  bytes32 private constant CHILD_CHAIN = keccak256("childChain");
                  bytes32 private constant STATE_SENDER = keccak256("stateSender");
                  bytes32 private constant SLASHING_MANAGER = keccak256("slashingManager");
              
                  address public erc20Predicate;
                  address public erc721Predicate;
              
                  mapping(bytes32 => address) public contractMap;
                  mapping(address => address) public rootToChildToken;
                  mapping(address => address) public childToRootToken;
                  mapping(address => bool) public proofValidatorContracts;
                  mapping(address => bool) public isERC721;
              
                  enum Type {Invalid, ERC20, ERC721, Custom}
                  struct Predicate {
                      Type _type;
                  }
                  mapping(address => Predicate) public predicates;
              
                  event TokenMapped(address indexed rootToken, address indexed childToken);
                  event ProofValidatorAdded(address indexed validator, address indexed from);
                  event ProofValidatorRemoved(address indexed validator, address indexed from);
                  event PredicateAdded(address indexed predicate, address indexed from);
                  event PredicateRemoved(address indexed predicate, address indexed from);
                  event ContractMapUpdated(bytes32 indexed key, address indexed previousContract, address indexed newContract);
              
                  constructor(address _governance) public Governable(_governance) {}
              
                  function updateContractMap(bytes32 _key, address _address) external onlyGovernance {
                      emit ContractMapUpdated(_key, contractMap[_key], _address);
                      contractMap[_key] = _address;
                  }
              
                  /**
                   * @dev Map root token to child token
                   * @param _rootToken Token address on the root chain
                   * @param _childToken Token address on the child chain
                   * @param _isERC721 Is the token being mapped ERC721
                   */
                  function mapToken(
                      address _rootToken,
                      address _childToken,
                      bool _isERC721
                  ) external onlyGovernance {
                      require(_rootToken != address(0x0) && _childToken != address(0x0), "INVALID_TOKEN_ADDRESS");
                      rootToChildToken[_rootToken] = _childToken;
                      childToRootToken[_childToken] = _rootToken;
                      isERC721[_rootToken] = _isERC721;
                      IWithdrawManager(contractMap[WITHDRAW_MANAGER]).createExitQueue(_rootToken);
                      emit TokenMapped(_rootToken, _childToken);
                  }
              
                  function addErc20Predicate(address predicate) public onlyGovernance {
                      require(predicate != address(0x0), "Can not add null address as predicate");
                      erc20Predicate = predicate;
                      addPredicate(predicate, Type.ERC20);
                  }
              
                  function addErc721Predicate(address predicate) public onlyGovernance {
                      erc721Predicate = predicate;
                      addPredicate(predicate, Type.ERC721);
                  }
              
                  function addPredicate(address predicate, Type _type) public onlyGovernance {
                      require(predicates[predicate]._type == Type.Invalid, "Predicate already added");
                      predicates[predicate]._type = _type;
                      emit PredicateAdded(predicate, msg.sender);
                  }
              
                  function removePredicate(address predicate) public onlyGovernance {
                      require(predicates[predicate]._type != Type.Invalid, "Predicate does not exist");
                      delete predicates[predicate];
                      emit PredicateRemoved(predicate, msg.sender);
                  }
              
                  function getValidatorShareAddress() public view returns (address) {
                      return contractMap[VALIDATOR_SHARE];
                  }
              
                  function getWethTokenAddress() public view returns (address) {
                      return contractMap[WETH_TOKEN];
                  }
              
                  function getDepositManagerAddress() public view returns (address) {
                      return contractMap[DEPOSIT_MANAGER];
                  }
              
                  function getStakeManagerAddress() public view returns (address) {
                      return contractMap[STAKE_MANAGER];
                  }
              
                  function getSlashingManagerAddress() public view returns (address) {
                      return contractMap[SLASHING_MANAGER];
                  }
              
                  function getWithdrawManagerAddress() public view returns (address) {
                      return contractMap[WITHDRAW_MANAGER];
                  }
              
                  function getChildChainAndStateSender() public view returns (address, address) {
                      return (contractMap[CHILD_CHAIN], contractMap[STATE_SENDER]);
                  }
              
                  function isTokenMapped(address _token) public view returns (bool) {
                      return rootToChildToken[_token] != address(0x0);
                  }
              
                  function isTokenMappedAndIsErc721(address _token) public view returns (bool) {
                      require(isTokenMapped(_token), "TOKEN_NOT_MAPPED");
                      return isERC721[_token];
                  }
              
                  function isTokenMappedAndGetPredicate(address _token) public view returns (address) {
                      if (isTokenMappedAndIsErc721(_token)) {
                          return erc721Predicate;
                      }
                      return erc20Predicate;
                  }
              
                  function isChildTokenErc721(address childToken) public view returns (bool) {
                      address rootToken = childToRootToken[childToken];
                      require(rootToken != address(0x0), "Child token is not mapped");
                      return isERC721[rootToken];
                  }
              }
              
              // File: openzeppelin-solidity/contracts/ownership/Ownable.sol
              
              pragma solidity ^0.5.2;
              
              
              /**
               * @title Ownable
               * @dev The Ownable contract has an owner address, and provides basic authorization control
               * functions, this simplifies the implementation of "user permissions".
               */
              contract Ownable {
                  address private _owner;
              
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
              
                  /**
                   * @dev The Ownable constructor sets the original `owner` of the contract to the sender
                   * account.
                   */
                  constructor() internal {
                      _owner = msg.sender;
                      emit OwnershipTransferred(address(0), _owner);
                  }
              
                  /**
                   * @return the address of the owner.
                   */
                  function owner() public view returns (address) {
                      return _owner;
                  }
              
                  /**
                   * @dev Throws if called by any account other than the owner.
                   */
                  modifier onlyOwner() {
                      require(isOwner());
                      _;
                  }
              
                  /**
                   * @return true if `msg.sender` is the owner of the contract.
                   */
                  function isOwner() public view returns (bool) {
                      return msg.sender == _owner;
                  }
              
                  /**
                   * @dev Allows the current owner to relinquish control of the contract.
                   * It will not be possible to call the functions with the `onlyOwner`
                   * modifier anymore.
                   * @notice Renouncing ownership will leave the contract without an owner,
                   * thereby removing any functionality that is only available to the owner.
                   */
                  function renounceOwnership() public onlyOwner {
                      emit OwnershipTransferred(_owner, address(0));
                      _owner = address(0);
                  }
              
                  /**
                   * @dev Allows the current owner to transfer control of the contract to a newOwner.
                   * @param newOwner The address to transfer ownership to.
                   */
                  function transferOwnership(address newOwner) public onlyOwner {
                      _transferOwnership(newOwner);
                  }
              
                  /**
                   * @dev Transfers control of the contract to a newOwner.
                   * @param newOwner The address to transfer ownership to.
                   */
                  function _transferOwnership(address newOwner) internal {
                      require(newOwner != address(0));
                      emit OwnershipTransferred(_owner, newOwner);
                      _owner = newOwner;
                  }
              }
              
              // File: contracts/common/misc/ProxyStorage.sol
              
              pragma solidity ^0.5.2;
              
              
              contract ProxyStorage is Ownable {
                  address internal proxyTo;
              }
              
              // File: contracts/common/misc/ERCProxy.sol
              
              /*
               * SPDX-License-Identitifer:    MIT
               */
              
              pragma solidity ^0.5.2;
              
              
              // See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-897.md
              
              interface ERCProxy {
                  function proxyType() external pure returns (uint256 proxyTypeId);
              
                  function implementation() external view returns (address codeAddr);
              }
              
              // File: contracts/common/misc/DelegateProxy.sol
              
              pragma solidity ^0.5.2;
              
              
              contract DelegateProxy is ERCProxy {
                  function proxyType() external pure returns (uint256 proxyTypeId) {
                      // Upgradeable proxy
                      proxyTypeId = 2;
                  }
              
                  function implementation() external view returns (address);
              
                  function delegatedFwd(address _dst, bytes memory _calldata) internal {
                      // solium-disable-next-line security/no-inline-assembly
                      assembly {
                          let result := delegatecall(sub(gas, 10000), _dst, add(_calldata, 0x20), mload(_calldata), 0, 0)
                          let size := returndatasize
              
                          let ptr := mload(0x40)
                          returndatacopy(ptr, 0, size)
              
                          // revert instead of invalid() bc if the underlying call failed with invalid() it already wasted gas.
                          // if the call returned error data, forward it
                          switch result
                              case 0 {
                                  revert(ptr, size)
                              }
                              default {
                                  return(ptr, size)
                              }
                      }
                  }
              }
              
              // File: contracts/common/misc/Proxy.sol
              
              pragma solidity ^0.5.2;
              
              
              contract Proxy is ProxyStorage, DelegateProxy {
                  event ProxyUpdated(address indexed _new, address indexed _old);
                  event OwnerUpdate(address _prevOwner, address _newOwner);
              
                  constructor(address _proxyTo) public {
                      updateImplementation(_proxyTo);
                  }
              
                  function() external payable {
                      // require(currentContract != 0, "If app code has not been set yet, do not call");
                      // Todo: filter out some calls or handle in the end fallback
                      delegatedFwd(proxyTo, msg.data);
                  }
              
                  function implementation() external view returns (address) {
                      return proxyTo;
                  }
              
                  function updateImplementation(address _newProxyTo) public onlyOwner {
                      require(_newProxyTo != address(0x0), "INVALID_PROXY_ADDRESS");
                      require(isContract(_newProxyTo), "DESTINATION_ADDRESS_IS_NOT_A_CONTRACT");
                      emit ProxyUpdated(_newProxyTo, proxyTo);
                      proxyTo = _newProxyTo;
                  }
              
                  function isContract(address _target) internal view returns (bool) {
                      if (_target == address(0)) {
                          return false;
                      }
              
                      uint256 size;
                      assembly {
                          size := extcodesize(_target)
                      }
                      return size > 0;
                  }
              }
              
              // File: solidity-rlp/contracts/RLPReader.sol
              
              /*
               * @author Hamdi Allam [email protected]
               * Please reach out with any questions or concerns
               */
              pragma solidity ^0.5.0;
              
              
              library RLPReader {
                  uint8 constant STRING_SHORT_START = 0x80;
                  uint8 constant STRING_LONG_START = 0xb8;
                  uint8 constant LIST_SHORT_START = 0xc0;
                  uint8 constant LIST_LONG_START = 0xf8;
                  uint8 constant WORD_SIZE = 32;
              
                  struct RLPItem {
                      uint256 len;
                      uint256 memPtr;
                  }
              
                  struct Iterator {
                      RLPItem item; // Item that's being iterated over.
                      uint256 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));
              
                      uint256 ptr = self.nextPtr;
                      uint256 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) {
                      uint256 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));
              
                      uint256 ptr = self.memPtr + _payloadOffset(self.memPtr);
                      return Iterator(self, ptr);
                  }
              
                  /*
                   * @param item RLP encoded bytes
                   */
                  function rlpLen(RLPItem memory item) internal pure returns (uint256) {
                      return item.len;
                  }
              
                  /*
                   * @param item RLP encoded bytes
                   */
                  function payloadLen(RLPItem memory item) internal pure returns (uint256) {
                      return item.len - _payloadOffset(item.memPtr);
                  }
              
                  /*
                   * @param item RLP encoded list in bytes
                   */
                  function toList(RLPItem memory item) internal pure returns (RLPItem[] memory) {
                      require(isList(item));
              
                      uint256 items = numItems(item);
                      RLPItem[] memory result = new RLPItem[](items);
              
                      uint256 memPtr = item.memPtr + _payloadOffset(item.memPtr);
                      uint256 dataLen;
                      for (uint256 i = 0; i < items; i++) {
                          dataLen = _itemLength(memPtr);
                          result[i] = RLPItem(dataLen, memPtr);
                          memPtr = memPtr + dataLen;
                      }
              
                      return result;
                  }
              
                  // @return indicator whether encoded payload is a list. negate this function call for isData.
                  function isList(RLPItem memory item) internal pure returns (bool) {
                      if (item.len == 0) return false;
              
                      uint8 byte0;
                      uint256 memPtr = item.memPtr;
                      assembly {
                          byte0 := byte(0, mload(memPtr))
                      }
              
                      if (byte0 < LIST_SHORT_START) return false;
                      return true;
                  }
              
                  /** RLPItem conversions into data types **/
              
                  // @returns raw rlp encoding in bytes
                  function toRlpBytes(RLPItem memory item) internal pure returns (bytes memory) {
                      bytes memory result = new bytes(item.len);
                      if (result.length == 0) return result;
              
                      uint256 ptr;
                      assembly {
                          ptr := add(0x20, result)
                      }
              
                      copy(item.memPtr, ptr, item.len);
                      return result;
                  }
              
                  // any non-zero byte is considered true
                  function toBoolean(RLPItem memory item) internal pure returns (bool) {
                      require(item.len == 1);
                      uint256 result;
                      uint256 memPtr = item.memPtr;
                      assembly {
                          result := byte(0, mload(memPtr))
                      }
              
                      return result == 0 ? false : 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 (uint256) {
                      require(item.len > 0 && item.len <= 33);
              
                      uint256 offset = _payloadOffset(item.memPtr);
                      uint256 len = item.len - offset;
              
                      uint256 result;
                      uint256 memPtr = item.memPtr + offset;
                      assembly {
                          result := mload(memPtr)
              
                          // shfit to the correct location if neccesary
                          if lt(len, 32) {
                              result := div(result, exp(256, sub(32, len)))
                          }
                      }
              
                      return result;
                  }
              
                  // enforces 32 byte length
                  function toUintStrict(RLPItem memory item) internal pure returns (uint256) {
                      // one byte prefix
                      require(item.len == 33);
              
                      uint256 result;
                      uint256 memPtr = item.memPtr + 1;
                      assembly {
                          result := mload(memPtr)
                      }
              
                      return result;
                  }
              
                  function toBytes(RLPItem memory item) internal pure returns (bytes memory) {
                      require(item.len > 0);
              
                      uint256 offset = _payloadOffset(item.memPtr);
                      uint256 len = item.len - offset; // data length
                      bytes memory result = new bytes(len);
              
                      uint256 destPtr;
                      assembly {
                          destPtr := add(0x20, result)
                      }
              
                      copy(item.memPtr + offset, destPtr, len);
                      return result;
                  }
              
                  /*
                   * Private Helpers
                   */
              
                  // @return number of payload items inside an encoded list.
                  function numItems(RLPItem memory item) private pure returns (uint256) {
                      if (item.len == 0) return 0;
              
                      uint256 count = 0;
                      uint256 currPtr = item.memPtr + _payloadOffset(item.memPtr);
                      uint256 endPtr = item.memPtr + item.len;
                      while (currPtr < endPtr) {
                          currPtr = currPtr + _itemLength(currPtr); // skip over an item
                          count++;
                      }
              
                      return count;
                  }
              
                  // @return entire rlp item byte length
                  function _itemLength(uint256 memPtr) private pure returns (uint256) {
                      uint256 itemLen;
                      uint256 byte0;
                      assembly {
                          byte0 := byte(0, mload(memPtr))
                      }
              
                      if (byte0 < STRING_SHORT_START) itemLen = 1;
                      else if (byte0 < STRING_LONG_START) itemLen = byte0 - STRING_SHORT_START + 1;
                      else if (byte0 < LIST_SHORT_START) {
                          assembly {
                              let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is
                              memPtr := add(memPtr, 1) // skip over the first byte
              
                              /* 32 byte word size */
                              let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to get the len
                              itemLen := add(dataLen, add(byteLen, 1))
                          }
                      } else if (byte0 < LIST_LONG_START) {
                          itemLen = byte0 - LIST_SHORT_START + 1;
                      } else {
                          assembly {
                              let byteLen := sub(byte0, 0xf7)
                              memPtr := add(memPtr, 1)
              
                              let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to the correct length
                              itemLen := add(dataLen, add(byteLen, 1))
                          }
                      }
              
                      return itemLen;
                  }
              
                  // @return number of bytes until the data
                  function _payloadOffset(uint256 memPtr) private pure returns (uint256) {
                      uint256 byte0;
                      assembly {
                          byte0 := byte(0, mload(memPtr))
                      }
              
                      if (byte0 < STRING_SHORT_START) return 0;
                      else if (byte0 < STRING_LONG_START || (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START)) return 1;
                      else if (byte0 < LIST_SHORT_START)
                          // being explicit
                          return byte0 - (STRING_LONG_START - 1) + 1;
                      else return byte0 - (LIST_LONG_START - 1) + 1;
                  }
              
                  /*
                   * @param src Pointer to source
                   * @param dest Pointer to destination
                   * @param len Amount of memory to copy from the source
                   */
                  function copy(
                      uint256 src,
                      uint256 dest,
                      uint256 len
                  ) private pure {
                      if (len == 0) return;
              
                      // copy as many word sizes as possible
                      for (; len >= WORD_SIZE; len -= WORD_SIZE) {
                          assembly {
                              mstore(dest, mload(src))
                          }
              
                          src += WORD_SIZE;
                          dest += WORD_SIZE;
                      }
              
                      // left over bytes. Mask is used to remove unwanted bytes from the word
                      uint256 mask = 256**(WORD_SIZE - len) - 1;
                      assembly {
                          let srcpart := and(mload(src), not(mask)) // zero out src
                          let destpart := and(mload(dest), mask) // retrieve the bytes
                          mstore(dest, or(destpart, srcpart))
                      }
                  }
              }
              
              // File: openzeppelin-solidity/contracts/math/SafeMath.sol
              
              pragma solidity ^0.5.2;
              
              
              /**
               * @title SafeMath
               * @dev Unsigned math operations with safety checks that revert on error
               */
              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;
                  }
              }
              
              // File: contracts/common/mixin/ChainIdMixin.sol
              
              pragma solidity ^0.5.2;
              
              
              contract ChainIdMixin {
                  bytes public constant networkId = hex"89";
                  uint256 public constant CHAINID = 137;
              }
              
              // File: contracts/root/RootChainStorage.sol
              
              pragma solidity ^0.5.2;
              
              
              contract RootChainHeader {
                  event NewHeaderBlock(
                      address indexed proposer,
                      uint256 indexed headerBlockId,
                      uint256 indexed reward,
                      uint256 start,
                      uint256 end,
                      bytes32 root
                  );
                  // housekeeping event
                  event ResetHeaderBlock(address indexed proposer, uint256 indexed headerBlockId);
                  struct HeaderBlock {
                      bytes32 root;
                      uint256 start;
                      uint256 end;
                      uint256 createdAt;
                      address proposer;
                  }
              }
              
              
              contract RootChainStorage is ProxyStorage, RootChainHeader, ChainIdMixin {
                  bytes32 public heimdallId;
                  uint8 public constant VOTE_TYPE = 2;
              
                  uint16 internal constant MAX_DEPOSITS = 10000;
                  uint256 public _nextHeaderBlock = MAX_DEPOSITS;
                  uint256 internal _blockDepositId = 1;
                  mapping(uint256 => HeaderBlock) public headerBlocks;
                  Registry internal registry;
              }
              
              // File: contracts/staking/stakeManager/IStakeManager.sol
              
              pragma solidity ^0.5.2;
              
              
              contract IStakeManager {
                  // validator replacement
                  function startAuction(uint256 validatorId, uint256 amount) external;
              
                  function confirmAuctionBid(
                      uint256 validatorId,
                      uint256 heimdallFee,
                      bool acceptDelegation,
                      bytes calldata signerPubkey
                  ) external;
              
                  function transferFunds(
                      uint256 validatorId,
                      uint256 amount,
                      address delegator
                  ) external returns (bool);
              
                  function delegationDeposit(
                      uint256 validatorId,
                      uint256 amount,
                      address delegator
                  ) external returns (bool);
              
                  function stake(
                      uint256 amount,
                      uint256 heimdallFee,
                      bool acceptDelegation,
                      bytes calldata signerPubkey
                  ) external;
              
                  function unstake(uint256 validatorId) external;
              
                  function totalStakedFor(address addr) external view returns (uint256);
              
                  function supportsHistory() external pure returns (bool);
              
                  function stakeFor(
                      address user,
                      uint256 amount,
                      uint256 heimdallFee,
                      bool acceptDelegation,
                      bytes memory signerPubkey
                  ) public;
              
                  function checkSignatures(
                      uint256 blockInterval,
                      bytes32 voteHash,
                      bytes32 stateRoot,
                      address proposer,
                      bytes memory sigs
                  ) public returns (uint256);
              
                  function updateValidatorState(uint256 validatorId, int256 amount) public;
              
                  function ownerOf(uint256 tokenId) public view returns (address);
              
                  function slash(bytes memory slashingInfoList) public returns (uint256);
              
                  function validatorStake(uint256 validatorId) public view returns (uint256);
              
                  function epoch() public view returns (uint256);
              
                  function withdrawalDelay() public view returns (uint256);
              }
              
              // File: contracts/root/IRootChain.sol
              
              pragma solidity ^0.5.2;
              
              
              interface IRootChain {
                  function slash() external;
              
                  function submitHeaderBlock(bytes calldata data, bytes calldata sigs) external;
              
                  function getLastChildBlock() external view returns (uint256);
              
                  function currentHeaderBlock() external view returns (uint256);
              }
              
              // File: contracts/root/RootChain.sol
              
              pragma solidity ^0.5.2;
              
              
              contract RootChain is RootChainStorage, IRootChain {
                  using SafeMath for uint256;
                  using RLPReader for bytes;
                  using RLPReader for RLPReader.RLPItem;
              
                  modifier onlyDepositManager() {
                      require(msg.sender == registry.getDepositManagerAddress(), "UNAUTHORIZED_DEPOSIT_MANAGER_ONLY");
                      _;
                  }
              
                  function submitHeaderBlock(bytes calldata data, bytes calldata sigs) external {
                      (address proposer, uint256 start, uint256 end, bytes32 rootHash, bytes32 accountHash, uint256 _borChainID) = abi
                          .decode(data, (address, uint256, uint256, bytes32, bytes32, uint256));
                      require(CHAINID == _borChainID, "Invalid bor chain id");
              
                      require(_buildHeaderBlock(proposer, start, end, rootHash), "INCORRECT_HEADER_DATA");
              
                      // check if it is better to keep it in local storage instead
                      IStakeManager stakeManager = IStakeManager(registry.getStakeManagerAddress());
                      uint256 _reward = stakeManager.checkSignatures(
                          end.sub(start).add(1),
                          /**  
                              prefix 01 to data 
                              01 represents positive vote on data and 00 is negative vote
                              malicious validator can try to send 2/3 on negative vote so 01 is appended
                           */
                          keccak256(abi.encodePacked(bytes(hex"01"), data)),
                          accountHash,
                          proposer,
                          sigs
                      );
              
                      require(_reward != 0, "Invalid checkpoint");
                      emit NewHeaderBlock(proposer, _nextHeaderBlock, _reward, start, end, rootHash);
                      _nextHeaderBlock = _nextHeaderBlock.add(MAX_DEPOSITS);
                      _blockDepositId = 1;
                  }
              
                  function updateDepositId(uint256 numDeposits) external onlyDepositManager returns (uint256 depositId) {
                      depositId = currentHeaderBlock().add(_blockDepositId);
                      // deposit ids will be (_blockDepositId, _blockDepositId + 1, .... _blockDepositId + numDeposits - 1)
                      _blockDepositId = _blockDepositId.add(numDeposits);
                      require(
                          // Since _blockDepositId is initialized to 1; only (MAX_DEPOSITS - 1) deposits per header block are allowed
                          _blockDepositId <= MAX_DEPOSITS,
                          "TOO_MANY_DEPOSITS"
                      );
                  }
              
                  function getLastChildBlock() external view returns (uint256) {
                      return headerBlocks[currentHeaderBlock()].end;
                  }
              
                  function slash() external {
                      //TODO: future implementation
                  }
              
                  function currentHeaderBlock() public view returns (uint256) {
                      return _nextHeaderBlock.sub(MAX_DEPOSITS);
                  }
              
                  function _buildHeaderBlock(
                      address proposer,
                      uint256 start,
                      uint256 end,
                      bytes32 rootHash
                  ) private returns (bool) {
                      uint256 nextChildBlock;
                      /*
                  The ID of the 1st header block is MAX_DEPOSITS.
                  if _nextHeaderBlock == MAX_DEPOSITS, then the first header block is yet to be submitted, hence nextChildBlock = 0
                  */
                      if (_nextHeaderBlock > MAX_DEPOSITS) {
                          nextChildBlock = headerBlocks[currentHeaderBlock()].end + 1;
                      }
                      if (nextChildBlock != start) {
                          return false;
                      }
              
                      HeaderBlock memory headerBlock = HeaderBlock({
                          root: rootHash,
                          start: nextChildBlock,
                          end: end,
                          createdAt: now,
                          proposer: proposer
                      });
              
                      headerBlocks[_nextHeaderBlock] = headerBlock;
                      return true;
                  }
              
                  // Housekeeping function. @todo remove later
                  function setNextHeaderBlock(uint256 _value) public onlyOwner {
                      require(_value % MAX_DEPOSITS == 0, "Invalid value");
                      for (uint256 i = _value; i < _nextHeaderBlock; i += MAX_DEPOSITS) {
                          delete headerBlocks[i];
                      }
                      _nextHeaderBlock = _value;
                      _blockDepositId = 1;
                      emit ResetHeaderBlock(msg.sender, _nextHeaderBlock);
                  }
              
                  // Housekeeping function. @todo remove later
                  function setHeimdallId(string memory _heimdallId) public onlyOwner {
                      heimdallId = keccak256(abi.encodePacked(_heimdallId));
                  }
              }
              
              // File: openzeppelin-solidity/contracts/introspection/IERC165.sol
              
              pragma solidity ^0.5.2;
              
              
              /**
               * @title IERC165
               * @dev https://eips.ethereum.org/EIPS/eip-165
               */
              interface IERC165 {
                  /**
                   * @notice Query if a contract implements an interface
                   * @param interfaceId The interface identifier, as specified in ERC-165
                   * @dev Interface identification is specified in ERC-165. This function
                   * uses less than 30,000 gas.
                   */
                  function supportsInterface(bytes4 interfaceId) external view returns (bool);
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC721/IERC721.sol
              
              pragma solidity ^0.5.2;
              
              
              /**
               * @title ERC721 Non-Fungible Token Standard basic interface
               * @dev see https://eips.ethereum.org/EIPS/eip-721
               */
              contract IERC721 is IERC165 {
                  event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
                  event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
                  event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
              
                  function balanceOf(address owner) public view returns (uint256 balance);
              
                  function ownerOf(uint256 tokenId) public view returns (address owner);
              
                  function approve(address to, uint256 tokenId) public;
              
                  function getApproved(uint256 tokenId) public view returns (address operator);
              
                  function setApprovalForAll(address operator, bool _approved) public;
              
                  function isApprovedForAll(address owner, address operator) public view returns (bool);
              
                  function transferFrom(
                      address from,
                      address to,
                      uint256 tokenId
                  ) public;
              
                  function safeTransferFrom(
                      address from,
                      address to,
                      uint256 tokenId
                  ) public;
              
                  function safeTransferFrom(
                      address from,
                      address to,
                      uint256 tokenId,
                      bytes memory data
                  ) public;
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC721/IERC721Receiver.sol
              
              pragma solidity ^0.5.2;
              
              
              /**
               * @title ERC721 token receiver interface
               * @dev Interface for any contract that wants to support safeTransfers
               * from ERC721 asset contracts.
               */
              contract IERC721Receiver {
                  /**
                   * @notice Handle the receipt of an NFT
                   * @dev The ERC721 smart contract calls this function on the recipient
                   * after a `safeTransfer`. This function MUST return the function selector,
                   * otherwise the caller will revert the transaction. The selector to be
                   * returned can be obtained as `this.onERC721Received.selector`. This
                   * function MAY throw to revert and reject the transfer.
                   * Note: the ERC721 contract address is always the message sender.
                   * @param operator The address which called `safeTransferFrom` function
                   * @param from The address which previously owned the token
                   * @param tokenId The NFT identifier which is being transferred
                   * @param data Additional data with no specified format
                   * @return bytes4 `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
                   */
                  function onERC721Received(
                      address operator,
                      address from,
                      uint256 tokenId,
                      bytes memory data
                  ) public returns (bytes4);
              }
              
              // File: openzeppelin-solidity/contracts/utils/Address.sol
              
              pragma solidity ^0.5.2;
              
              
              /**
               * Utility library of inline functions on addresses
               */
              library Address {
                  /**
                   * Returns whether the target address is a contract
                   * @dev This function will return false if invoked during the constructor of a contract,
                   * as the code is not actually created until after the constructor finishes.
                   * @param account address of the account to check
                   * @return whether the target address is a contract
                   */
                  function isContract(address account) internal view returns (bool) {
                      uint256 size;
                      // XXX Currently there is no better way to check if there is a contract in an address
                      // than to check the size of the code at that address.
                      // See https://ethereum.stackexchange.com/a/14016/36603
                      // for more details about how this works.
                      // TODO Check this again before the Serenity release, because all addresses will be
                      // contracts then.
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          size := extcodesize(account)
                      }
                      return size > 0;
                  }
              }
              
              // File: openzeppelin-solidity/contracts/drafts/Counters.sol
              
              pragma solidity ^0.5.2;
              
              
              /**
               * @title Counters
               * @author Matt Condon (@shrugs)
               * @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number
               * of elements in a mapping, issuing ERC721 ids, or counting request ids
               *
               * Include with `using Counters for Counters.Counter;`
               * Since it is not possible to overflow a 256 bit integer with increments of one, `increment` can skip the SafeMath
               * overflow check, thereby saving gas. This does assume however correct usage, in that the underlying `_value` is never
               * directly accessed.
               */
              library Counters {
                  using SafeMath for uint256;
              
                  struct Counter {
                      // This variable should never be directly accessed by users of the library: interactions must be restricted to
                      // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
                      // this feature: see https://github.com/ethereum/solidity/issues/4637
                      uint256 _value; // default: 0
                  }
              
                  function current(Counter storage counter) internal view returns (uint256) {
                      return counter._value;
                  }
              
                  function increment(Counter storage counter) internal {
                      counter._value += 1;
                  }
              
                  function decrement(Counter storage counter) internal {
                      counter._value = counter._value.sub(1);
                  }
              }
              
              // File: openzeppelin-solidity/contracts/introspection/ERC165.sol
              
              pragma solidity ^0.5.2;
              
              
              /**
               * @title ERC165
               * @author Matt Condon (@shrugs)
               * @dev Implements ERC165 using a lookup table.
               */
              contract ERC165 is IERC165 {
                  bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
                  /*
                   * 0x01ffc9a7 ===
                   *     bytes4(keccak256('supportsInterface(bytes4)'))
                   */
              
                  /**
                   * @dev a mapping of interface id to whether or not it's supported
                   */
                  mapping(bytes4 => bool) private _supportedInterfaces;
              
                  /**
                   * @dev A contract implementing SupportsInterfaceWithLookup
                   * implement ERC165 itself
                   */
                  constructor() internal {
                      _registerInterface(_INTERFACE_ID_ERC165);
                  }
              
                  /**
                   * @dev implement supportsInterface(bytes4) using a lookup table
                   */
                  function supportsInterface(bytes4 interfaceId) external view returns (bool) {
                      return _supportedInterfaces[interfaceId];
                  }
              
                  /**
                   * @dev internal method for registering an interface
                   */
                  function _registerInterface(bytes4 interfaceId) internal {
                      require(interfaceId != 0xffffffff);
                      _supportedInterfaces[interfaceId] = true;
                  }
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC721/ERC721.sol
              
              pragma solidity ^0.5.2;
              
              
              /**
               * @title ERC721 Non-Fungible Token Standard basic implementation
               * @dev see https://eips.ethereum.org/EIPS/eip-721
               */
              contract ERC721 is ERC165, IERC721 {
                  using SafeMath for uint256;
                  using Address for address;
                  using Counters for Counters.Counter;
              
                  // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
                  // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector`
                  bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
              
                  // Mapping from token ID to owner
                  mapping(uint256 => address) private _tokenOwner;
              
                  // Mapping from token ID to approved address
                  mapping(uint256 => address) private _tokenApprovals;
              
                  // Mapping from owner to number of owned token
                  mapping(address => Counters.Counter) private _ownedTokensCount;
              
                  // Mapping from owner to operator approvals
                  mapping(address => mapping(address => bool)) private _operatorApprovals;
              
                  bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
              
                  /*
                   * 0x80ac58cd ===
                   *     bytes4(keccak256('balanceOf(address)')) ^
                   *     bytes4(keccak256('ownerOf(uint256)')) ^
                   *     bytes4(keccak256('approve(address,uint256)')) ^
                   *     bytes4(keccak256('getApproved(uint256)')) ^
                   *     bytes4(keccak256('setApprovalForAll(address,bool)')) ^
                   *     bytes4(keccak256('isApprovedForAll(address,address)')) ^
                   *     bytes4(keccak256('transferFrom(address,address,uint256)')) ^
                   *     bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^
                   *     bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)'))
                   */
              
                  constructor() public {
                      // register the supported interfaces to conform to ERC721 via ERC165
                      _registerInterface(_INTERFACE_ID_ERC721);
                  }
              
                  /**
                   * @dev Gets the balance of the specified address
                   * @param owner address to query the balance of
                   * @return uint256 representing the amount owned by the passed address
                   */
                  function balanceOf(address owner) public view returns (uint256) {
                      require(owner != address(0));
                      return _ownedTokensCount[owner].current();
                  }
              
                  /**
                   * @dev Gets the owner of the specified token ID
                   * @param tokenId uint256 ID of the token to query the owner of
                   * @return address currently marked as the owner of the given token ID
                   */
                  function ownerOf(uint256 tokenId) public view returns (address) {
                      address owner = _tokenOwner[tokenId];
                      require(owner != address(0));
                      return owner;
                  }
              
                  /**
                   * @dev Approves another address to transfer the given token ID
                   * The zero address indicates there is no approved address.
                   * There can only be one approved address per token at a given time.
                   * Can only be called by the token owner or an approved operator.
                   * @param to address to be approved for the given token ID
                   * @param tokenId uint256 ID of the token to be approved
                   */
                  function approve(address to, uint256 tokenId) public {
                      address owner = ownerOf(tokenId);
                      require(to != owner);
                      require(msg.sender == owner || isApprovedForAll(owner, msg.sender));
              
                      _tokenApprovals[tokenId] = to;
                      emit Approval(owner, to, tokenId);
                  }
              
                  /**
                   * @dev Gets the approved address for a token ID, or zero if no address set
                   * Reverts if the token ID does not exist.
                   * @param tokenId uint256 ID of the token to query the approval of
                   * @return address currently approved for the given token ID
                   */
                  function getApproved(uint256 tokenId) public view returns (address) {
                      require(_exists(tokenId));
                      return _tokenApprovals[tokenId];
                  }
              
                  /**
                   * @dev Sets or unsets the approval of a given operator
                   * An operator is allowed to transfer all tokens of the sender on their behalf
                   * @param to operator address to set the approval
                   * @param approved representing the status of the approval to be set
                   */
                  function setApprovalForAll(address to, bool approved) public {
                      require(to != msg.sender);
                      _operatorApprovals[msg.sender][to] = approved;
                      emit ApprovalForAll(msg.sender, to, approved);
                  }
              
                  /**
                   * @dev Tells whether an operator is approved by a given owner
                   * @param owner owner address which you want to query the approval of
                   * @param operator operator address which you want to query the approval of
                   * @return bool whether the given operator is approved by the given owner
                   */
                  function isApprovedForAll(address owner, address operator) public view returns (bool) {
                      return _operatorApprovals[owner][operator];
                  }
              
                  /**
                   * @dev Transfers the ownership of a given token ID to another address
                   * Usage of this method is discouraged, use `safeTransferFrom` whenever possible
                   * Requires the msg.sender to be the owner, approved, or operator
                   * @param from current owner of the token
                   * @param to address to receive the ownership of the given token ID
                   * @param tokenId uint256 ID of the token to be transferred
                   */
                  function transferFrom(
                      address from,
                      address to,
                      uint256 tokenId
                  ) public {
                      require(_isApprovedOrOwner(msg.sender, tokenId));
              
                      _transferFrom(from, to, tokenId);
                  }
              
                  /**
                   * @dev Safely transfers the ownership of a given token ID to another address
                   * If the target address is a contract, it must implement `onERC721Received`,
                   * which is called upon a safe transfer, and return the magic value
                   * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
                   * the transfer is reverted.
                   * Requires the msg.sender to be the owner, approved, or operator
                   * @param from current owner of the token
                   * @param to address to receive the ownership of the given token ID
                   * @param tokenId uint256 ID of the token to be transferred
                   */
                  function safeTransferFrom(
                      address from,
                      address to,
                      uint256 tokenId
                  ) public {
                      safeTransferFrom(from, to, tokenId, "");
                  }
              
                  /**
                   * @dev Safely transfers the ownership of a given token ID to another address
                   * If the target address is a contract, it must implement `onERC721Received`,
                   * which is called upon a safe transfer, and return the magic value
                   * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
                   * the transfer is reverted.
                   * Requires the msg.sender to be the owner, approved, or operator
                   * @param from current owner of the token
                   * @param to address to receive the ownership of the given token ID
                   * @param tokenId uint256 ID of the token to be transferred
                   * @param _data bytes data to send along with a safe transfer check
                   */
                  function safeTransferFrom(
                      address from,
                      address to,
                      uint256 tokenId,
                      bytes memory _data
                  ) public {
                      transferFrom(from, to, tokenId);
                      require(_checkOnERC721Received(from, to, tokenId, _data));
                  }
              
                  /**
                   * @dev Returns whether the specified token exists
                   * @param tokenId uint256 ID of the token to query the existence of
                   * @return bool whether the token exists
                   */
                  function _exists(uint256 tokenId) internal view returns (bool) {
                      address owner = _tokenOwner[tokenId];
                      return owner != address(0);
                  }
              
                  /**
                   * @dev Returns whether the given spender can transfer a given token ID
                   * @param spender address of the spender to query
                   * @param tokenId uint256 ID of the token to be transferred
                   * @return bool whether the msg.sender is approved for the given token ID,
                   * is an operator of the owner, or is the owner of the token
                   */
                  function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
                      address owner = ownerOf(tokenId);
                      return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
                  }
              
                  /**
                   * @dev Internal function to mint a new token
                   * Reverts if the given token ID already exists
                   * @param to The address that will own the minted token
                   * @param tokenId uint256 ID of the token to be minted
                   */
                  function _mint(address to, uint256 tokenId) internal {
                      require(to != address(0));
                      require(!_exists(tokenId));
              
                      _tokenOwner[tokenId] = to;
                      _ownedTokensCount[to].increment();
              
                      emit Transfer(address(0), to, tokenId);
                  }
              
                  /**
                   * @dev Internal function to burn a specific token
                   * Reverts if the token does not exist
                   * Deprecated, use _burn(uint256) instead.
                   * @param owner owner of the token to burn
                   * @param tokenId uint256 ID of the token being burned
                   */
                  function _burn(address owner, uint256 tokenId) internal {
                      require(ownerOf(tokenId) == owner);
              
                      _clearApproval(tokenId);
              
                      _ownedTokensCount[owner].decrement();
                      _tokenOwner[tokenId] = address(0);
              
                      emit Transfer(owner, address(0), tokenId);
                  }
              
                  /**
                   * @dev Internal function to burn a specific token
                   * Reverts if the token does not exist
                   * @param tokenId uint256 ID of the token being burned
                   */
                  function _burn(uint256 tokenId) internal {
                      _burn(ownerOf(tokenId), tokenId);
                  }
              
                  /**
                   * @dev Internal function to transfer ownership of a given token ID to another address.
                   * As opposed to transferFrom, this imposes no restrictions on msg.sender.
                   * @param from current owner of the token
                   * @param to address to receive the ownership of the given token ID
                   * @param tokenId uint256 ID of the token to be transferred
                   */
                  function _transferFrom(
                      address from,
                      address to,
                      uint256 tokenId
                  ) internal {
                      require(ownerOf(tokenId) == from);
                      require(to != address(0));
              
                      _clearApproval(tokenId);
              
                      _ownedTokensCount[from].decrement();
                      _ownedTokensCount[to].increment();
              
                      _tokenOwner[tokenId] = to;
              
                      emit Transfer(from, to, tokenId);
                  }
              
                  /**
                   * @dev Internal function to invoke `onERC721Received` on a target address
                   * The call is not executed if the target address is not a contract
                   * @param from address representing the previous owner of the given token ID
                   * @param to target address that will receive the tokens
                   * @param tokenId uint256 ID of the token to be transferred
                   * @param _data bytes optional data to send along with the call
                   * @return bool whether the call correctly returned the expected magic value
                   */
                  function _checkOnERC721Received(
                      address from,
                      address to,
                      uint256 tokenId,
                      bytes memory _data
                  ) internal returns (bool) {
                      if (!to.isContract()) {
                          return true;
                      }
              
                      bytes4 retval = IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, _data);
                      return (retval == _ERC721_RECEIVED);
                  }
              
                  /**
                   * @dev Private function to clear current approval of a given token ID
                   * @param tokenId uint256 ID of the token to be transferred
                   */
                  function _clearApproval(uint256 tokenId) private {
                      if (_tokenApprovals[tokenId] != address(0)) {
                          _tokenApprovals[tokenId] = address(0);
                      }
                  }
              }
              
              // File: contracts/root/withdrawManager/ExitNFT.sol
              
              pragma solidity ^0.5.2;
              
              
              contract ExitNFT is ERC721 {
                  Registry internal registry;
              
                  modifier onlyWithdrawManager() {
                      require(msg.sender == registry.getWithdrawManagerAddress(), "UNAUTHORIZED_WITHDRAW_MANAGER_ONLY");
                      _;
                  }
              
                  constructor(address _registry) public {
                      registry = Registry(_registry);
                  }
              
                  function mint(address _owner, uint256 _tokenId) external onlyWithdrawManager {
                      _mint(_owner, _tokenId);
                  }
              
                  function burn(uint256 _tokenId) external onlyWithdrawManager {
                      _burn(_tokenId);
                  }
              
                  function exists(uint256 tokenId) public view returns (bool) {
                      return _exists(tokenId);
                  }
              }
              
              // File: contracts/root/withdrawManager/WithdrawManagerStorage.sol
              
              pragma solidity ^0.5.2;
              
              
              contract ExitsDataStructure {
                  struct Input {
                      address utxoOwner;
                      address predicate;
                      address token;
                  }
              
                  struct PlasmaExit {
                      uint256 receiptAmountOrNFTId;
                      bytes32 txHash;
                      address owner;
                      address token;
                      bool isRegularExit;
                      address predicate;
                      // Mapping from age of input to Input
                      mapping(uint256 => Input) inputs;
                  }
              }
              
              
              contract WithdrawManagerHeader is ExitsDataStructure {
                  event Withdraw(uint256 indexed exitId, address indexed user, address indexed token, uint256 amount);
              
                  event ExitStarted(
                      address indexed exitor,
                      uint256 indexed exitId,
                      address indexed token,
                      uint256 amount,
                      bool isRegularExit
                  );
              
                  event ExitUpdated(uint256 indexed exitId, uint256 indexed age, address signer);
                  event ExitPeriodUpdate(uint256 indexed oldExitPeriod, uint256 indexed newExitPeriod);
              
                  event ExitCancelled(uint256 indexed exitId);
              }
              
              
              contract WithdrawManagerStorage is ProxyStorage, WithdrawManagerHeader {
                  // 0.5 week = 7 * 86400 / 2 = 302400
                  uint256 public HALF_EXIT_PERIOD = 302400;
              
                  // Bonded exits collaterized at 0.1 ETH
                  uint256 internal constant BOND_AMOUNT = 10**17;
              
                  Registry internal registry;
                  RootChain internal rootChain;
              
                  mapping(uint128 => bool) isKnownExit;
                  mapping(uint256 => PlasmaExit) public exits;
                  // mapping with token => (owner => exitId) keccak(token+owner) keccak(token+owner+tokenId)
                  mapping(bytes32 => uint256) public ownerExits;
                  mapping(address => address) public exitsQueues;
                  ExitNFT public exitNft;
              
                  // ERC721, ERC20 and Weth transfers require 155000, 100000, 52000 gas respectively
                  // Processing each exit in a while loop iteration requires ~52000 gas (@todo check if this changed)
                  // uint32 constant internal ITERATION_GAS = 52000;
              
                  // So putting an upper limit of 155000 + 52000 + leeway
                  uint32 public ON_FINALIZE_GAS_LIMIT = 300000;
              
                  uint256 public exitWindow;
              }
              
              // File: contracts/root/withdrawManager/WithdrawManagerProxy.sol
              
              pragma solidity ^0.5.2;
              
              
              contract WithdrawManagerProxy is Proxy, WithdrawManagerStorage {
                  constructor(
                      address _proxyTo,
                      address _registry,
                      address _rootChain,
                      address _exitNft
                  ) public Proxy(_proxyTo) {
                      registry = Registry(_registry);
                      rootChain = RootChain(_rootChain);
                      exitNft = ExitNFT(_exitNft);
                  }
              }

              File 2 of 9: ExitNFT
              /**
              Matic network contracts
              */
              
              pragma solidity ^0.5.2;
              
              
              /**
               * @title IERC165
               * @dev https://eips.ethereum.org/EIPS/eip-165
               */
              interface IERC165 {
                  /**
                   * @notice Query if a contract implements an interface
                   * @param interfaceId The interface identifier, as specified in ERC-165
                   * @dev Interface identification is specified in ERC-165. This function
                   * uses less than 30,000 gas.
                   */
                  function supportsInterface(bytes4 interfaceId) external view returns (bool);
              }
              
              /**
               * @title ERC721 Non-Fungible Token Standard basic interface
               * @dev see https://eips.ethereum.org/EIPS/eip-721
               */
              contract IERC721 is IERC165 {
                  event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
                  event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
                  event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
              
                  function balanceOf(address owner) public view returns (uint256 balance);
                  function ownerOf(uint256 tokenId) public view returns (address owner);
              
                  function approve(address to, uint256 tokenId) public;
                  function getApproved(uint256 tokenId) public view returns (address operator);
              
                  function setApprovalForAll(address operator, bool _approved) public;
                  function isApprovedForAll(address owner, address operator) public view returns (bool);
              
                  function transferFrom(address from, address to, uint256 tokenId) public;
                  function safeTransferFrom(address from, address to, uint256 tokenId) public;
              
                  function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public;
              }
              
              /**
               * @title ERC721 token receiver interface
               * @dev Interface for any contract that wants to support safeTransfers
               * from ERC721 asset contracts.
               */
              contract IERC721Receiver {
                  /**
                   * @notice Handle the receipt of an NFT
                   * @dev The ERC721 smart contract calls this function on the recipient
                   * after a `safeTransfer`. This function MUST return the function selector,
                   * otherwise the caller will revert the transaction. The selector to be
                   * returned can be obtained as `this.onERC721Received.selector`. This
                   * function MAY throw to revert and reject the transfer.
                   * Note: the ERC721 contract address is always the message sender.
                   * @param operator The address which called `safeTransferFrom` function
                   * @param from The address which previously owned the token
                   * @param tokenId The NFT identifier which is being transferred
                   * @param data Additional data with no specified format
                   * @return bytes4 `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
                   */
                  function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data)
                  public returns (bytes4);
              }
              
              /**
               * @title SafeMath
               * @dev Unsigned math operations with safety checks that revert on error
               */
              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;
                  }
              }
              
              /**
               * Utility library of inline functions on addresses
               */
              library Address {
                  /**
                   * Returns whether the target address is a contract
                   * @dev This function will return false if invoked during the constructor of a contract,
                   * as the code is not actually created until after the constructor finishes.
                   * @param account address of the account to check
                   * @return whether the target address is a contract
                   */
                  function isContract(address account) internal view returns (bool) {
                      uint256 size;
                      // XXX Currently there is no better way to check if there is a contract in an address
                      // than to check the size of the code at that address.
                      // See https://ethereum.stackexchange.com/a/14016/36603
                      // for more details about how this works.
                      // TODO Check this again before the Serenity release, because all addresses will be
                      // contracts then.
                      // solhint-disable-next-line no-inline-assembly
                      assembly { size := extcodesize(account) }
                      return size > 0;
                  }
              }
              
              /**
               * @title Counters
               * @author Matt Condon (@shrugs)
               * @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number
               * of elements in a mapping, issuing ERC721 ids, or counting request ids
               *
               * Include with `using Counters for Counters.Counter;`
               * Since it is not possible to overflow a 256 bit integer with increments of one, `increment` can skip the SafeMath
               * overflow check, thereby saving gas. This does assume however correct usage, in that the underlying `_value` is never
               * directly accessed.
               */
              library Counters {
                  using SafeMath for uint256;
              
                  struct Counter {
                      // This variable should never be directly accessed by users of the library: interactions must be restricted to
                      // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
                      // this feature: see https://github.com/ethereum/solidity/issues/4637
                      uint256 _value; // default: 0
                  }
              
                  function current(Counter storage counter) internal view returns (uint256) {
                      return counter._value;
                  }
              
                  function increment(Counter storage counter) internal {
                      counter._value += 1;
                  }
              
                  function decrement(Counter storage counter) internal {
                      counter._value = counter._value.sub(1);
                  }
              }
              
              /**
               * @title ERC165
               * @author Matt Condon (@shrugs)
               * @dev Implements ERC165 using a lookup table.
               */
              contract ERC165 is IERC165 {
                  bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
                  /*
                   * 0x01ffc9a7 ===
                   *     bytes4(keccak256('supportsInterface(bytes4)'))
                   */
              
                  /**
                   * @dev a mapping of interface id to whether or not it's supported
                   */
                  mapping(bytes4 => bool) private _supportedInterfaces;
              
                  /**
                   * @dev A contract implementing SupportsInterfaceWithLookup
                   * implement ERC165 itself
                   */
                  constructor () internal {
                      _registerInterface(_INTERFACE_ID_ERC165);
                  }
              
                  /**
                   * @dev implement supportsInterface(bytes4) using a lookup table
                   */
                  function supportsInterface(bytes4 interfaceId) external view returns (bool) {
                      return _supportedInterfaces[interfaceId];
                  }
              
                  /**
                   * @dev internal method for registering an interface
                   */
                  function _registerInterface(bytes4 interfaceId) internal {
                      require(interfaceId != 0xffffffff);
                      _supportedInterfaces[interfaceId] = true;
                  }
              }
              
              contract ERC721 is ERC165, IERC721 {
                  using SafeMath for uint256;
                  using Address for address;
                  using Counters for Counters.Counter;
              
                  // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
                  // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector`
                  bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
              
                  // Mapping from token ID to owner
                  mapping (uint256 => address) private _tokenOwner;
              
                  // Mapping from token ID to approved address
                  mapping (uint256 => address) private _tokenApprovals;
              
                  // Mapping from owner to number of owned token
                  mapping (address => Counters.Counter) private _ownedTokensCount;
              
                  // Mapping from owner to operator approvals
                  mapping (address => mapping (address => bool)) private _operatorApprovals;
              
                  bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
                  /*
                   * 0x80ac58cd ===
                   *     bytes4(keccak256('balanceOf(address)')) ^
                   *     bytes4(keccak256('ownerOf(uint256)')) ^
                   *     bytes4(keccak256('approve(address,uint256)')) ^
                   *     bytes4(keccak256('getApproved(uint256)')) ^
                   *     bytes4(keccak256('setApprovalForAll(address,bool)')) ^
                   *     bytes4(keccak256('isApprovedForAll(address,address)')) ^
                   *     bytes4(keccak256('transferFrom(address,address,uint256)')) ^
                   *     bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^
                   *     bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)'))
                   */
              
                  constructor () public {
                      // register the supported interfaces to conform to ERC721 via ERC165
                      _registerInterface(_INTERFACE_ID_ERC721);
                  }
              
                  /**
                   * @dev Gets the balance of the specified address
                   * @param owner address to query the balance of
                   * @return uint256 representing the amount owned by the passed address
                   */
                  function balanceOf(address owner) public view returns (uint256) {
                      require(owner != address(0));
                      return _ownedTokensCount[owner].current();
                  }
              
                  /**
                   * @dev Gets the owner of the specified token ID
                   * @param tokenId uint256 ID of the token to query the owner of
                   * @return address currently marked as the owner of the given token ID
                   */
                  function ownerOf(uint256 tokenId) public view returns (address) {
                      address owner = _tokenOwner[tokenId];
                      require(owner != address(0));
                      return owner;
                  }
              
                  /**
                   * @dev Approves another address to transfer the given token ID
                   * The zero address indicates there is no approved address.
                   * There can only be one approved address per token at a given time.
                   * Can only be called by the token owner or an approved operator.
                   * @param to address to be approved for the given token ID
                   * @param tokenId uint256 ID of the token to be approved
                   */
                  function approve(address to, uint256 tokenId) public {
                      address owner = ownerOf(tokenId);
                      require(to != owner);
                      require(msg.sender == owner || isApprovedForAll(owner, msg.sender));
              
                      _tokenApprovals[tokenId] = to;
                      emit Approval(owner, to, tokenId);
                  }
              
                  /**
                   * @dev Gets the approved address for a token ID, or zero if no address set
                   * Reverts if the token ID does not exist.
                   * @param tokenId uint256 ID of the token to query the approval of
                   * @return address currently approved for the given token ID
                   */
                  function getApproved(uint256 tokenId) public view returns (address) {
                      require(_exists(tokenId));
                      return _tokenApprovals[tokenId];
                  }
              
                  /**
                   * @dev Sets or unsets the approval of a given operator
                   * An operator is allowed to transfer all tokens of the sender on their behalf
                   * @param to operator address to set the approval
                   * @param approved representing the status of the approval to be set
                   */
                  function setApprovalForAll(address to, bool approved) public {
                      require(to != msg.sender);
                      _operatorApprovals[msg.sender][to] = approved;
                      emit ApprovalForAll(msg.sender, to, approved);
                  }
              
                  /**
                   * @dev Tells whether an operator is approved by a given owner
                   * @param owner owner address which you want to query the approval of
                   * @param operator operator address which you want to query the approval of
                   * @return bool whether the given operator is approved by the given owner
                   */
                  function isApprovedForAll(address owner, address operator) public view returns (bool) {
                      return _operatorApprovals[owner][operator];
                  }
              
                  /**
                   * @dev Transfers the ownership of a given token ID to another address
                   * Usage of this method is discouraged, use `safeTransferFrom` whenever possible
                   * Requires the msg.sender to be the owner, approved, or operator
                   * @param from current owner of the token
                   * @param to address to receive the ownership of the given token ID
                   * @param tokenId uint256 ID of the token to be transferred
                   */
                  function transferFrom(address from, address to, uint256 tokenId) public {
                      require(_isApprovedOrOwner(msg.sender, tokenId));
              
                      _transferFrom(from, to, tokenId);
                  }
              
                  /**
                   * @dev Safely transfers the ownership of a given token ID to another address
                   * If the target address is a contract, it must implement `onERC721Received`,
                   * which is called upon a safe transfer, and return the magic value
                   * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
                   * the transfer is reverted.
                   * Requires the msg.sender to be the owner, approved, or operator
                   * @param from current owner of the token
                   * @param to address to receive the ownership of the given token ID
                   * @param tokenId uint256 ID of the token to be transferred
                   */
                  function safeTransferFrom(address from, address to, uint256 tokenId) public {
                      safeTransferFrom(from, to, tokenId, "");
                  }
              
                  /**
                   * @dev Safely transfers the ownership of a given token ID to another address
                   * If the target address is a contract, it must implement `onERC721Received`,
                   * which is called upon a safe transfer, and return the magic value
                   * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
                   * the transfer is reverted.
                   * Requires the msg.sender to be the owner, approved, or operator
                   * @param from current owner of the token
                   * @param to address to receive the ownership of the given token ID
                   * @param tokenId uint256 ID of the token to be transferred
                   * @param _data bytes data to send along with a safe transfer check
                   */
                  function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public {
                      transferFrom(from, to, tokenId);
                      require(_checkOnERC721Received(from, to, tokenId, _data));
                  }
              
                  /**
                   * @dev Returns whether the specified token exists
                   * @param tokenId uint256 ID of the token to query the existence of
                   * @return bool whether the token exists
                   */
                  function _exists(uint256 tokenId) internal view returns (bool) {
                      address owner = _tokenOwner[tokenId];
                      return owner != address(0);
                  }
              
                  /**
                   * @dev Returns whether the given spender can transfer a given token ID
                   * @param spender address of the spender to query
                   * @param tokenId uint256 ID of the token to be transferred
                   * @return bool whether the msg.sender is approved for the given token ID,
                   * is an operator of the owner, or is the owner of the token
                   */
                  function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
                      address owner = ownerOf(tokenId);
                      return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
                  }
              
                  /**
                   * @dev Internal function to mint a new token
                   * Reverts if the given token ID already exists
                   * @param to The address that will own the minted token
                   * @param tokenId uint256 ID of the token to be minted
                   */
                  function _mint(address to, uint256 tokenId) internal {
                      require(to != address(0));
                      require(!_exists(tokenId));
              
                      _tokenOwner[tokenId] = to;
                      _ownedTokensCount[to].increment();
              
                      emit Transfer(address(0), to, tokenId);
                  }
              
                  /**
                   * @dev Internal function to burn a specific token
                   * Reverts if the token does not exist
                   * Deprecated, use _burn(uint256) instead.
                   * @param owner owner of the token to burn
                   * @param tokenId uint256 ID of the token being burned
                   */
                  function _burn(address owner, uint256 tokenId) internal {
                      require(ownerOf(tokenId) == owner);
              
                      _clearApproval(tokenId);
              
                      _ownedTokensCount[owner].decrement();
                      _tokenOwner[tokenId] = address(0);
              
                      emit Transfer(owner, address(0), tokenId);
                  }
              
                  /**
                   * @dev Internal function to burn a specific token
                   * Reverts if the token does not exist
                   * @param tokenId uint256 ID of the token being burned
                   */
                  function _burn(uint256 tokenId) internal {
                      _burn(ownerOf(tokenId), tokenId);
                  }
              
                  /**
                   * @dev Internal function to transfer ownership of a given token ID to another address.
                   * As opposed to transferFrom, this imposes no restrictions on msg.sender.
                   * @param from current owner of the token
                   * @param to address to receive the ownership of the given token ID
                   * @param tokenId uint256 ID of the token to be transferred
                   */
                  function _transferFrom(address from, address to, uint256 tokenId) internal {
                      require(ownerOf(tokenId) == from);
                      require(to != address(0));
              
                      _clearApproval(tokenId);
              
                      _ownedTokensCount[from].decrement();
                      _ownedTokensCount[to].increment();
              
                      _tokenOwner[tokenId] = to;
              
                      emit Transfer(from, to, tokenId);
                  }
              
                  /**
                   * @dev Internal function to invoke `onERC721Received` on a target address
                   * The call is not executed if the target address is not a contract
                   * @param from address representing the previous owner of the given token ID
                   * @param to target address that will receive the tokens
                   * @param tokenId uint256 ID of the token to be transferred
                   * @param _data bytes optional data to send along with the call
                   * @return bool whether the call correctly returned the expected magic value
                   */
                  function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)
                      internal returns (bool)
                  {
                      if (!to.isContract()) {
                          return true;
                      }
              
                      bytes4 retval = IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, _data);
                      return (retval == _ERC721_RECEIVED);
                  }
              
                  /**
                   * @dev Private function to clear current approval of a given token ID
                   * @param tokenId uint256 ID of the token to be transferred
                   */
                  function _clearApproval(uint256 tokenId) private {
                      if (_tokenApprovals[tokenId] != address(0)) {
                          _tokenApprovals[tokenId] = address(0);
                      }
                  }
              }
              
              interface IGovernance {
                  function update(address target, bytes calldata data) external;
              }
              
              contract Governable {
                  IGovernance public governance;
              
                  constructor(address _governance) public {
                      governance = IGovernance(_governance);
                  }
              
                  modifier onlyGovernance() {
                      require(
                          msg.sender == address(governance),
                          "Only governance contract is authorized"
                      );
                      _;
                  }
              }
              
              contract IWithdrawManager {
                  function createExitQueue(address token) external;
              
                  function verifyInclusion(
                      bytes calldata data,
                      uint8 offset,
                      bool verifyTxInclusion
                  ) external view returns (uint256 age);
              
                  function addExitToQueue(
                      address exitor,
                      address childToken,
                      address rootToken,
                      uint256 exitAmountOrTokenId,
                      bytes32 txHash,
                      bool isRegularExit,
                      uint256 priority
                  ) external;
              
                  function addInput(
                      uint256 exitId,
                      uint256 age,
                      address utxoOwner,
                      address token
                  ) external;
              
                  function challengeExit(
                      uint256 exitId,
                      uint256 inputId,
                      bytes calldata challengeData,
                      address adjudicatorPredicate
                  ) external;
              }
              
              contract Registry is Governable {
                  // @todo hardcode constants
                  bytes32 private constant WETH_TOKEN = keccak256("wethToken");
                  bytes32 private constant DEPOSIT_MANAGER = keccak256("depositManager");
                  bytes32 private constant STAKE_MANAGER = keccak256("stakeManager");
                  bytes32 private constant VALIDATOR_SHARE = keccak256("validatorShare");
                  bytes32 private constant WITHDRAW_MANAGER = keccak256("withdrawManager");
                  bytes32 private constant CHILD_CHAIN = keccak256("childChain");
                  bytes32 private constant STATE_SENDER = keccak256("stateSender");
                  bytes32 private constant SLASHING_MANAGER = keccak256("slashingManager");
              
                  address public erc20Predicate;
                  address public erc721Predicate;
              
                  mapping(bytes32 => address) public contractMap;
                  mapping(address => address) public rootToChildToken;
                  mapping(address => address) public childToRootToken;
                  mapping(address => bool) public proofValidatorContracts;
                  mapping(address => bool) public isERC721;
              
                  enum Type {Invalid, ERC20, ERC721, Custom}
                  struct Predicate {
                      Type _type;
                  }
                  mapping(address => Predicate) public predicates;
              
                  event TokenMapped(address indexed rootToken, address indexed childToken);
                  event ProofValidatorAdded(address indexed validator, address indexed from);
                  event ProofValidatorRemoved(address indexed validator, address indexed from);
                  event PredicateAdded(address indexed predicate, address indexed from);
                  event PredicateRemoved(address indexed predicate, address indexed from);
                  event ContractMapUpdated(bytes32 indexed key, address indexed previousContract, address indexed newContract);
              
                  constructor(address _governance) public Governable(_governance) {}
              
                  function updateContractMap(bytes32 _key, address _address) external onlyGovernance {
                      emit ContractMapUpdated(_key, contractMap[_key], _address);
                      contractMap[_key] = _address;
                  }
              
                  /**
                   * @dev Map root token to child token
                   * @param _rootToken Token address on the root chain
                   * @param _childToken Token address on the child chain
                   * @param _isERC721 Is the token being mapped ERC721
                   */
                  function mapToken(
                      address _rootToken,
                      address _childToken,
                      bool _isERC721
                  ) external onlyGovernance {
                      require(_rootToken != address(0x0) && _childToken != address(0x0), "INVALID_TOKEN_ADDRESS");
                      rootToChildToken[_rootToken] = _childToken;
                      childToRootToken[_childToken] = _rootToken;
                      isERC721[_rootToken] = _isERC721;
                      IWithdrawManager(contractMap[WITHDRAW_MANAGER]).createExitQueue(_rootToken);
                      emit TokenMapped(_rootToken, _childToken);
                  }
              
                  function addErc20Predicate(address predicate) public onlyGovernance {
                      require(predicate != address(0x0), "Can not add null address as predicate");
                      erc20Predicate = predicate;
                      addPredicate(predicate, Type.ERC20);
                  }
              
                  function addErc721Predicate(address predicate) public onlyGovernance {
                      erc721Predicate = predicate;
                      addPredicate(predicate, Type.ERC721);
                  }
              
                  function addPredicate(address predicate, Type _type) public onlyGovernance {
                      require(predicates[predicate]._type == Type.Invalid, "Predicate already added");
                      predicates[predicate]._type = _type;
                      emit PredicateAdded(predicate, msg.sender);
                  }
              
                  function removePredicate(address predicate) public onlyGovernance {
                      require(predicates[predicate]._type != Type.Invalid, "Predicate does not exist");
                      delete predicates[predicate];
                      emit PredicateRemoved(predicate, msg.sender);
                  }
              
                  function getValidatorShareAddress() public view returns (address) {
                      return contractMap[VALIDATOR_SHARE];
                  }
              
                  function getWethTokenAddress() public view returns (address) {
                      return contractMap[WETH_TOKEN];
                  }
              
                  function getDepositManagerAddress() public view returns (address) {
                      return contractMap[DEPOSIT_MANAGER];
                  }
              
                  function getStakeManagerAddress() public view returns (address) {
                      return contractMap[STAKE_MANAGER];
                  }
              
                  function getSlashingManagerAddress() public view returns (address) {
                      return contractMap[SLASHING_MANAGER];
                  }
              
                  function getWithdrawManagerAddress() public view returns (address) {
                      return contractMap[WITHDRAW_MANAGER];
                  }
              
                  function getChildChainAndStateSender() public view returns (address, address) {
                      return (contractMap[CHILD_CHAIN], contractMap[STATE_SENDER]);
                  }
              
                  function isTokenMapped(address _token) public view returns (bool) {
                      return rootToChildToken[_token] != address(0x0);
                  }
              
                  function isTokenMappedAndIsErc721(address _token) public view returns (bool) {
                      require(isTokenMapped(_token), "TOKEN_NOT_MAPPED");
                      return isERC721[_token];
                  }
              
                  function isTokenMappedAndGetPredicate(address _token) public view returns (address) {
                      if (isTokenMappedAndIsErc721(_token)) {
                          return erc721Predicate;
                      }
                      return erc20Predicate;
                  }
              
                  function isChildTokenErc721(address childToken) public view returns (bool) {
                      address rootToken = childToRootToken[childToken];
                      require(rootToken != address(0x0), "Child token is not mapped");
                      return isERC721[rootToken];
                  }
              }
              
              contract ExitNFT is ERC721 {
                  Registry internal registry;
              
                  modifier onlyWithdrawManager() {
                      require(
                          msg.sender == registry.getWithdrawManagerAddress(),
                          "UNAUTHORIZED_WITHDRAW_MANAGER_ONLY"
                      );
                      _;
                  }
              
                  constructor(address _registry) public {
                      registry = Registry(_registry);
                  }
              
                  function mint(address _owner, uint256 _tokenId)
                      external
                      onlyWithdrawManager
                  {
                      _mint(_owner, _tokenId);
                  }
              
                  function burn(uint256 _tokenId) external onlyWithdrawManager {
                      _burn(_tokenId);
                  }
              
                  function exists(uint256 tokenId) public view returns (bool) {
                      return _exists(tokenId);
                  }
              }

              File 3 of 9: DepositManagerProxy
              // File: contracts/common/governance/IGovernance.sol
              
              pragma solidity ^0.5.2;
              
              interface IGovernance {
                  function update(address target, bytes calldata data) external;
              }
              
              // File: contracts/common/governance/Governable.sol
              
              pragma solidity ^0.5.2;
              
              
              contract Governable {
                  IGovernance public governance;
              
                  constructor(address _governance) public {
                      governance = IGovernance(_governance);
                  }
              
                  modifier onlyGovernance() {
                      require(
                          msg.sender == address(governance),
                          "Only governance contract is authorized"
                      );
                      _;
                  }
              }
              
              // File: contracts/root/withdrawManager/IWithdrawManager.sol
              
              pragma solidity ^0.5.2;
              
              contract IWithdrawManager {
                  function createExitQueue(address token) external;
              
                  function verifyInclusion(
                      bytes calldata data,
                      uint8 offset,
                      bool verifyTxInclusion
                  ) external view returns (uint256 age);
              
                  function addExitToQueue(
                      address exitor,
                      address childToken,
                      address rootToken,
                      uint256 exitAmountOrTokenId,
                      bytes32 txHash,
                      bool isRegularExit,
                      uint256 priority
                  ) external;
              
                  function addInput(
                      uint256 exitId,
                      uint256 age,
                      address utxoOwner,
                      address token
                  ) external;
              
                  function challengeExit(
                      uint256 exitId,
                      uint256 inputId,
                      bytes calldata challengeData,
                      address adjudicatorPredicate
                  ) external;
              }
              
              // File: contracts/common/Registry.sol
              
              pragma solidity ^0.5.2;
              
              
              
              
              contract Registry is Governable {
                  // @todo hardcode constants
                  bytes32 private constant WETH_TOKEN = keccak256("wethToken");
                  bytes32 private constant DEPOSIT_MANAGER = keccak256("depositManager");
                  bytes32 private constant STAKE_MANAGER = keccak256("stakeManager");
                  bytes32 private constant VALIDATOR_SHARE = keccak256("validatorShare");
                  bytes32 private constant WITHDRAW_MANAGER = keccak256("withdrawManager");
                  bytes32 private constant CHILD_CHAIN = keccak256("childChain");
                  bytes32 private constant STATE_SENDER = keccak256("stateSender");
                  bytes32 private constant SLASHING_MANAGER = keccak256("slashingManager");
              
                  address public erc20Predicate;
                  address public erc721Predicate;
              
                  mapping(bytes32 => address) public contractMap;
                  mapping(address => address) public rootToChildToken;
                  mapping(address => address) public childToRootToken;
                  mapping(address => bool) public proofValidatorContracts;
                  mapping(address => bool) public isERC721;
              
                  enum Type {Invalid, ERC20, ERC721, Custom}
                  struct Predicate {
                      Type _type;
                  }
                  mapping(address => Predicate) public predicates;
              
                  event TokenMapped(address indexed rootToken, address indexed childToken);
                  event ProofValidatorAdded(address indexed validator, address indexed from);
                  event ProofValidatorRemoved(address indexed validator, address indexed from);
                  event PredicateAdded(address indexed predicate, address indexed from);
                  event PredicateRemoved(address indexed predicate, address indexed from);
                  event ContractMapUpdated(bytes32 indexed key, address indexed previousContract, address indexed newContract);
              
                  constructor(address _governance) public Governable(_governance) {}
              
                  function updateContractMap(bytes32 _key, address _address) external onlyGovernance {
                      emit ContractMapUpdated(_key, contractMap[_key], _address);
                      contractMap[_key] = _address;
                  }
              
                  /**
                   * @dev Map root token to child token
                   * @param _rootToken Token address on the root chain
                   * @param _childToken Token address on the child chain
                   * @param _isERC721 Is the token being mapped ERC721
                   */
                  function mapToken(
                      address _rootToken,
                      address _childToken,
                      bool _isERC721
                  ) external onlyGovernance {
                      require(_rootToken != address(0x0) && _childToken != address(0x0), "INVALID_TOKEN_ADDRESS");
                      rootToChildToken[_rootToken] = _childToken;
                      childToRootToken[_childToken] = _rootToken;
                      isERC721[_rootToken] = _isERC721;
                      IWithdrawManager(contractMap[WITHDRAW_MANAGER]).createExitQueue(_rootToken);
                      emit TokenMapped(_rootToken, _childToken);
                  }
              
                  function addErc20Predicate(address predicate) public onlyGovernance {
                      require(predicate != address(0x0), "Can not add null address as predicate");
                      erc20Predicate = predicate;
                      addPredicate(predicate, Type.ERC20);
                  }
              
                  function addErc721Predicate(address predicate) public onlyGovernance {
                      erc721Predicate = predicate;
                      addPredicate(predicate, Type.ERC721);
                  }
              
                  function addPredicate(address predicate, Type _type) public onlyGovernance {
                      require(predicates[predicate]._type == Type.Invalid, "Predicate already added");
                      predicates[predicate]._type = _type;
                      emit PredicateAdded(predicate, msg.sender);
                  }
              
                  function removePredicate(address predicate) public onlyGovernance {
                      require(predicates[predicate]._type != Type.Invalid, "Predicate does not exist");
                      delete predicates[predicate];
                      emit PredicateRemoved(predicate, msg.sender);
                  }
              
                  function getValidatorShareAddress() public view returns (address) {
                      return contractMap[VALIDATOR_SHARE];
                  }
              
                  function getWethTokenAddress() public view returns (address) {
                      return contractMap[WETH_TOKEN];
                  }
              
                  function getDepositManagerAddress() public view returns (address) {
                      return contractMap[DEPOSIT_MANAGER];
                  }
              
                  function getStakeManagerAddress() public view returns (address) {
                      return contractMap[STAKE_MANAGER];
                  }
              
                  function getSlashingManagerAddress() public view returns (address) {
                      return contractMap[SLASHING_MANAGER];
                  }
              
                  function getWithdrawManagerAddress() public view returns (address) {
                      return contractMap[WITHDRAW_MANAGER];
                  }
              
                  function getChildChainAndStateSender() public view returns (address, address) {
                      return (contractMap[CHILD_CHAIN], contractMap[STATE_SENDER]);
                  }
              
                  function isTokenMapped(address _token) public view returns (bool) {
                      return rootToChildToken[_token] != address(0x0);
                  }
              
                  function isTokenMappedAndIsErc721(address _token) public view returns (bool) {
                      require(isTokenMapped(_token), "TOKEN_NOT_MAPPED");
                      return isERC721[_token];
                  }
              
                  function isTokenMappedAndGetPredicate(address _token) public view returns (address) {
                      if (isTokenMappedAndIsErc721(_token)) {
                          return erc721Predicate;
                      }
                      return erc20Predicate;
                  }
              
                  function isChildTokenErc721(address childToken) public view returns (bool) {
                      address rootToken = childToRootToken[childToken];
                      require(rootToken != address(0x0), "Child token is not mapped");
                      return isERC721[rootToken];
                  }
              }
              
              // File: solidity-rlp/contracts/RLPReader.sol
              
              /*
              * @author Hamdi Allam [email protected]
              * Please reach out with any questions or concerns
              */
              pragma solidity ^0.5.0;
              
              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 item RLP encoded bytes
                  */
                  function rlpLen(RLPItem memory item) internal pure returns (uint) {
                      return item.len;
                  }
              
                  /*
                  * @param item RLP encoded bytes
                  */
                  function payloadLen(RLPItem memory item) internal pure returns (uint) {
                      return item.len - _payloadOffset(item.memPtr);
                  }
              
                  /*
                  * @param item RLP encoded list in bytes
                  */
                  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;
                      }
              
                      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;
                  }
              
                  /** 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 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))
                      }
              
                      return result == 0 ? false : 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 offset = _payloadOffset(item.memPtr);
                      uint len = item.len - offset;
              
                      uint result;
                      uint memPtr = item.memPtr + offset;
                      assembly {
                          result := mload(memPtr)
              
                          // shfit to the correct location if neccesary
                          if lt(len, 32) {
                              result := div(result, exp(256, sub(32, len)))
                          }
                      }
              
                      return result;
                  }
              
                  // enforces 32 byte length
                  function toUintStrict(RLPItem memory item) internal pure returns (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 offset = _payloadOffset(item.memPtr);
                      uint len = item.len - offset; // data length
                      bytes memory result = new bytes(len);
              
                      uint destPtr;
                      assembly {
                          destPtr := add(0x20, result)
                      }
              
                      copy(item.memPtr + offset, destPtr, len);
                      return result;
                  }
              
                  /*
                  * Private Helpers
                  */
              
                  // @return number of payload items inside an encoded list.
                  function numItems(RLPItem memory item) private pure returns (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;
                      }
              
                      // 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))
                      }
                  }
              }
              
              // File: openzeppelin-solidity/contracts/math/SafeMath.sol
              
              pragma solidity ^0.5.2;
              
              /**
               * @title SafeMath
               * @dev Unsigned math operations with safety checks that revert on error
               */
              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;
                  }
              }
              
              // File: openzeppelin-solidity/contracts/ownership/Ownable.sol
              
              pragma solidity ^0.5.2;
              
              /**
               * @title Ownable
               * @dev The Ownable contract has an owner address, and provides basic authorization control
               * functions, this simplifies the implementation of "user permissions".
               */
              contract Ownable {
                  address private _owner;
              
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
              
                  /**
                   * @dev The Ownable constructor sets the original `owner` of the contract to the sender
                   * account.
                   */
                  constructor () internal {
                      _owner = msg.sender;
                      emit OwnershipTransferred(address(0), _owner);
                  }
              
                  /**
                   * @return the address of the owner.
                   */
                  function owner() public view returns (address) {
                      return _owner;
                  }
              
                  /**
                   * @dev Throws if called by any account other than the owner.
                   */
                  modifier onlyOwner() {
                      require(isOwner());
                      _;
                  }
              
                  /**
                   * @return true if `msg.sender` is the owner of the contract.
                   */
                  function isOwner() public view returns (bool) {
                      return msg.sender == _owner;
                  }
              
                  /**
                   * @dev Allows the current owner to relinquish control of the contract.
                   * It will not be possible to call the functions with the `onlyOwner`
                   * modifier anymore.
                   * @notice Renouncing ownership will leave the contract without an owner,
                   * thereby removing any functionality that is only available to the owner.
                   */
                  function renounceOwnership() public onlyOwner {
                      emit OwnershipTransferred(_owner, address(0));
                      _owner = address(0);
                  }
              
                  /**
                   * @dev Allows the current owner to transfer control of the contract to a newOwner.
                   * @param newOwner The address to transfer ownership to.
                   */
                  function transferOwnership(address newOwner) public onlyOwner {
                      _transferOwnership(newOwner);
                  }
              
                  /**
                   * @dev Transfers control of the contract to a newOwner.
                   * @param newOwner The address to transfer ownership to.
                   */
                  function _transferOwnership(address newOwner) internal {
                      require(newOwner != address(0));
                      emit OwnershipTransferred(_owner, newOwner);
                      _owner = newOwner;
                  }
              }
              
              // File: contracts/common/misc/ProxyStorage.sol
              
              pragma solidity ^0.5.2;
              
              
              contract ProxyStorage is Ownable {
                  address internal proxyTo;
              }
              
              // File: contracts/common/mixin/ChainIdMixin.sol
              
              pragma solidity ^0.5.2;
              
              contract ChainIdMixin {
                bytes constant public networkId = hex"89";
                uint256 constant public CHAINID = 137;
              }
              
              // File: contracts/root/RootChainStorage.sol
              
              pragma solidity ^0.5.2;
              
              
              
              
              
              contract RootChainHeader {
                  event NewHeaderBlock(
                      address indexed proposer,
                      uint256 indexed headerBlockId,
                      uint256 indexed reward,
                      uint256 start,
                      uint256 end,
                      bytes32 root
                  );
                  // housekeeping event
                  event ResetHeaderBlock(address indexed proposer, uint256 indexed headerBlockId);
                  struct HeaderBlock {
                      bytes32 root;
                      uint256 start;
                      uint256 end;
                      uint256 createdAt;
                      address proposer;
                  }
              }
              
              
              contract RootChainStorage is ProxyStorage, RootChainHeader, ChainIdMixin {
                  bytes32 public heimdallId;
                  uint8 public constant VOTE_TYPE = 2;
              
                  uint16 internal constant MAX_DEPOSITS = 10000;
                  uint256 public _nextHeaderBlock = MAX_DEPOSITS;
                  uint256 internal _blockDepositId = 1;
                  mapping(uint256 => HeaderBlock) public headerBlocks;
                  Registry internal registry;
              }
              
              // File: contracts/staking/stakeManager/IStakeManager.sol
              
              pragma solidity ^0.5.2;
              
              
              contract IStakeManager {
                  // validator replacement
                  function startAuction(uint256 validatorId, uint256 amount) external;
              
                  function confirmAuctionBid(
                      uint256 validatorId,
                      uint256 heimdallFee,
                      bool acceptDelegation,
                      bytes calldata signerPubkey
                  ) external;
              
                  function transferFunds(
                      uint256 validatorId,
                      uint256 amount,
                      address delegator
                  ) external returns (bool);
              
                  function delegationDeposit(
                      uint256 validatorId,
                      uint256 amount,
                      address delegator
                  ) external returns (bool);
              
                  function stake(
                      uint256 amount,
                      uint256 heimdallFee,
                      bool acceptDelegation,
                      bytes calldata signerPubkey
                  ) external;
              
                  function unstake(uint256 validatorId) external;
              
                  function totalStakedFor(address addr) external view returns (uint256);
              
                  function supportsHistory() external pure returns (bool);
              
                  function stakeFor(
                      address user,
                      uint256 amount,
                      uint256 heimdallFee,
                      bool acceptDelegation,
                      bytes memory signerPubkey
                  ) public;
              
                  function checkSignatures(
                      uint256 blockInterval,
                      bytes32 voteHash,
                      bytes32 stateRoot,
                      address proposer,
                      bytes memory sigs
                  ) public returns (uint256);
              
                  function updateValidatorState(uint256 validatorId, int256 amount) public;
              
                  function ownerOf(uint256 tokenId) public view returns (address);
              
                  function slash(bytes memory slashingInfoList) public returns (uint256);
              
                  function validatorStake(uint256 validatorId) public view returns (uint256);
              
                  function epoch() public view returns (uint256);
              
                  function withdrawalDelay() public view returns (uint256);
              }
              
              // File: contracts/root/IRootChain.sol
              
              pragma solidity ^0.5.2;
              
              
              interface IRootChain {
                  function slash() external;
              
                  function submitHeaderBlock(bytes calldata data, bytes calldata sigs)
                      external;
              
                  function getLastChildBlock() external view returns (uint256);
              
                  function currentHeaderBlock() external view returns (uint256);
              }
              
              // File: contracts/root/RootChain.sol
              
              pragma solidity ^0.5.2;
              
              
              
              
              
              
              
              
              contract RootChain is RootChainStorage, IRootChain {
                  using SafeMath for uint256;
                  using RLPReader for bytes;
                  using RLPReader for RLPReader.RLPItem;
              
                  modifier onlyDepositManager() {
                      require(msg.sender == registry.getDepositManagerAddress(), "UNAUTHORIZED_DEPOSIT_MANAGER_ONLY");
                      _;
                  }
              
                  function submitHeaderBlock(bytes calldata data, bytes calldata sigs) external {
                      (address proposer, uint256 start, uint256 end, bytes32 rootHash, bytes32 accountHash, uint256 _borChainID) = abi
                          .decode(data, (address, uint256, uint256, bytes32, bytes32, uint256));
                      require(CHAINID == _borChainID, "Invalid bor chain id");
              
                      require(_buildHeaderBlock(proposer, start, end, rootHash), "INCORRECT_HEADER_DATA");
              
                      // check if it is better to keep it in local storage instead
                      IStakeManager stakeManager = IStakeManager(registry.getStakeManagerAddress());
                      uint256 _reward = stakeManager.checkSignatures(
                          end.sub(start).add(1),
                          /**  
                              prefix 01 to data 
                              01 represents positive vote on data and 00 is negative vote
                              malicious validator can try to send 2/3 on negative vote so 01 is appended
                           */
                          keccak256(abi.encodePacked(bytes(hex"01"), data)),
                          accountHash,
                          proposer,
                          sigs
                      );
              
                      require(_reward != 0, "Invalid checkpoint");
                      emit NewHeaderBlock(proposer, _nextHeaderBlock, _reward, start, end, rootHash);
                      _nextHeaderBlock = _nextHeaderBlock.add(MAX_DEPOSITS);
                      _blockDepositId = 1;
                  }
              
                  function updateDepositId(uint256 numDeposits) external onlyDepositManager returns (uint256 depositId) {
                      depositId = currentHeaderBlock().add(_blockDepositId);
                      // deposit ids will be (_blockDepositId, _blockDepositId + 1, .... _blockDepositId + numDeposits - 1)
                      _blockDepositId = _blockDepositId.add(numDeposits);
                      require(
                          // Since _blockDepositId is initialized to 1; only (MAX_DEPOSITS - 1) deposits per header block are allowed
                          _blockDepositId <= MAX_DEPOSITS,
                          "TOO_MANY_DEPOSITS"
                      );
                  }
              
                  function getLastChildBlock() external view returns (uint256) {
                      return headerBlocks[currentHeaderBlock()].end;
                  }
              
                  function slash() external {
                      //TODO: future implementation
                  }
              
                  function currentHeaderBlock() public view returns (uint256) {
                      return _nextHeaderBlock.sub(MAX_DEPOSITS);
                  }
              
                  function _buildHeaderBlock(
                      address proposer,
                      uint256 start,
                      uint256 end,
                      bytes32 rootHash
                  ) private returns (bool) {
                      uint256 nextChildBlock;
                      /*
                  The ID of the 1st header block is MAX_DEPOSITS.
                  if _nextHeaderBlock == MAX_DEPOSITS, then the first header block is yet to be submitted, hence nextChildBlock = 0
                  */
                      if (_nextHeaderBlock > MAX_DEPOSITS) {
                          nextChildBlock = headerBlocks[currentHeaderBlock()].end + 1;
                      }
                      if (nextChildBlock != start) {
                          return false;
                      }
              
                      HeaderBlock memory headerBlock = HeaderBlock({
                          root: rootHash,
                          start: nextChildBlock,
                          end: end,
                          createdAt: now,
                          proposer: proposer
                      });
              
                      headerBlocks[_nextHeaderBlock] = headerBlock;
                      return true;
                  }
              
                  // Housekeeping function. @todo remove later
                  function setNextHeaderBlock(uint256 _value) public onlyOwner {
                      require(_value % MAX_DEPOSITS == 0, "Invalid value");
                      for (uint256 i = _value; i < _nextHeaderBlock; i += MAX_DEPOSITS) {
                          delete headerBlocks[i];
                      }
                      _nextHeaderBlock = _value;
                      _blockDepositId = 1;
                      emit ResetHeaderBlock(msg.sender, _nextHeaderBlock);
                  }
              
                  // Housekeeping function. @todo remove later
                  function setHeimdallId(string memory _heimdallId) public onlyOwner {
                      heimdallId = keccak256(abi.encodePacked(_heimdallId));
                  }
              }
              
              // File: contracts/root/stateSyncer/StateSender.sol
              
              pragma solidity ^0.5.2;
              
              
              
              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: contracts/common/mixin/Lockable.sol
              
              pragma solidity ^0.5.2;
              
              
              contract Lockable is Governable {
                  bool public locked;
              
                  modifier onlyWhenUnlocked() {
                      require(!locked, "Is Locked");
                      _;
                  }
              
                  constructor(address _governance) public Governable(_governance) {}
              
                  function lock() external onlyGovernance {
                      locked = true;
                  }
              
                  function unlock() external onlyGovernance {
                      locked = false;
                  }
              }
              
              // File: contracts/root/depositManager/DepositManagerStorage.sol
              
              pragma solidity ^0.5.2;
              
              
              
              
              
              
              
              contract DepositManagerHeader {
                  event NewDepositBlock(address indexed owner, address indexed token, uint256 amountOrNFTId, uint256 depositBlockId);
                  event MaxErc20DepositUpdate(uint256 indexed oldLimit, uint256 indexed newLimit);
              
                  struct DepositBlock {
                      bytes32 depositHash;
                      uint256 createdAt;
                  }
              }
              
              
              contract DepositManagerStorage is ProxyStorage, Lockable, DepositManagerHeader {
                  Registry public registry;
                  RootChain public rootChain;
                  StateSender public stateSender;
              
                  mapping(uint256 => DepositBlock) public deposits;
              
                  address public childChain;
                  uint256 public maxErc20Deposit = 100 * (10**18);
              }
              
              // File: contracts/common/misc/ERCProxy.sol
              
              /*
               * SPDX-License-Identitifer:    MIT
               */
              
              pragma solidity ^0.5.2;
              
              // See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-897.md
              
              interface ERCProxy {
                  function proxyType() external pure returns (uint256 proxyTypeId);
                  function implementation() external view returns (address codeAddr);
              }
              
              // File: contracts/common/misc/DelegateProxy.sol
              
              pragma solidity ^0.5.2;
              
              
              
              contract DelegateProxy is ERCProxy {
                  function proxyType() external pure returns (uint256 proxyTypeId) {
                      // Upgradeable proxy
                      proxyTypeId = 2;
                  }
              
                  function implementation() external view returns (address);
              
                  function delegatedFwd(address _dst, bytes memory _calldata) internal {
                      // solium-disable-next-line security/no-inline-assembly
                      assembly {
                          let result := delegatecall(
                              sub(gas, 10000),
                              _dst,
                              add(_calldata, 0x20),
                              mload(_calldata),
                              0,
                              0
                          )
                          let size := returndatasize
              
                          let ptr := mload(0x40)
                          returndatacopy(ptr, 0, size)
              
                          // revert instead of invalid() bc if the underlying call failed with invalid() it already wasted gas.
                          // if the call returned error data, forward it
                          switch result
                              case 0 {
                                  revert(ptr, size)
                              }
                              default {
                                  return(ptr, size)
                              }
                      }
                  }
              }
              
              // File: contracts/common/misc/Proxy.sol
              
              pragma solidity ^0.5.2;
              
              
              
              
              contract Proxy is ProxyStorage, DelegateProxy {
                  event ProxyUpdated(address indexed _new, address indexed _old);
                  event OwnerUpdate(address _prevOwner, address _newOwner);
              
                  constructor(address _proxyTo) public {
                      updateImplementation(_proxyTo);
                  }
              
                  function() external payable {
                      // require(currentContract != 0, "If app code has not been set yet, do not call");
                      // Todo: filter out some calls or handle in the end fallback
                      delegatedFwd(proxyTo, msg.data);
                  }
              
                  function implementation() external view returns (address) {
                      return proxyTo;
                  }
              
                  function updateImplementation(address _newProxyTo) public onlyOwner {
                      require(_newProxyTo != address(0x0), "INVALID_PROXY_ADDRESS");
                      require(isContract(_newProxyTo), "DESTINATION_ADDRESS_IS_NOT_A_CONTRACT");
                      emit ProxyUpdated(_newProxyTo, proxyTo);
                      proxyTo = _newProxyTo;
                  }
              
                  function isContract(address _target) internal view returns (bool) {
                      if (_target == address(0)) {
                          return false;
                      }
              
                      uint256 size;
                      assembly {
                          size := extcodesize(_target)
                      }
                      return size > 0;
                  }
              }
              
              // File: contracts/root/depositManager/DepositManagerProxy.sol
              
              pragma solidity ^0.5.2;
              
              
              
              
              
              
              contract DepositManagerProxy is Proxy, DepositManagerStorage {
                  constructor(
                      address _proxyTo,
                      address _registry,
                      address _rootChain,
                      address _governance
                  ) public Proxy(_proxyTo) Lockable(_governance) {
                      registry = Registry(_registry);
                      rootChain = RootChain(_rootChain);
                  }
              }

              File 4 of 9: MaticToken
              pragma solidity 0.5.2;
              
              // File: openzeppelin-solidity/contracts/token/ERC20/IERC20.sol
              
              /**
               * @title ERC20 interface
               * @dev see https://github.com/ethereum/EIPs/issues/20
               */
              interface IERC20 {
                  function transfer(address to, uint256 value) external returns (bool);
              
                  function approve(address spender, uint256 value) external returns (bool);
              
                  function transferFrom(address from, address to, uint256 value) external returns (bool);
              
                  function totalSupply() external view returns (uint256);
              
                  function balanceOf(address who) external view returns (uint256);
              
                  function allowance(address owner, address spender) external view returns (uint256);
              
                  event Transfer(address indexed from, address indexed to, uint256 value);
              
                  event Approval(address indexed owner, address indexed spender, uint256 value);
              }
              
              // File: openzeppelin-solidity/contracts/math/SafeMath.sol
              
              /**
               * @title SafeMath
               * @dev Unsigned math operations with safety checks that revert on error
               */
              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;
                  }
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC20/ERC20.sol
              
              /**
               * @title Standard ERC20 token
               *
               * @dev Implementation of the basic standard token.
               * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
               * Originally based on code by FirstBlood:
               * https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
               *
               * This implementation emits additional Approval events, allowing applications to reconstruct the allowance status for
               * all accounts just by listening to said events. Note that this isn't required by the specification, and other
               * compliant implementations may not do it.
               */
              contract ERC20 is IERC20 {
                  using SafeMath for uint256;
              
                  mapping (address => uint256) private _balances;
              
                  mapping (address => mapping (address => uint256)) private _allowed;
              
                  uint256 private _totalSupply;
              
                  /**
                  * @dev Total number of tokens in existence
                  */
                  function totalSupply() public view returns (uint256) {
                      return _totalSupply;
                  }
              
                  /**
                  * @dev Gets the balance of the specified address.
                  * @param owner The address to query the balance of.
                  * @return An uint256 representing the amount owned by the passed address.
                  */
                  function balanceOf(address owner) public view returns (uint256) {
                      return _balances[owner];
                  }
              
                  /**
                   * @dev Function to check the amount of tokens that an owner allowed to a spender.
                   * @param owner address The address which owns the funds.
                   * @param spender address The address which will spend the funds.
                   * @return A uint256 specifying the amount of tokens still available for the spender.
                   */
                  function allowance(address owner, address spender) public view returns (uint256) {
                      return _allowed[owner][spender];
                  }
              
                  /**
                  * @dev Transfer token for a specified address
                  * @param to The address to transfer to.
                  * @param value The amount to be transferred.
                  */
                  function transfer(address to, uint256 value) public returns (bool) {
                      _transfer(msg.sender, to, value);
                      return true;
                  }
              
                  /**
                   * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
                   * 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
                   * @param spender The address which will spend the funds.
                   * @param value The amount of tokens to be spent.
                   */
                  function approve(address spender, uint256 value) public returns (bool) {
                      require(spender != address(0));
              
                      _allowed[msg.sender][spender] = value;
                      emit Approval(msg.sender, spender, value);
                      return true;
                  }
              
                  /**
                   * @dev Transfer tokens from one address to another.
                   * Note that while this function emits an Approval event, this is not required as per the specification,
                   * and other compliant implementations may not emit the event.
                   * @param from address The address which you want to send tokens from
                   * @param to address The address which you want to transfer to
                   * @param value uint256 the amount of tokens to be transferred
                   */
                  function transferFrom(address from, address to, uint256 value) public returns (bool) {
                      _allowed[from][msg.sender] = _allowed[from][msg.sender].sub(value);
                      _transfer(from, to, value);
                      emit Approval(from, msg.sender, _allowed[from][msg.sender]);
                      return true;
                  }
              
                  /**
                   * @dev Increase the amount of tokens that an owner allowed to a spender.
                   * approve should be called when allowed_[_spender] == 0. To increment
                   * allowed value is better to use this function to avoid 2 calls (and wait until
                   * the first transaction is mined)
                   * From MonolithDAO Token.sol
                   * Emits an Approval event.
                   * @param spender The address which will spend the funds.
                   * @param addedValue The amount of tokens to increase the allowance by.
                   */
                  function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
                      require(spender != address(0));
              
                      _allowed[msg.sender][spender] = _allowed[msg.sender][spender].add(addedValue);
                      emit Approval(msg.sender, spender, _allowed[msg.sender][spender]);
                      return true;
                  }
              
                  /**
                   * @dev Decrease the amount of tokens that an owner allowed to a spender.
                   * approve should be called when allowed_[_spender] == 0. To decrement
                   * allowed value is better to use this function to avoid 2 calls (and wait until
                   * the first transaction is mined)
                   * From MonolithDAO Token.sol
                   * Emits an Approval event.
                   * @param spender The address which will spend the funds.
                   * @param subtractedValue The amount of tokens to decrease the allowance by.
                   */
                  function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
                      require(spender != address(0));
              
                      _allowed[msg.sender][spender] = _allowed[msg.sender][spender].sub(subtractedValue);
                      emit Approval(msg.sender, spender, _allowed[msg.sender][spender]);
                      return true;
                  }
              
                  /**
                  * @dev Transfer token for a specified addresses
                  * @param from The address to transfer from.
                  * @param to The address to transfer to.
                  * @param value The amount to be transferred.
                  */
                  function _transfer(address from, address to, uint256 value) internal {
                      require(to != address(0));
              
                      _balances[from] = _balances[from].sub(value);
                      _balances[to] = _balances[to].add(value);
                      emit Transfer(from, to, value);
                  }
              
                  /**
                   * @dev Internal function that mints an amount of the token and assigns it to
                   * an account. This encapsulates the modification of balances such that the
                   * proper events are emitted.
                   * @param account The account that will receive the created tokens.
                   * @param value The amount that will be created.
                   */
                  function _mint(address account, uint256 value) internal {
                      require(account != address(0));
              
                      _totalSupply = _totalSupply.add(value);
                      _balances[account] = _balances[account].add(value);
                      emit Transfer(address(0), account, value);
                  }
              
                  /**
                   * @dev Internal function that burns an amount of the token of a given
                   * account.
                   * @param account The account whose tokens will be burnt.
                   * @param value The amount that will be burnt.
                   */
                  function _burn(address account, uint256 value) internal {
                      require(account != address(0));
              
                      _totalSupply = _totalSupply.sub(value);
                      _balances[account] = _balances[account].sub(value);
                      emit Transfer(account, address(0), value);
                  }
              
                  /**
                   * @dev Internal function that burns an amount of the token of a given
                   * account, deducting from the sender's allowance for said account. Uses the
                   * internal burn function.
                   * Emits an Approval event (reflecting the reduced allowance).
                   * @param account The account whose tokens will be burnt.
                   * @param value The amount that will be burnt.
                   */
                  function _burnFrom(address account, uint256 value) internal {
                      _allowed[account][msg.sender] = _allowed[account][msg.sender].sub(value);
                      _burn(account, value);
                      emit Approval(account, msg.sender, _allowed[account][msg.sender]);
                  }
              }
              
              // File: openzeppelin-solidity/contracts/access/Roles.sol
              
              /**
               * @title Roles
               * @dev Library for managing addresses assigned to a Role.
               */
              library Roles {
                  struct Role {
                      mapping (address => bool) bearer;
                  }
              
                  /**
                   * @dev give an account access to this role
                   */
                  function add(Role storage role, address account) internal {
                      require(account != address(0));
                      require(!has(role, account));
              
                      role.bearer[account] = true;
                  }
              
                  /**
                   * @dev remove an account's access to this role
                   */
                  function remove(Role storage role, address account) internal {
                      require(account != address(0));
                      require(has(role, account));
              
                      role.bearer[account] = false;
                  }
              
                  /**
                   * @dev check if an account has this role
                   * @return bool
                   */
                  function has(Role storage role, address account) internal view returns (bool) {
                      require(account != address(0));
                      return role.bearer[account];
                  }
              }
              
              // File: openzeppelin-solidity/contracts/access/roles/PauserRole.sol
              
              contract PauserRole {
                  using Roles for Roles.Role;
              
                  event PauserAdded(address indexed account);
                  event PauserRemoved(address indexed account);
              
                  Roles.Role private _pausers;
              
                  constructor () internal {
                      _addPauser(msg.sender);
                  }
              
                  modifier onlyPauser() {
                      require(isPauser(msg.sender));
                      _;
                  }
              
                  function isPauser(address account) public view returns (bool) {
                      return _pausers.has(account);
                  }
              
                  function addPauser(address account) public onlyPauser {
                      _addPauser(account);
                  }
              
                  function renouncePauser() public {
                      _removePauser(msg.sender);
                  }
              
                  function _addPauser(address account) internal {
                      _pausers.add(account);
                      emit PauserAdded(account);
                  }
              
                  function _removePauser(address account) internal {
                      _pausers.remove(account);
                      emit PauserRemoved(account);
                  }
              }
              
              // File: openzeppelin-solidity/contracts/lifecycle/Pausable.sol
              
              /**
               * @title Pausable
               * @dev Base contract which allows children to implement an emergency stop mechanism.
               */
              contract Pausable is PauserRole {
                  event Paused(address account);
                  event Unpaused(address account);
              
                  bool private _paused;
              
                  constructor () internal {
                      _paused = false;
                  }
              
                  /**
                   * @return true if the contract is paused, false otherwise.
                   */
                  function paused() public view returns (bool) {
                      return _paused;
                  }
              
                  /**
                   * @dev Modifier to make a function callable only when the contract is not paused.
                   */
                  modifier whenNotPaused() {
                      require(!_paused);
                      _;
                  }
              
                  /**
                   * @dev Modifier to make a function callable only when the contract is paused.
                   */
                  modifier whenPaused() {
                      require(_paused);
                      _;
                  }
              
                  /**
                   * @dev called by the owner to pause, triggers stopped state
                   */
                  function pause() public onlyPauser whenNotPaused {
                      _paused = true;
                      emit Paused(msg.sender);
                  }
              
                  /**
                   * @dev called by the owner to unpause, returns to normal state
                   */
                  function unpause() public onlyPauser whenPaused {
                      _paused = false;
                      emit Unpaused(msg.sender);
                  }
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC20/ERC20Pausable.sol
              
              /**
               * @title Pausable token
               * @dev ERC20 modified with pausable transfers.
               **/
              contract ERC20Pausable is ERC20, Pausable {
                  function transfer(address to, uint256 value) public whenNotPaused returns (bool) {
                      return super.transfer(to, value);
                  }
              
                  function transferFrom(address from, address to, uint256 value) public whenNotPaused returns (bool) {
                      return super.transferFrom(from, to, value);
                  }
              
                  function approve(address spender, uint256 value) public whenNotPaused returns (bool) {
                      return super.approve(spender, value);
                  }
              
                  function increaseAllowance(address spender, uint addedValue) public whenNotPaused returns (bool success) {
                      return super.increaseAllowance(spender, addedValue);
                  }
              
                  function decreaseAllowance(address spender, uint subtractedValue) public whenNotPaused returns (bool success) {
                      return super.decreaseAllowance(spender, subtractedValue);
                  }
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol
              
              /**
               * @title ERC20Detailed token
               * @dev The decimals are only for visualization purposes.
               * All the operations are done using the smallest and indivisible token unit,
               * just as on Ethereum all the operations are done in wei.
               */
              contract ERC20Detailed is IERC20 {
                  string private _name;
                  string private _symbol;
                  uint8 private _decimals;
              
                  constructor (string memory name, string memory symbol, uint8 decimals) public {
                      _name = name;
                      _symbol = symbol;
                      _decimals = decimals;
                  }
              
                  /**
                   * @return the name of the token.
                   */
                  function name() public view returns (string memory) {
                      return _name;
                  }
              
                  /**
                   * @return the symbol of the token.
                   */
                  function symbol() public view returns (string memory) {
                      return _symbol;
                  }
              
                  /**
                   * @return the number of decimals of the token.
                   */
                  function decimals() public view returns (uint8) {
                      return _decimals;
                  }
              }
              
              // File: contracts/MaticToken.sol
              
              contract MaticToken is ERC20Pausable, ERC20Detailed {
                  constructor (string memory name, string memory symbol, uint8 decimals, uint256 totalSupply)
                  public
                  ERC20Detailed (name, symbol, decimals) {
                      _mint(msg.sender, totalSupply);
                  }
              }

              File 5 of 9: WithdrawManager
              // File: openzeppelin-solidity/contracts/token/ERC20/IERC20.sol
              
              pragma solidity ^0.5.2;
              
              /**
               * @title ERC20 interface
               * @dev see https://eips.ethereum.org/EIPS/eip-20
               */
              interface IERC20 {
                  function transfer(address to, uint256 value) external returns (bool);
              
                  function approve(address spender, uint256 value) external returns (bool);
              
                  function transferFrom(address from, address to, uint256 value) external returns (bool);
              
                  function totalSupply() external view returns (uint256);
              
                  function balanceOf(address who) external view returns (uint256);
              
                  function allowance(address owner, address spender) external view returns (uint256);
              
                  event Transfer(address indexed from, address indexed to, uint256 value);
              
                  event Approval(address indexed owner, address indexed spender, uint256 value);
              }
              
              // File: openzeppelin-solidity/contracts/math/SafeMath.sol
              
              pragma solidity ^0.5.2;
              
              /**
               * @title SafeMath
               * @dev Unsigned math operations with safety checks that revert on error
               */
              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;
                  }
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC20/ERC20.sol
              
              pragma solidity ^0.5.2;
              
              
              
              /**
               * @title Standard ERC20 token
               *
               * @dev Implementation of the basic standard token.
               * https://eips.ethereum.org/EIPS/eip-20
               * Originally based on code by FirstBlood:
               * https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
               *
               * This implementation emits additional Approval events, allowing applications to reconstruct the allowance status for
               * all accounts just by listening to said events. Note that this isn't required by the specification, and other
               * compliant implementations may not do it.
               */
              contract ERC20 is IERC20 {
                  using SafeMath for uint256;
              
                  mapping (address => uint256) private _balances;
              
                  mapping (address => mapping (address => uint256)) private _allowed;
              
                  uint256 private _totalSupply;
              
                  /**
                   * @dev Total number of tokens in existence
                   */
                  function totalSupply() public view returns (uint256) {
                      return _totalSupply;
                  }
              
                  /**
                   * @dev Gets the balance of the specified address.
                   * @param owner The address to query the balance of.
                   * @return A uint256 representing the amount owned by the passed address.
                   */
                  function balanceOf(address owner) public view returns (uint256) {
                      return _balances[owner];
                  }
              
                  /**
                   * @dev Function to check the amount of tokens that an owner allowed to a spender.
                   * @param owner address The address which owns the funds.
                   * @param spender address The address which will spend the funds.
                   * @return A uint256 specifying the amount of tokens still available for the spender.
                   */
                  function allowance(address owner, address spender) public view returns (uint256) {
                      return _allowed[owner][spender];
                  }
              
                  /**
                   * @dev Transfer token to a specified address
                   * @param to The address to transfer to.
                   * @param value The amount to be transferred.
                   */
                  function transfer(address to, uint256 value) public returns (bool) {
                      _transfer(msg.sender, to, value);
                      return true;
                  }
              
                  /**
                   * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
                   * 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
                   * @param spender The address which will spend the funds.
                   * @param value The amount of tokens to be spent.
                   */
                  function approve(address spender, uint256 value) public returns (bool) {
                      _approve(msg.sender, spender, value);
                      return true;
                  }
              
                  /**
                   * @dev Transfer tokens from one address to another.
                   * Note that while this function emits an Approval event, this is not required as per the specification,
                   * and other compliant implementations may not emit the event.
                   * @param from address The address which you want to send tokens from
                   * @param to address The address which you want to transfer to
                   * @param value uint256 the amount of tokens to be transferred
                   */
                  function transferFrom(address from, address to, uint256 value) public returns (bool) {
                      _transfer(from, to, value);
                      _approve(from, msg.sender, _allowed[from][msg.sender].sub(value));
                      return true;
                  }
              
                  /**
                   * @dev Increase the amount of tokens that an owner allowed to a spender.
                   * approve should be called when _allowed[msg.sender][spender] == 0. To increment
                   * allowed value is better to use this function to avoid 2 calls (and wait until
                   * the first transaction is mined)
                   * From MonolithDAO Token.sol
                   * Emits an Approval event.
                   * @param spender The address which will spend the funds.
                   * @param addedValue The amount of tokens to increase the allowance by.
                   */
                  function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
                      _approve(msg.sender, spender, _allowed[msg.sender][spender].add(addedValue));
                      return true;
                  }
              
                  /**
                   * @dev Decrease the amount of tokens that an owner allowed to a spender.
                   * approve should be called when _allowed[msg.sender][spender] == 0. To decrement
                   * allowed value is better to use this function to avoid 2 calls (and wait until
                   * the first transaction is mined)
                   * From MonolithDAO Token.sol
                   * Emits an Approval event.
                   * @param spender The address which will spend the funds.
                   * @param subtractedValue The amount of tokens to decrease the allowance by.
                   */
                  function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
                      _approve(msg.sender, spender, _allowed[msg.sender][spender].sub(subtractedValue));
                      return true;
                  }
              
                  /**
                   * @dev Transfer token for a specified addresses
                   * @param from The address to transfer from.
                   * @param to The address to transfer to.
                   * @param value The amount to be transferred.
                   */
                  function _transfer(address from, address to, uint256 value) internal {
                      require(to != address(0));
              
                      _balances[from] = _balances[from].sub(value);
                      _balances[to] = _balances[to].add(value);
                      emit Transfer(from, to, value);
                  }
              
                  /**
                   * @dev Internal function that mints an amount of the token and assigns it to
                   * an account. This encapsulates the modification of balances such that the
                   * proper events are emitted.
                   * @param account The account that will receive the created tokens.
                   * @param value The amount that will be created.
                   */
                  function _mint(address account, uint256 value) internal {
                      require(account != address(0));
              
                      _totalSupply = _totalSupply.add(value);
                      _balances[account] = _balances[account].add(value);
                      emit Transfer(address(0), account, value);
                  }
              
                  /**
                   * @dev Internal function that burns an amount of the token of a given
                   * account.
                   * @param account The account whose tokens will be burnt.
                   * @param value The amount that will be burnt.
                   */
                  function _burn(address account, uint256 value) internal {
                      require(account != address(0));
              
                      _totalSupply = _totalSupply.sub(value);
                      _balances[account] = _balances[account].sub(value);
                      emit Transfer(account, address(0), value);
                  }
              
                  /**
                   * @dev Approve an address to spend another addresses' tokens.
                   * @param owner The address that owns the tokens.
                   * @param spender The address that will spend the tokens.
                   * @param value The number of tokens that can be spent.
                   */
                  function _approve(address owner, address spender, uint256 value) internal {
                      require(spender != address(0));
                      require(owner != address(0));
              
                      _allowed[owner][spender] = value;
                      emit Approval(owner, spender, value);
                  }
              
                  /**
                   * @dev Internal function that burns an amount of the token of a given
                   * account, deducting from the sender's allowance for said account. Uses the
                   * internal burn function.
                   * Emits an Approval event (reflecting the reduced allowance).
                   * @param account The account whose tokens will be burnt.
                   * @param value The amount that will be burnt.
                   */
                  function _burnFrom(address account, uint256 value) internal {
                      _burn(account, value);
                      _approve(account, msg.sender, _allowed[account][msg.sender].sub(value));
                  }
              }
              
              // File: openzeppelin-solidity/contracts/introspection/IERC165.sol
              
              pragma solidity ^0.5.2;
              
              /**
               * @title IERC165
               * @dev https://eips.ethereum.org/EIPS/eip-165
               */
              interface IERC165 {
                  /**
                   * @notice Query if a contract implements an interface
                   * @param interfaceId The interface identifier, as specified in ERC-165
                   * @dev Interface identification is specified in ERC-165. This function
                   * uses less than 30,000 gas.
                   */
                  function supportsInterface(bytes4 interfaceId) external view returns (bool);
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC721/IERC721.sol
              
              pragma solidity ^0.5.2;
              
              
              /**
               * @title ERC721 Non-Fungible Token Standard basic interface
               * @dev see https://eips.ethereum.org/EIPS/eip-721
               */
              contract IERC721 is IERC165 {
                  event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
                  event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
                  event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
              
                  function balanceOf(address owner) public view returns (uint256 balance);
                  function ownerOf(uint256 tokenId) public view returns (address owner);
              
                  function approve(address to, uint256 tokenId) public;
                  function getApproved(uint256 tokenId) public view returns (address operator);
              
                  function setApprovalForAll(address operator, bool _approved) public;
                  function isApprovedForAll(address owner, address operator) public view returns (bool);
              
                  function transferFrom(address from, address to, uint256 tokenId) public;
                  function safeTransferFrom(address from, address to, uint256 tokenId) public;
              
                  function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public;
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC721/IERC721Receiver.sol
              
              pragma solidity ^0.5.2;
              
              /**
               * @title ERC721 token receiver interface
               * @dev Interface for any contract that wants to support safeTransfers
               * from ERC721 asset contracts.
               */
              contract IERC721Receiver {
                  /**
                   * @notice Handle the receipt of an NFT
                   * @dev The ERC721 smart contract calls this function on the recipient
                   * after a `safeTransfer`. This function MUST return the function selector,
                   * otherwise the caller will revert the transaction. The selector to be
                   * returned can be obtained as `this.onERC721Received.selector`. This
                   * function MAY throw to revert and reject the transfer.
                   * Note: the ERC721 contract address is always the message sender.
                   * @param operator The address which called `safeTransferFrom` function
                   * @param from The address which previously owned the token
                   * @param tokenId The NFT identifier which is being transferred
                   * @param data Additional data with no specified format
                   * @return bytes4 `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
                   */
                  function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data)
                  public returns (bytes4);
              }
              
              // File: openzeppelin-solidity/contracts/utils/Address.sol
              
              pragma solidity ^0.5.2;
              
              /**
               * Utility library of inline functions on addresses
               */
              library Address {
                  /**
                   * Returns whether the target address is a contract
                   * @dev This function will return false if invoked during the constructor of a contract,
                   * as the code is not actually created until after the constructor finishes.
                   * @param account address of the account to check
                   * @return whether the target address is a contract
                   */
                  function isContract(address account) internal view returns (bool) {
                      uint256 size;
                      // XXX Currently there is no better way to check if there is a contract in an address
                      // than to check the size of the code at that address.
                      // See https://ethereum.stackexchange.com/a/14016/36603
                      // for more details about how this works.
                      // TODO Check this again before the Serenity release, because all addresses will be
                      // contracts then.
                      // solhint-disable-next-line no-inline-assembly
                      assembly { size := extcodesize(account) }
                      return size > 0;
                  }
              }
              
              // File: openzeppelin-solidity/contracts/drafts/Counters.sol
              
              pragma solidity ^0.5.2;
              
              
              /**
               * @title Counters
               * @author Matt Condon (@shrugs)
               * @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number
               * of elements in a mapping, issuing ERC721 ids, or counting request ids
               *
               * Include with `using Counters for Counters.Counter;`
               * Since it is not possible to overflow a 256 bit integer with increments of one, `increment` can skip the SafeMath
               * overflow check, thereby saving gas. This does assume however correct usage, in that the underlying `_value` is never
               * directly accessed.
               */
              library Counters {
                  using SafeMath for uint256;
              
                  struct Counter {
                      // This variable should never be directly accessed by users of the library: interactions must be restricted to
                      // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
                      // this feature: see https://github.com/ethereum/solidity/issues/4637
                      uint256 _value; // default: 0
                  }
              
                  function current(Counter storage counter) internal view returns (uint256) {
                      return counter._value;
                  }
              
                  function increment(Counter storage counter) internal {
                      counter._value += 1;
                  }
              
                  function decrement(Counter storage counter) internal {
                      counter._value = counter._value.sub(1);
                  }
              }
              
              // File: openzeppelin-solidity/contracts/introspection/ERC165.sol
              
              pragma solidity ^0.5.2;
              
              
              /**
               * @title ERC165
               * @author Matt Condon (@shrugs)
               * @dev Implements ERC165 using a lookup table.
               */
              contract ERC165 is IERC165 {
                  bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
                  /*
                   * 0x01ffc9a7 ===
                   *     bytes4(keccak256('supportsInterface(bytes4)'))
                   */
              
                  /**
                   * @dev a mapping of interface id to whether or not it's supported
                   */
                  mapping(bytes4 => bool) private _supportedInterfaces;
              
                  /**
                   * @dev A contract implementing SupportsInterfaceWithLookup
                   * implement ERC165 itself
                   */
                  constructor () internal {
                      _registerInterface(_INTERFACE_ID_ERC165);
                  }
              
                  /**
                   * @dev implement supportsInterface(bytes4) using a lookup table
                   */
                  function supportsInterface(bytes4 interfaceId) external view returns (bool) {
                      return _supportedInterfaces[interfaceId];
                  }
              
                  /**
                   * @dev internal method for registering an interface
                   */
                  function _registerInterface(bytes4 interfaceId) internal {
                      require(interfaceId != 0xffffffff);
                      _supportedInterfaces[interfaceId] = true;
                  }
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC721/ERC721.sol
              
              pragma solidity ^0.5.2;
              
              
              
              
              
              
              
              /**
               * @title ERC721 Non-Fungible Token Standard basic implementation
               * @dev see https://eips.ethereum.org/EIPS/eip-721
               */
              contract ERC721 is ERC165, IERC721 {
                  using SafeMath for uint256;
                  using Address for address;
                  using Counters for Counters.Counter;
              
                  // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
                  // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector`
                  bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
              
                  // Mapping from token ID to owner
                  mapping (uint256 => address) private _tokenOwner;
              
                  // Mapping from token ID to approved address
                  mapping (uint256 => address) private _tokenApprovals;
              
                  // Mapping from owner to number of owned token
                  mapping (address => Counters.Counter) private _ownedTokensCount;
              
                  // Mapping from owner to operator approvals
                  mapping (address => mapping (address => bool)) private _operatorApprovals;
              
                  bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
                  /*
                   * 0x80ac58cd ===
                   *     bytes4(keccak256('balanceOf(address)')) ^
                   *     bytes4(keccak256('ownerOf(uint256)')) ^
                   *     bytes4(keccak256('approve(address,uint256)')) ^
                   *     bytes4(keccak256('getApproved(uint256)')) ^
                   *     bytes4(keccak256('setApprovalForAll(address,bool)')) ^
                   *     bytes4(keccak256('isApprovedForAll(address,address)')) ^
                   *     bytes4(keccak256('transferFrom(address,address,uint256)')) ^
                   *     bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^
                   *     bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)'))
                   */
              
                  constructor () public {
                      // register the supported interfaces to conform to ERC721 via ERC165
                      _registerInterface(_INTERFACE_ID_ERC721);
                  }
              
                  /**
                   * @dev Gets the balance of the specified address
                   * @param owner address to query the balance of
                   * @return uint256 representing the amount owned by the passed address
                   */
                  function balanceOf(address owner) public view returns (uint256) {
                      require(owner != address(0));
                      return _ownedTokensCount[owner].current();
                  }
              
                  /**
                   * @dev Gets the owner of the specified token ID
                   * @param tokenId uint256 ID of the token to query the owner of
                   * @return address currently marked as the owner of the given token ID
                   */
                  function ownerOf(uint256 tokenId) public view returns (address) {
                      address owner = _tokenOwner[tokenId];
                      require(owner != address(0));
                      return owner;
                  }
              
                  /**
                   * @dev Approves another address to transfer the given token ID
                   * The zero address indicates there is no approved address.
                   * There can only be one approved address per token at a given time.
                   * Can only be called by the token owner or an approved operator.
                   * @param to address to be approved for the given token ID
                   * @param tokenId uint256 ID of the token to be approved
                   */
                  function approve(address to, uint256 tokenId) public {
                      address owner = ownerOf(tokenId);
                      require(to != owner);
                      require(msg.sender == owner || isApprovedForAll(owner, msg.sender));
              
                      _tokenApprovals[tokenId] = to;
                      emit Approval(owner, to, tokenId);
                  }
              
                  /**
                   * @dev Gets the approved address for a token ID, or zero if no address set
                   * Reverts if the token ID does not exist.
                   * @param tokenId uint256 ID of the token to query the approval of
                   * @return address currently approved for the given token ID
                   */
                  function getApproved(uint256 tokenId) public view returns (address) {
                      require(_exists(tokenId));
                      return _tokenApprovals[tokenId];
                  }
              
                  /**
                   * @dev Sets or unsets the approval of a given operator
                   * An operator is allowed to transfer all tokens of the sender on their behalf
                   * @param to operator address to set the approval
                   * @param approved representing the status of the approval to be set
                   */
                  function setApprovalForAll(address to, bool approved) public {
                      require(to != msg.sender);
                      _operatorApprovals[msg.sender][to] = approved;
                      emit ApprovalForAll(msg.sender, to, approved);
                  }
              
                  /**
                   * @dev Tells whether an operator is approved by a given owner
                   * @param owner owner address which you want to query the approval of
                   * @param operator operator address which you want to query the approval of
                   * @return bool whether the given operator is approved by the given owner
                   */
                  function isApprovedForAll(address owner, address operator) public view returns (bool) {
                      return _operatorApprovals[owner][operator];
                  }
              
                  /**
                   * @dev Transfers the ownership of a given token ID to another address
                   * Usage of this method is discouraged, use `safeTransferFrom` whenever possible
                   * Requires the msg.sender to be the owner, approved, or operator
                   * @param from current owner of the token
                   * @param to address to receive the ownership of the given token ID
                   * @param tokenId uint256 ID of the token to be transferred
                   */
                  function transferFrom(address from, address to, uint256 tokenId) public {
                      require(_isApprovedOrOwner(msg.sender, tokenId));
              
                      _transferFrom(from, to, tokenId);
                  }
              
                  /**
                   * @dev Safely transfers the ownership of a given token ID to another address
                   * If the target address is a contract, it must implement `onERC721Received`,
                   * which is called upon a safe transfer, and return the magic value
                   * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
                   * the transfer is reverted.
                   * Requires the msg.sender to be the owner, approved, or operator
                   * @param from current owner of the token
                   * @param to address to receive the ownership of the given token ID
                   * @param tokenId uint256 ID of the token to be transferred
                   */
                  function safeTransferFrom(address from, address to, uint256 tokenId) public {
                      safeTransferFrom(from, to, tokenId, "");
                  }
              
                  /**
                   * @dev Safely transfers the ownership of a given token ID to another address
                   * If the target address is a contract, it must implement `onERC721Received`,
                   * which is called upon a safe transfer, and return the magic value
                   * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
                   * the transfer is reverted.
                   * Requires the msg.sender to be the owner, approved, or operator
                   * @param from current owner of the token
                   * @param to address to receive the ownership of the given token ID
                   * @param tokenId uint256 ID of the token to be transferred
                   * @param _data bytes data to send along with a safe transfer check
                   */
                  function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public {
                      transferFrom(from, to, tokenId);
                      require(_checkOnERC721Received(from, to, tokenId, _data));
                  }
              
                  /**
                   * @dev Returns whether the specified token exists
                   * @param tokenId uint256 ID of the token to query the existence of
                   * @return bool whether the token exists
                   */
                  function _exists(uint256 tokenId) internal view returns (bool) {
                      address owner = _tokenOwner[tokenId];
                      return owner != address(0);
                  }
              
                  /**
                   * @dev Returns whether the given spender can transfer a given token ID
                   * @param spender address of the spender to query
                   * @param tokenId uint256 ID of the token to be transferred
                   * @return bool whether the msg.sender is approved for the given token ID,
                   * is an operator of the owner, or is the owner of the token
                   */
                  function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
                      address owner = ownerOf(tokenId);
                      return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
                  }
              
                  /**
                   * @dev Internal function to mint a new token
                   * Reverts if the given token ID already exists
                   * @param to The address that will own the minted token
                   * @param tokenId uint256 ID of the token to be minted
                   */
                  function _mint(address to, uint256 tokenId) internal {
                      require(to != address(0));
                      require(!_exists(tokenId));
              
                      _tokenOwner[tokenId] = to;
                      _ownedTokensCount[to].increment();
              
                      emit Transfer(address(0), to, tokenId);
                  }
              
                  /**
                   * @dev Internal function to burn a specific token
                   * Reverts if the token does not exist
                   * Deprecated, use _burn(uint256) instead.
                   * @param owner owner of the token to burn
                   * @param tokenId uint256 ID of the token being burned
                   */
                  function _burn(address owner, uint256 tokenId) internal {
                      require(ownerOf(tokenId) == owner);
              
                      _clearApproval(tokenId);
              
                      _ownedTokensCount[owner].decrement();
                      _tokenOwner[tokenId] = address(0);
              
                      emit Transfer(owner, address(0), tokenId);
                  }
              
                  /**
                   * @dev Internal function to burn a specific token
                   * Reverts if the token does not exist
                   * @param tokenId uint256 ID of the token being burned
                   */
                  function _burn(uint256 tokenId) internal {
                      _burn(ownerOf(tokenId), tokenId);
                  }
              
                  /**
                   * @dev Internal function to transfer ownership of a given token ID to another address.
                   * As opposed to transferFrom, this imposes no restrictions on msg.sender.
                   * @param from current owner of the token
                   * @param to address to receive the ownership of the given token ID
                   * @param tokenId uint256 ID of the token to be transferred
                   */
                  function _transferFrom(address from, address to, uint256 tokenId) internal {
                      require(ownerOf(tokenId) == from);
                      require(to != address(0));
              
                      _clearApproval(tokenId);
              
                      _ownedTokensCount[from].decrement();
                      _ownedTokensCount[to].increment();
              
                      _tokenOwner[tokenId] = to;
              
                      emit Transfer(from, to, tokenId);
                  }
              
                  /**
                   * @dev Internal function to invoke `onERC721Received` on a target address
                   * The call is not executed if the target address is not a contract
                   * @param from address representing the previous owner of the given token ID
                   * @param to target address that will receive the tokens
                   * @param tokenId uint256 ID of the token to be transferred
                   * @param _data bytes optional data to send along with the call
                   * @return bool whether the call correctly returned the expected magic value
                   */
                  function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)
                      internal returns (bool)
                  {
                      if (!to.isContract()) {
                          return true;
                      }
              
                      bytes4 retval = IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, _data);
                      return (retval == _ERC721_RECEIVED);
                  }
              
                  /**
                   * @dev Private function to clear current approval of a given token ID
                   * @param tokenId uint256 ID of the token to be transferred
                   */
                  function _clearApproval(uint256 tokenId) private {
                      if (_tokenApprovals[tokenId] != address(0)) {
                          _tokenApprovals[tokenId] = address(0);
                      }
                  }
              }
              
              // File: openzeppelin-solidity/contracts/math/Math.sol
              
              pragma solidity ^0.5.2;
              
              /**
               * @title Math
               * @dev Assorted math operations
               */
              library Math {
                  /**
                   * @dev Returns the largest of two numbers.
                   */
                  function max(uint256 a, uint256 b) internal pure returns (uint256) {
                      return a >= b ? a : b;
                  }
              
                  /**
                   * @dev Returns the smallest of two numbers.
                   */
                  function min(uint256 a, uint256 b) internal pure returns (uint256) {
                      return a < b ? a : b;
                  }
              
                  /**
                   * @dev Calculates the average of two numbers. Since these are integers,
                   * averages of an even and odd number cannot be represented, and will be
                   * rounded down.
                   */
                  function average(uint256 a, uint256 b) internal pure returns (uint256) {
                      // (a + b) / 2 can overflow, so we distribute
                      return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
                  }
              }
              
              // File: solidity-rlp/contracts/RLPReader.sol
              
              /*
              * @author Hamdi Allam [email protected]
              * Please reach out with any questions or concerns
              */
              pragma solidity ^0.5.0;
              
              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 item RLP encoded bytes
                  */
                  function rlpLen(RLPItem memory item) internal pure returns (uint) {
                      return item.len;
                  }
              
                  /*
                  * @param item RLP encoded bytes
                  */
                  function payloadLen(RLPItem memory item) internal pure returns (uint) {
                      return item.len - _payloadOffset(item.memPtr);
                  }
              
                  /*
                  * @param item RLP encoded list in bytes
                  */
                  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;
                      }
              
                      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;
                  }
              
                  /** 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 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))
                      }
              
                      return result == 0 ? false : 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 offset = _payloadOffset(item.memPtr);
                      uint len = item.len - offset;
              
                      uint result;
                      uint memPtr = item.memPtr + offset;
                      assembly {
                          result := mload(memPtr)
              
                          // shfit to the correct location if neccesary
                          if lt(len, 32) {
                              result := div(result, exp(256, sub(32, len)))
                          }
                      }
              
                      return result;
                  }
              
                  // enforces 32 byte length
                  function toUintStrict(RLPItem memory item) internal pure returns (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 offset = _payloadOffset(item.memPtr);
                      uint len = item.len - offset; // data length
                      bytes memory result = new bytes(len);
              
                      uint destPtr;
                      assembly {
                          destPtr := add(0x20, result)
                      }
              
                      copy(item.memPtr + offset, destPtr, len);
                      return result;
                  }
              
                  /*
                  * Private Helpers
                  */
              
                  // @return number of payload items inside an encoded list.
                  function numItems(RLPItem memory item) private pure returns (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;
                      }
              
                      // 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))
                      }
                  }
              }
              
              // File: contracts/common/lib/Merkle.sol
              
              pragma solidity ^0.5.2;
              
              library Merkle {
                  function checkMembership(
                      bytes32 leaf,
                      uint256 index,
                      bytes32 rootHash,
                      bytes memory proof
                  ) internal pure returns (bool) {
                      require(proof.length % 32 == 0, "Invalid proof length");
                      uint256 proofHeight = proof.length / 32;
                      // Proof of size n means, height of the tree is n+1.
                      // In a tree of height n+1, max #leafs possible is 2 ^ n
                      require(index < 2 ** proofHeight, "Leaf index is too big");
              
                      bytes32 proofElement;
                      bytes32 computedHash = leaf;
                      for (uint256 i = 32; i <= proof.length; i += 32) {
                          assembly {
                              proofElement := mload(add(proof, i))
                          }
              
                          if (index % 2 == 0) {
                              computedHash = keccak256(
                                  abi.encodePacked(computedHash, proofElement)
                              );
                          } else {
                              computedHash = keccak256(
                                  abi.encodePacked(proofElement, computedHash)
                              );
                          }
              
                          index = index / 2;
                      }
                      return computedHash == rootHash;
                  }
              }
              
              // File: contracts/common/lib/MerklePatriciaProof.sol
              
              /*
               * @title MerklePatriciaVerifier
               * @author Sam Mayo ([email protected])
               *
               * @dev Library for verifing merkle patricia proofs.
               */
              pragma solidity ^0.5.2;
              
              
              library MerklePatriciaProof {
                  /*
                 * @dev Verifies a merkle patricia proof.
                 * @param value The terminating value in the trie.
                 * @param encodedPath The path in the trie leading to value.
                 * @param rlpParentNodes The rlp encoded stack of nodes.
                 * @param root The root hash of the trie.
                 * @return The boolean validity of the proof.
                 */
                  function verify(
                      bytes memory value,
                      bytes memory encodedPath,
                      bytes memory rlpParentNodes,
                      bytes32 root
                  ) internal pure returns (bool) {
                      RLPReader.RLPItem memory item = RLPReader.toRlpItem(rlpParentNodes);
                      RLPReader.RLPItem[] memory parentNodes = RLPReader.toList(item);
              
                      bytes memory currentNode;
                      RLPReader.RLPItem[] memory currentNodeList;
              
                      bytes32 nodeKey = root;
                      uint256 pathPtr = 0;
              
                      bytes memory path = _getNibbleArray(encodedPath);
                      if (path.length == 0) {
                          return false;
                      }
              
                      for (uint256 i = 0; i < parentNodes.length; i++) {
                          if (pathPtr > path.length) {
                              return false;
                          }
              
                          currentNode = RLPReader.toRlpBytes(parentNodes[i]);
                          if (nodeKey != keccak256(currentNode)) {
                              return false;
                          }
                          currentNodeList = RLPReader.toList(parentNodes[i]);
              
                          if (currentNodeList.length == 17) {
                              if (pathPtr == path.length) {
                                  if (
                                      keccak256(RLPReader.toBytes(currentNodeList[16])) ==
                                      keccak256(value)
                                  ) {
                                      return true;
                                  } else {
                                      return false;
                                  }
                              }
              
                              uint8 nextPathNibble = uint8(path[pathPtr]);
                              if (nextPathNibble > 16) {
                                  return false;
                              }
                              nodeKey = bytes32(
                                  RLPReader.toUintStrict(currentNodeList[nextPathNibble])
                              );
                              pathPtr += 1;
                          } else if (currentNodeList.length == 2) {
                              uint256 traversed = _nibblesToTraverse(
                                  RLPReader.toBytes(currentNodeList[0]),
                                  path,
                                  pathPtr
                              );
                              if (pathPtr + traversed == path.length) {
                                  //leaf node
                                  if (
                                      keccak256(RLPReader.toBytes(currentNodeList[1])) ==
                                      keccak256(value)
                                  ) {
                                      return true;
                                  } else {
                                      return false;
                                  }
                              }
              
                              //extension node
                              if (traversed == 0) {
                                  return false;
                              }
              
                              pathPtr += traversed;
                              nodeKey = bytes32(RLPReader.toUintStrict(currentNodeList[1]));
                          } else {
                              return false;
                          }
                      }
                  }
              
                  function _nibblesToTraverse(
                      bytes memory encodedPartialPath,
                      bytes memory path,
                      uint256 pathPtr
                  ) private pure returns (uint256) {
                      uint256 len;
                      // 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)
                      private
                      pure
                      returns (bytes memory)
                  {
                      bytes memory nibbles;
                      if (b.length > 0) {
                          uint8 offset;
                          uint8 hpNibble = uint8(_getNthNibbleOfBytes(0, b));
                          if (hpNibble == 1 || hpNibble == 3) {
                              nibbles = new bytes(b.length * 2 - 1);
                              bytes1 oddNibble = _getNthNibbleOfBytes(1, b);
                              nibbles[0] = oddNibble;
                              offset = 1;
                          } else {
                              nibbles = new bytes(b.length * 2 - 2);
                              offset = 0;
                          }
              
                          for (uint256 i = offset; i < nibbles.length; i++) {
                              nibbles[i] = _getNthNibbleOfBytes(i - offset + 2, b);
                          }
                      }
                      return nibbles;
                  }
              
                  function _getNthNibbleOfBytes(uint256 n, bytes memory str)
                      private
                      pure
                      returns (bytes1)
                  {
                      return
                          bytes1(
                              n % 2 == 0 ? uint8(str[n / 2]) / 0x10 : uint8(str[n / 2]) % 0x10
                          );
                  }
              }
              
              // File: openzeppelin-solidity/contracts/ownership/Ownable.sol
              
              pragma solidity ^0.5.2;
              
              /**
               * @title Ownable
               * @dev The Ownable contract has an owner address, and provides basic authorization control
               * functions, this simplifies the implementation of "user permissions".
               */
              contract Ownable {
                  address private _owner;
              
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
              
                  /**
                   * @dev The Ownable constructor sets the original `owner` of the contract to the sender
                   * account.
                   */
                  constructor () internal {
                      _owner = msg.sender;
                      emit OwnershipTransferred(address(0), _owner);
                  }
              
                  /**
                   * @return the address of the owner.
                   */
                  function owner() public view returns (address) {
                      return _owner;
                  }
              
                  /**
                   * @dev Throws if called by any account other than the owner.
                   */
                  modifier onlyOwner() {
                      require(isOwner());
                      _;
                  }
              
                  /**
                   * @return true if `msg.sender` is the owner of the contract.
                   */
                  function isOwner() public view returns (bool) {
                      return msg.sender == _owner;
                  }
              
                  /**
                   * @dev Allows the current owner to relinquish control of the contract.
                   * It will not be possible to call the functions with the `onlyOwner`
                   * modifier anymore.
                   * @notice Renouncing ownership will leave the contract without an owner,
                   * thereby removing any functionality that is only available to the owner.
                   */
                  function renounceOwnership() public onlyOwner {
                      emit OwnershipTransferred(_owner, address(0));
                      _owner = address(0);
                  }
              
                  /**
                   * @dev Allows the current owner to transfer control of the contract to a newOwner.
                   * @param newOwner The address to transfer ownership to.
                   */
                  function transferOwnership(address newOwner) public onlyOwner {
                      _transferOwnership(newOwner);
                  }
              
                  /**
                   * @dev Transfers control of the contract to a newOwner.
                   * @param newOwner The address to transfer ownership to.
                   */
                  function _transferOwnership(address newOwner) internal {
                      require(newOwner != address(0));
                      emit OwnershipTransferred(_owner, newOwner);
                      _owner = newOwner;
                  }
              }
              
              // File: contracts/common/lib/PriorityQueue.sol
              
              pragma solidity ^0.5.2;
              
              
              
              /**
               * @title PriorityQueue
               * @dev A priority queue implementation.
               */
              contract PriorityQueue is Ownable {
                  using SafeMath for uint256;
              
                  uint256[] heapList;
                  uint256 public currentSize;
              
                  constructor() public {
                      heapList = [0];
                  }
              
                  /**
                * @dev Inserts an element into the priority queue.
                * @param _priority Priority to insert.
                * @param _value Some additional value.
                */
                  function insert(uint256 _priority, uint256 _value) public onlyOwner {
                      uint256 element = (_priority << 128) | _value;
                      heapList.push(element);
                      currentSize = currentSize.add(1);
                      _percUp(currentSize);
                  }
              
                  /**
                * @dev Returns the top element of the heap.
                * @return The smallest element in the priority queue.
                */
                  function getMin() public view returns (uint256, uint256) {
                      return _splitElement(heapList[1]);
                  }
              
                  /**
                * @dev Deletes the top element of the heap and shifts everything up.
                * @return The smallest element in the priorty queue.
                */
                  function delMin() public onlyOwner returns (uint256, uint256) {
                      uint256 retVal = heapList[1];
                      heapList[1] = heapList[currentSize];
                      delete heapList[currentSize];
                      currentSize = currentSize.sub(1);
                      _percDown(1);
                      heapList.length = heapList.length.sub(1);
                      return _splitElement(retVal);
                  }
              
                  /**
                * @dev Determines the minimum child of a given node in the tree.
                * @param _index Index of the node in the tree.
                * @return The smallest child node.
                */
                  function _minChild(uint256 _index) private view returns (uint256) {
                      if (_index.mul(2).add(1) > currentSize) {
                          return _index.mul(2);
                      } else {
                          if (heapList[_index.mul(2)] < heapList[_index.mul(2).add(1)]) {
                              return _index.mul(2);
                          } else {
                              return _index.mul(2).add(1);
                          }
                      }
                  }
              
                  /**
                 * @dev Bubbles the element at some index up.
                 */
                  function _percUp(uint256 _index) private {
                      uint256 index = _index;
                      uint256 j = index;
                      uint256 newVal = heapList[index];
              
                      while (newVal < heapList[index.div(2)]) {
                          heapList[index] = heapList[index.div(2)];
                          index = index.div(2);
                      }
              
                      if (index != j) {
                          heapList[index] = newVal;
                      }
                  }
              
                  /**
                 * @dev Bubbles the element at some index down.
                 */
                  function _percDown(uint256 _index) private {
                      uint256 index = _index;
                      uint256 j = index;
                      uint256 newVal = heapList[index];
                      uint256 mc = _minChild(index);
                      while (mc <= currentSize && newVal > heapList[mc]) {
                          heapList[index] = heapList[mc];
                          index = mc;
                          mc = _minChild(index);
                      }
              
                      if (index != j) {
                          heapList[index] = newVal;
                      }
                  }
              
                  /**
                 * @dev Split an element into its priority and value.
                 * @param _element Element to decode.
                 * @return A tuple containing the priority and value.
                 */
                  function _splitElement(uint256 _element)
                      private
                      pure
                      returns (uint256, uint256)
                  {
                      uint256 priority = _element >> 128;
                      uint256 value = uint256(uint128(_element));
                      return (priority, value);
                  }
              }
              
              // File: contracts/common/lib/BytesLib.sol
              
              pragma solidity ^0.5.2;
              
              
              library BytesLib {
                  function concat(bytes memory _preBytes, bytes memory _postBytes)
                      internal
                      pure
                      returns (bytes memory)
                  {
                      bytes memory tempBytes;
                      assembly {
                          // Get a location of some free memory and store it in tempBytes as
                          // Solidity does for memory variables.
                          tempBytes := mload(0x40)
              
                          // Store the length of the first bytes array at the beginning of
                          // the memory for tempBytes.
                          let length := mload(_preBytes)
                          mstore(tempBytes, length)
              
                          // Maintain a memory counter for the current write location in the
                          // temp bytes array by adding the 32 bytes for the array length to
                          // the starting location.
                          let mc := add(tempBytes, 0x20)
                          // Stop copying when the memory counter reaches the length of the
                          // first bytes array.
                          let end := add(mc, length)
              
                          for {
                              // Initialize a copy counter to the start of the _preBytes data,
                              // 32 bytes into its memory.
                              let cc := add(_preBytes, 0x20)
                          } lt(mc, end) {
                              // Increase both counters by 32 bytes each iteration.
                              mc := add(mc, 0x20)
                              cc := add(cc, 0x20)
                          } {
                              // Write the _preBytes data into the tempBytes memory 32 bytes
                              // at a time.
                              mstore(mc, mload(cc))
                          }
              
                          // Add the length of _postBytes to the current length of tempBytes
                          // and store it as the new length in the first 32 bytes of the
                          // tempBytes memory.
                          length := mload(_postBytes)
                          mstore(tempBytes, add(length, mload(tempBytes)))
              
                          // Move the memory counter back from a multiple of 0x20 to the
                          // actual end of the _preBytes data.
                          mc := end
                          // Stop copying when the memory counter reaches the new combined
                          // length of the arrays.
                          end := add(mc, length)
              
                          for {
                              let cc := add(_postBytes, 0x20)
                          } lt(mc, end) {
                              mc := add(mc, 0x20)
                              cc := add(cc, 0x20)
                          } {
                              mstore(mc, mload(cc))
                          }
              
                          // Update the free-memory pointer by padding our last write location
                          // to 32 bytes: add 31 bytes to the end of tempBytes to move to the
                          // next 32 byte block, then round down to the nearest multiple of
                          // 32. If the sum of the length of the two arrays is zero then add
                          // one before rounding down to leave a blank 32 bytes (the length block with 0).
                          mstore(
                              0x40,
                              and(
                                  add(add(end, iszero(add(length, mload(_preBytes)))), 31),
                                  not(31) // Round down to the nearest 32 bytes.
                              )
                          )
                      }
                      return tempBytes;
                  }
              
                  function slice(bytes memory _bytes, uint256 _start, uint256 _length)
                      internal
                      pure
                      returns (bytes memory)
                  {
                      require(_bytes.length >= (_start + _length));
                      bytes memory tempBytes;
                      assembly {
                          switch iszero(_length)
                              case 0 {
                                  // Get a location of some free memory and store it in tempBytes as
                                  // Solidity does for memory variables.
                                  tempBytes := mload(0x40)
              
                                  // The first word of the slice result is potentially a partial
                                  // word read from the original array. To read it, we calculate
                                  // the length of that partial word and start copying that many
                                  // bytes into the array. The first word we copy will start with
                                  // data we don't care about, but the last `lengthmod` bytes will
                                  // land at the beginning of the contents of the new array. When
                                  // we're done copying, we overwrite the full first word with
                                  // the actual length of the slice.
                                  let lengthmod := and(_length, 31)
              
                                  // The multiplication in the next line is necessary
                                  // because when slicing multiples of 32 bytes (lengthmod == 0)
                                  // the following copy loop was copying the origin's length
                                  // and then ending prematurely not copying everything it should.
                                  let mc := add(
                                      add(tempBytes, lengthmod),
                                      mul(0x20, iszero(lengthmod))
                                  )
                                  let end := add(mc, _length)
              
                                  for {
                                      // The multiplication in the next line has the same exact purpose
                                      // as the one above.
                                      let cc := add(
                                          add(
                                              add(_bytes, lengthmod),
                                              mul(0x20, iszero(lengthmod))
                                          ),
                                          _start
                                      )
                                  } lt(mc, end) {
                                      mc := add(mc, 0x20)
                                      cc := add(cc, 0x20)
                                  } {
                                      mstore(mc, mload(cc))
                                  }
              
                                  mstore(tempBytes, _length)
              
                                  //update free-memory pointer
                                  //allocating the array padded to 32 bytes like the compiler does now
                                  mstore(0x40, and(add(mc, 31), not(31)))
                              }
                              //if we want a zero-length slice let's just return a zero-length array
                              default {
                                  tempBytes := mload(0x40)
                                  mstore(0x40, add(tempBytes, 0x20))
                              }
                      }
              
                      return tempBytes;
                  }
              
                  // Pad a bytes array to 32 bytes
                  function leftPad(bytes memory _bytes) internal pure returns (bytes memory) {
                      // may underflow if bytes.length < 32. Hence using SafeMath.sub
                      bytes memory newBytes = new bytes(SafeMath.sub(32, _bytes.length));
                      return concat(newBytes, _bytes);
                  }
              
                  function toBytes32(bytes memory b) internal pure returns (bytes32) {
                      require(b.length >= 32, "Bytes array should atleast be 32 bytes");
                      bytes32 out;
                      for (uint256 i = 0; i < 32; i++) {
                          out |= bytes32(b[i] & 0xFF) >> (i * 8);
                      }
                      return out;
                  }
              
                  function toBytes4(bytes memory b) internal pure returns (bytes4 result) {
                      assembly {
                          result := mload(add(b, 32))
                      }
                  }
              
                  function fromBytes32(bytes32 x) internal pure returns (bytes memory) {
                      bytes memory b = new bytes(32);
                      for (uint256 i = 0; i < 32; i++) {
                          b[i] = bytes1(uint8(uint256(x) / (2**(8 * (31 - i)))));
                      }
                      return b;
                  }
              
                  function fromUint(uint256 _num) internal pure returns (bytes memory _ret) {
                      _ret = new bytes(32);
                      assembly {
                          mstore(add(_ret, 32), _num)
                      }
                  }
              
                  function toUint(bytes memory _bytes, uint256 _start)
                      internal
                      pure
                      returns (uint256)
                  {
                      require(_bytes.length >= (_start + 32));
                      uint256 tempUint;
                      assembly {
                          tempUint := mload(add(add(_bytes, 0x20), _start))
                      }
                      return tempUint;
                  }
              
                  function toAddress(bytes memory _bytes, uint256 _start)
                      internal
                      pure
                      returns (address)
                  {
                      require(_bytes.length >= (_start + 20));
                      address tempAddress;
                      assembly {
                          tempAddress := div(
                              mload(add(add(_bytes, 0x20), _start)),
                              0x1000000000000000000000000
                          )
                      }
              
                      return tempAddress;
                  }
              }
              
              // File: contracts/common/lib/ExitPayloadReader.sol
              
              pragma solidity 0.5.17;
              
              
              
              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;
                }
              
                function toExitPayload(bytes memory data)
                      internal
                      pure
                      returns (ExitPayload memory)
                  {
                      RLPReader.RLPItem[] memory payloadData = data
                          .toRlpItem()
                          .toList();
              
                      return ExitPayload(payloadData);
                  }
              
                  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 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();
                  }
              
                  function getTx(ExitPayload memory payload) internal pure returns(bytes memory) {
                    return payload.data[10].toBytes();
                  }
              
                  function getTxProof(ExitPayload memory payload) internal pure returns(bytes memory) {
                    return payload.data[11].toBytes();
                  }
                  
                  // 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];
                  }
              }
              
              // File: contracts/common/governance/IGovernance.sol
              
              pragma solidity ^0.5.2;
              
              interface IGovernance {
                  function update(address target, bytes calldata data) external;
              }
              
              // File: contracts/common/governance/Governable.sol
              
              pragma solidity ^0.5.2;
              
              
              contract Governable {
                  IGovernance public governance;
              
                  constructor(address _governance) public {
                      governance = IGovernance(_governance);
                  }
              
                  modifier onlyGovernance() {
                      _assertGovernance();
                      _;
                  }
              
                  function _assertGovernance() private view {
                      require(
                          msg.sender == address(governance),
                          "Only governance contract is authorized"
                      );
                  }
              }
              
              // File: contracts/root/withdrawManager/IWithdrawManager.sol
              
              pragma solidity ^0.5.2;
              
              contract IWithdrawManager {
                  function createExitQueue(address token) external;
              
                  function verifyInclusion(
                      bytes calldata data,
                      uint8 offset,
                      bool verifyTxInclusion
                  ) external view returns (uint256 age);
              
                  function addExitToQueue(
                      address exitor,
                      address childToken,
                      address rootToken,
                      uint256 exitAmountOrTokenId,
                      bytes32 txHash,
                      bool isRegularExit,
                      uint256 priority
                  ) external;
              
                  function addInput(
                      uint256 exitId,
                      uint256 age,
                      address utxoOwner,
                      address token
                  ) external;
              
                  function challengeExit(
                      uint256 exitId,
                      uint256 inputId,
                      bytes calldata challengeData,
                      address adjudicatorPredicate
                  ) external;
              }
              
              // File: contracts/common/Registry.sol
              
              pragma solidity ^0.5.2;
              
              
              
              
              contract Registry is Governable {
                  // @todo hardcode constants
                  bytes32 private constant WETH_TOKEN = keccak256("wethToken");
                  bytes32 private constant DEPOSIT_MANAGER = keccak256("depositManager");
                  bytes32 private constant STAKE_MANAGER = keccak256("stakeManager");
                  bytes32 private constant VALIDATOR_SHARE = keccak256("validatorShare");
                  bytes32 private constant WITHDRAW_MANAGER = keccak256("withdrawManager");
                  bytes32 private constant CHILD_CHAIN = keccak256("childChain");
                  bytes32 private constant STATE_SENDER = keccak256("stateSender");
                  bytes32 private constant SLASHING_MANAGER = keccak256("slashingManager");
              
                  address public erc20Predicate;
                  address public erc721Predicate;
              
                  mapping(bytes32 => address) public contractMap;
                  mapping(address => address) public rootToChildToken;
                  mapping(address => address) public childToRootToken;
                  mapping(address => bool) public proofValidatorContracts;
                  mapping(address => bool) public isERC721;
              
                  enum Type {Invalid, ERC20, ERC721, Custom}
                  struct Predicate {
                      Type _type;
                  }
                  mapping(address => Predicate) public predicates;
              
                  event TokenMapped(address indexed rootToken, address indexed childToken);
                  event ProofValidatorAdded(address indexed validator, address indexed from);
                  event ProofValidatorRemoved(address indexed validator, address indexed from);
                  event PredicateAdded(address indexed predicate, address indexed from);
                  event PredicateRemoved(address indexed predicate, address indexed from);
                  event ContractMapUpdated(bytes32 indexed key, address indexed previousContract, address indexed newContract);
              
                  constructor(address _governance) public Governable(_governance) {}
              
                  function updateContractMap(bytes32 _key, address _address) external onlyGovernance {
                      emit ContractMapUpdated(_key, contractMap[_key], _address);
                      contractMap[_key] = _address;
                  }
              
                  /**
                   * @dev Map root token to child token
                   * @param _rootToken Token address on the root chain
                   * @param _childToken Token address on the child chain
                   * @param _isERC721 Is the token being mapped ERC721
                   */
                  function mapToken(
                      address _rootToken,
                      address _childToken,
                      bool _isERC721
                  ) external onlyGovernance {
                      require(_rootToken != address(0x0) && _childToken != address(0x0), "INVALID_TOKEN_ADDRESS");
                      rootToChildToken[_rootToken] = _childToken;
                      childToRootToken[_childToken] = _rootToken;
                      isERC721[_rootToken] = _isERC721;
                      IWithdrawManager(contractMap[WITHDRAW_MANAGER]).createExitQueue(_rootToken);
                      emit TokenMapped(_rootToken, _childToken);
                  }
              
                  function addErc20Predicate(address predicate) public onlyGovernance {
                      require(predicate != address(0x0), "Can not add null address as predicate");
                      erc20Predicate = predicate;
                      addPredicate(predicate, Type.ERC20);
                  }
              
                  function addErc721Predicate(address predicate) public onlyGovernance {
                      erc721Predicate = predicate;
                      addPredicate(predicate, Type.ERC721);
                  }
              
                  function addPredicate(address predicate, Type _type) public onlyGovernance {
                      require(predicates[predicate]._type == Type.Invalid, "Predicate already added");
                      predicates[predicate]._type = _type;
                      emit PredicateAdded(predicate, msg.sender);
                  }
              
                  function removePredicate(address predicate) public onlyGovernance {
                      require(predicates[predicate]._type != Type.Invalid, "Predicate does not exist");
                      delete predicates[predicate];
                      emit PredicateRemoved(predicate, msg.sender);
                  }
              
                  function getValidatorShareAddress() public view returns (address) {
                      return contractMap[VALIDATOR_SHARE];
                  }
              
                  function getWethTokenAddress() public view returns (address) {
                      return contractMap[WETH_TOKEN];
                  }
              
                  function getDepositManagerAddress() public view returns (address) {
                      return contractMap[DEPOSIT_MANAGER];
                  }
              
                  function getStakeManagerAddress() public view returns (address) {
                      return contractMap[STAKE_MANAGER];
                  }
              
                  function getSlashingManagerAddress() public view returns (address) {
                      return contractMap[SLASHING_MANAGER];
                  }
              
                  function getWithdrawManagerAddress() public view returns (address) {
                      return contractMap[WITHDRAW_MANAGER];
                  }
              
                  function getChildChainAndStateSender() public view returns (address, address) {
                      return (contractMap[CHILD_CHAIN], contractMap[STATE_SENDER]);
                  }
              
                  function isTokenMapped(address _token) public view returns (bool) {
                      return rootToChildToken[_token] != address(0x0);
                  }
              
                  function isTokenMappedAndIsErc721(address _token) public view returns (bool) {
                      require(isTokenMapped(_token), "TOKEN_NOT_MAPPED");
                      return isERC721[_token];
                  }
              
                  function isTokenMappedAndGetPredicate(address _token) public view returns (address) {
                      if (isTokenMappedAndIsErc721(_token)) {
                          return erc721Predicate;
                      }
                      return erc20Predicate;
                  }
              
                  function isChildTokenErc721(address childToken) public view returns (bool) {
                      address rootToken = childToRootToken[childToken];
                      require(rootToken != address(0x0), "Child token is not mapped");
                      return isERC721[rootToken];
                  }
              }
              
              // File: contracts/root/withdrawManager/ExitNFT.sol
              
              pragma solidity ^0.5.2;
              
              
              
              contract ExitNFT is ERC721 {
                  Registry internal registry;
              
                  modifier onlyWithdrawManager() {
                      require(
                          msg.sender == registry.getWithdrawManagerAddress(),
                          "UNAUTHORIZED_WITHDRAW_MANAGER_ONLY"
                      );
                      _;
                  }
              
                  constructor(address _registry) public {
                      registry = Registry(_registry);
                  }
              
                  function mint(address _owner, uint256 _tokenId)
                      external
                      onlyWithdrawManager
                  {
                      _mint(_owner, _tokenId);
                  }
              
                  function burn(uint256 _tokenId) external onlyWithdrawManager {
                      _burn(_tokenId);
                  }
              
                  function exists(uint256 tokenId) public view returns (bool) {
                      return _exists(tokenId);
                  }
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC721/ERC721Holder.sol
              
              pragma solidity ^0.5.2;
              
              
              contract ERC721Holder is IERC721Receiver {
                  function onERC721Received(address, address, uint256, bytes memory) public returns (bytes4) {
                      return this.onERC721Received.selector;
                  }
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC20/SafeERC20.sol
              
              pragma solidity ^0.5.2;
              
              
              
              
              /**
               * @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 ERC20;` 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));
                  }
              
                  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'
                      require((value == 0) || (token.allowance(address(this), spender) == 0));
                      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);
                      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 equal true).
                   * @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.
              
                      // A Solidity high level call has three parts:
                      //  1. The target address is checked to verify it contains contract code
                      //  2. The call itself is made, and success asserted
                      //  3. The return value is decoded, which in turn checks the size of the returned data.
              
                      require(address(token).isContract());
              
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory returndata) = address(token).call(data);
                      require(success);
              
                      if (returndata.length > 0) { // Return data is optional
                          require(abi.decode(returndata, (bool)));
                      }
                  }
              }
              
              // File: contracts/common/tokens/WETH.sol
              
              pragma solidity ^0.5.2;
              
              
              contract WETH is ERC20 {
                  event Deposit(address indexed dst, uint256 wad);
                  event Withdrawal(address indexed src, uint256 wad);
              
                  function deposit() public payable;
              
                  function withdraw(uint256 wad) public;
              
                  function withdraw(uint256 wad, address user) public;
              }
              
              // File: contracts/root/depositManager/IDepositManager.sol
              
              pragma solidity ^0.5.2;
              
              interface IDepositManager {
                  function depositEther() external payable;
                  function transferAssets(
                      address _token,
                      address _user,
                      uint256 _amountOrNFTId
                  ) external;
                  function depositERC20(address _token, uint256 _amount) external;
                  function depositERC721(address _token, uint256 _tokenId) external;
              }
              
              // File: contracts/common/misc/ProxyStorage.sol
              
              pragma solidity ^0.5.2;
              
              
              contract ProxyStorage is Ownable {
                  address internal proxyTo;
              }
              
              // File: contracts/common/mixin/ChainIdMixin.sol
              
              pragma solidity ^0.5.2;
              
              contract ChainIdMixin {
                bytes constant public networkId = hex"89";
                uint256 constant public CHAINID = 137;
              }
              
              // File: contracts/root/RootChainStorage.sol
              
              pragma solidity ^0.5.2;
              
              
              
              
              
              contract RootChainHeader {
                  event NewHeaderBlock(
                      address indexed proposer,
                      uint256 indexed headerBlockId,
                      uint256 indexed reward,
                      uint256 start,
                      uint256 end,
                      bytes32 root
                  );
                  // housekeeping event
                  event ResetHeaderBlock(address indexed proposer, uint256 indexed headerBlockId);
                  struct HeaderBlock {
                      bytes32 root;
                      uint256 start;
                      uint256 end;
                      uint256 createdAt;
                      address proposer;
                  }
              }
              
              
              contract RootChainStorage is ProxyStorage, RootChainHeader, ChainIdMixin {
                  bytes32 public heimdallId;
                  uint8 public constant VOTE_TYPE = 2;
              
                  uint16 internal constant MAX_DEPOSITS = 10000;
                  uint256 public _nextHeaderBlock = MAX_DEPOSITS;
                  uint256 internal _blockDepositId = 1;
                  mapping(uint256 => HeaderBlock) public headerBlocks;
                  Registry internal registry;
              }
              
              // File: contracts/staking/stakeManager/IStakeManager.sol
              
              pragma solidity 0.5.17;
              
              contract IStakeManager {
                  // validator replacement
                  function startAuction(
                      uint256 validatorId,
                      uint256 amount,
                      bool acceptDelegation,
                      bytes calldata signerPubkey
                  ) external;
              
                  function confirmAuctionBid(uint256 validatorId, uint256 heimdallFee) external;
              
                  function transferFunds(
                      uint256 validatorId,
                      uint256 amount,
                      address delegator
                  ) external returns (bool);
              
                  function delegationDeposit(
                      uint256 validatorId,
                      uint256 amount,
                      address delegator
                  ) external returns (bool);
              
                  function unstake(uint256 validatorId) external;
              
                  function totalStakedFor(address addr) external view returns (uint256);
              
                  function stakeFor(
                      address user,
                      uint256 amount,
                      uint256 heimdallFee,
                      bool acceptDelegation,
                      bytes memory signerPubkey
                  ) public;
              
                  function checkSignatures(
                      uint256 blockInterval,
                      bytes32 voteHash,
                      bytes32 stateRoot,
                      address proposer,
                      uint[3][] calldata sigs
                  ) external returns (uint256);
              
                  function updateValidatorState(uint256 validatorId, int256 amount) public;
              
                  function ownerOf(uint256 tokenId) public view returns (address);
              
                  function slash(bytes calldata slashingInfoList) external returns (uint256);
              
                  function validatorStake(uint256 validatorId) public view returns (uint256);
              
                  function epoch() public view returns (uint256);
              
                  function getRegistry() public view returns (address);
              
                  function withdrawalDelay() public view returns (uint256);
              
                  function delegatedAmount(uint256 validatorId) public view returns(uint256);
              
                  function decreaseValidatorDelegatedAmount(uint256 validatorId, uint256 amount) public;
              
                  function withdrawDelegatorsReward(uint256 validatorId) public returns(uint256);
              
                  function delegatorsReward(uint256 validatorId) public view returns(uint256);
              
                  function dethroneAndStake(
                      address auctionUser,
                      uint256 heimdallFee,
                      uint256 validatorId,
                      uint256 auctionAmount,
                      bool acceptDelegation,
                      bytes calldata signerPubkey
                  ) external;
              }
              
              // File: contracts/root/IRootChain.sol
              
              pragma solidity ^0.5.2;
              
              
              interface IRootChain {
                  function slash() external;
              
                  function submitHeaderBlock(bytes calldata data, bytes calldata sigs)
                      external;
                  
                  function submitCheckpoint(bytes calldata data, uint[3][] calldata sigs)
                      external;
              
                  function getLastChildBlock() external view returns (uint256);
              
                  function currentHeaderBlock() external view returns (uint256);
              }
              
              // File: contracts/root/RootChain.sol
              
              pragma solidity ^0.5.2;
              
              
              
              
              
              
              
              
              contract RootChain is RootChainStorage, IRootChain {
                  using SafeMath for uint256;
                  using RLPReader for bytes;
                  using RLPReader for RLPReader.RLPItem;
              
                  modifier onlyDepositManager() {
                      require(msg.sender == registry.getDepositManagerAddress(), "UNAUTHORIZED_DEPOSIT_MANAGER_ONLY");
                      _;
                  }
              
                  function submitHeaderBlock(bytes calldata data, bytes calldata sigs) external {
                      revert();
                  }
              
                  function submitCheckpoint(bytes calldata data, uint[3][] calldata sigs) external {
                      (address proposer, uint256 start, uint256 end, bytes32 rootHash, bytes32 accountHash, uint256 _borChainID) = abi
                          .decode(data, (address, uint256, uint256, bytes32, bytes32, uint256));
                      require(CHAINID == _borChainID, "Invalid bor chain id");
              
                      require(_buildHeaderBlock(proposer, start, end, rootHash), "INCORRECT_HEADER_DATA");
              
                      // check if it is better to keep it in local storage instead
                      IStakeManager stakeManager = IStakeManager(registry.getStakeManagerAddress());
                      uint256 _reward = stakeManager.checkSignatures(
                          end.sub(start).add(1),
                          /**  
                              prefix 01 to data 
                              01 represents positive vote on data and 00 is negative vote
                              malicious validator can try to send 2/3 on negative vote so 01 is appended
                           */
                          keccak256(abi.encodePacked(bytes(hex"01"), data)),
                          accountHash,
                          proposer,
                          sigs
                      );
              
                      require(_reward != 0, "Invalid checkpoint");
                      emit NewHeaderBlock(proposer, _nextHeaderBlock, _reward, start, end, rootHash);
                      _nextHeaderBlock = _nextHeaderBlock.add(MAX_DEPOSITS);
                      _blockDepositId = 1;
                  }
              
                  function updateDepositId(uint256 numDeposits) external onlyDepositManager returns (uint256 depositId) {
                      depositId = currentHeaderBlock().add(_blockDepositId);
                      // deposit ids will be (_blockDepositId, _blockDepositId + 1, .... _blockDepositId + numDeposits - 1)
                      _blockDepositId = _blockDepositId.add(numDeposits);
                      require(
                          // Since _blockDepositId is initialized to 1; only (MAX_DEPOSITS - 1) deposits per header block are allowed
                          _blockDepositId <= MAX_DEPOSITS,
                          "TOO_MANY_DEPOSITS"
                      );
                  }
              
                  function getLastChildBlock() external view returns (uint256) {
                      return headerBlocks[currentHeaderBlock()].end;
                  }
              
                  function slash() external {
                      //TODO: future implementation
                  }
              
                  function currentHeaderBlock() public view returns (uint256) {
                      return _nextHeaderBlock.sub(MAX_DEPOSITS);
                  }
              
                  function _buildHeaderBlock(
                      address proposer,
                      uint256 start,
                      uint256 end,
                      bytes32 rootHash
                  ) private returns (bool) {
                      uint256 nextChildBlock;
                      /*
                  The ID of the 1st header block is MAX_DEPOSITS.
                  if _nextHeaderBlock == MAX_DEPOSITS, then the first header block is yet to be submitted, hence nextChildBlock = 0
                  */
                      if (_nextHeaderBlock > MAX_DEPOSITS) {
                          nextChildBlock = headerBlocks[currentHeaderBlock()].end + 1;
                      }
                      if (nextChildBlock != start) {
                          return false;
                      }
              
                      HeaderBlock memory headerBlock = HeaderBlock({
                          root: rootHash,
                          start: nextChildBlock,
                          end: end,
                          createdAt: now,
                          proposer: proposer
                      });
              
                      headerBlocks[_nextHeaderBlock] = headerBlock;
                      return true;
                  }
              
                  // Housekeeping function. @todo remove later
                  function setNextHeaderBlock(uint256 _value) public onlyOwner {
                      require(_value % MAX_DEPOSITS == 0, "Invalid value");
                      for (uint256 i = _value; i < _nextHeaderBlock; i += MAX_DEPOSITS) {
                          delete headerBlocks[i];
                      }
                      _nextHeaderBlock = _value;
                      _blockDepositId = 1;
                      emit ResetHeaderBlock(msg.sender, _nextHeaderBlock);
                  }
              
                  // Housekeeping function. @todo remove later
                  function setHeimdallId(string memory _heimdallId) public onlyOwner {
                      heimdallId = keccak256(abi.encodePacked(_heimdallId));
                  }
              }
              
              // File: contracts/root/stateSyncer/StateSender.sol
              
              pragma solidity ^0.5.2;
              
              
              
              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: contracts/common/mixin/Lockable.sol
              
              pragma solidity ^0.5.2;
              
              contract Lockable {
                  bool public locked;
              
                  modifier onlyWhenUnlocked() {
                      _assertUnlocked();
                      _;
                  }
              
                  function _assertUnlocked() private view {
                      require(!locked, "locked");
                  }
              
                  function lock() public {
                      locked = true;
                  }
              
                  function unlock() public {
                      locked = false;
                  }
              }
              
              // File: contracts/common/mixin/GovernanceLockable.sol
              
              pragma solidity ^0.5.2;
              
              
              
              contract GovernanceLockable is Lockable, Governable {
                  constructor(address governance) public Governable(governance) {}
              
                  function lock() public onlyGovernance {
                      super.lock();
                  }
              
                  function unlock() public onlyGovernance {
                      super.unlock();
                  }
              }
              
              // File: contracts/root/depositManager/DepositManagerStorage.sol
              
              pragma solidity ^0.5.2;
              
              
              
              
              
              
              
              contract DepositManagerHeader {
                  event NewDepositBlock(address indexed owner, address indexed token, uint256 amountOrNFTId, uint256 depositBlockId);
                  event MaxErc20DepositUpdate(uint256 indexed oldLimit, uint256 indexed newLimit);
              
                  struct DepositBlock {
                      bytes32 depositHash;
                      uint256 createdAt;
                  }
              }
              
              
              contract DepositManagerStorage is ProxyStorage, GovernanceLockable, DepositManagerHeader {
                  Registry public registry;
                  RootChain public rootChain;
                  StateSender public stateSender;
              
                  mapping(uint256 => DepositBlock) public deposits;
              
                  address public childChain;
                  uint256 public maxErc20Deposit = 100 * (10**18);
              }
              
              // File: contracts/root/depositManager/DepositManager.sol
              
              pragma solidity ^0.5.2;
              
              
              
              
              
              
              
              
              
              
              
              
              
              
              contract DepositManager is DepositManagerStorage, IDepositManager, ERC721Holder {
                  using SafeMath for uint256;
                  using SafeERC20 for IERC20;
              
                  modifier isTokenMapped(address _token) {
                      require(registry.isTokenMapped(_token), "TOKEN_NOT_SUPPORTED");
                      _;
                  }
              
                  modifier isPredicateAuthorized() {
                      require(uint8(registry.predicates(msg.sender)) != 0, "Not a valid predicate");
                      _;
                  }
              
                  constructor() public GovernanceLockable(address(0x0)) {}
              
                  // deposit ETH by sending to this contract
                  function() external payable {
                      depositEther();
                  }
              
                  function updateMaxErc20Deposit(uint256 maxDepositAmount) public onlyGovernance {
                      require(maxDepositAmount != 0);
                      emit MaxErc20DepositUpdate(maxErc20Deposit, maxDepositAmount);
                      maxErc20Deposit = maxDepositAmount;
                  }
              
                  function transferAssets(
                      address _token,
                      address _user,
                      uint256 _amountOrNFTId
                  ) external isPredicateAuthorized {
                      address wethToken = registry.getWethTokenAddress();
                      if (registry.isERC721(_token)) {
                          IERC721(_token).transferFrom(address(this), _user, _amountOrNFTId);
                      } else if (_token == wethToken) {
                          WETH t = WETH(_token);
                          t.withdraw(_amountOrNFTId, _user);
                      } else {
                          require(IERC20(_token).transfer(_user, _amountOrNFTId), "TRANSFER_FAILED");
                      }
                  }
              
                  function depositERC20(address _token, uint256 _amount) external {
                      depositERC20ForUser(_token, msg.sender, _amount);
                  }
              
                  function depositERC721(address _token, uint256 _tokenId) external {
                      depositERC721ForUser(_token, msg.sender, _tokenId);
                  }
              
                  function depositBulk(
                      address[] calldata _tokens,
                      uint256[] calldata _amountOrTokens,
                      address _user
                  )
                      external
                      onlyWhenUnlocked // unlike other deposit functions, depositBulk doesn't invoke _safeCreateDepositBlock
                  {
                      require(_tokens.length == _amountOrTokens.length, "Invalid Input");
                      uint256 depositId = rootChain.updateDepositId(_tokens.length);
                      Registry _registry = registry;
              
                      for (uint256 i = 0; i < _tokens.length; i++) {
                          // will revert if token is not mapped
                          if (_registry.isTokenMappedAndIsErc721(_tokens[i])) {
                              _safeTransferERC721(msg.sender, _tokens[i], _amountOrTokens[i]);
                          } else {
                              IERC20(_tokens[i]).safeTransferFrom(msg.sender, address(this), _amountOrTokens[i]);
                          }
              
                          _createDepositBlock(_user, _tokens[i], _amountOrTokens[i], depositId);
                          depositId = depositId.add(1);
                      }
                  }
              
                  /**
                   * @dev Caches childChain and stateSender (frequently used variables) from registry
                   */
                  function updateChildChainAndStateSender() public {
                      (address _childChain, address _stateSender) = registry.getChildChainAndStateSender();
                      require(
                          _stateSender != address(stateSender) || _childChain != childChain,
                          "Atleast one of stateSender or childChain address should change"
                      );
                      childChain = _childChain;
                      stateSender = StateSender(_stateSender);
                  }
              
                  function depositERC20ForUser(
                      address _token,
                      address _user,
                      uint256 _amount
                  ) public {
                      require(_amount <= maxErc20Deposit, "exceed maximum deposit amount");
                      IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);
                      _safeCreateDepositBlock(_user, _token, _amount);
                  }
              
                  function depositERC721ForUser(
                      address _token,
                      address _user,
                      uint256 _tokenId
                  ) public {
                      require(registry.isTokenMappedAndIsErc721(_token), "not erc721");
              
                      _safeTransferERC721(msg.sender, _token, _tokenId);
                      _safeCreateDepositBlock(_user, _token, _tokenId);
                  }
              
                  // @todo: write depositEtherForUser
                  function depositEther() public payable {
                      address wethToken = registry.getWethTokenAddress();
                      WETH t = WETH(wethToken);
                      t.deposit.value(msg.value)();
                      _safeCreateDepositBlock(msg.sender, wethToken, msg.value);
                  }
              
                  function _safeCreateDepositBlock(
                      address _user,
                      address _token,
                      uint256 _amountOrToken
                  ) internal onlyWhenUnlocked isTokenMapped(_token) {
                      _createDepositBlock(
                          _user,
                          _token,
                          _amountOrToken,
                          rootChain.updateDepositId(1) /* returns _depositId */
                      );
                  }
              
                  function _createDepositBlock(
                      address _user,
                      address _token,
                      uint256 _amountOrToken,
                      uint256 _depositId
                  ) internal {
                      deposits[_depositId] = DepositBlock(keccak256(abi.encodePacked(_user, _token, _amountOrToken)), now);
                      stateSender.syncState(childChain, abi.encode(_user, _token, _amountOrToken, _depositId));
                      emit NewDepositBlock(_user, _token, _amountOrToken, _depositId);
                  }
              
                  // Housekeeping function. @todo remove later
                  function updateRootChain(address _rootChain) public onlyOwner {
                      rootChain = RootChain(_rootChain);
                  }
              
                  function _safeTransferERC721(address _user, address _token, uint256 _tokenId) private {
                      IERC721(_token).safeTransferFrom(_user, address(this), _tokenId);
                  }
              }
              
              // File: contracts/common/lib/Common.sol
              
              pragma solidity ^0.5.2;
              
              
              library Common {
                  function getV(bytes memory v, uint16 chainId) public pure returns (uint8) {
                      if (chainId > 0) {
                          return
                              uint8(
                                  BytesLib.toUint(BytesLib.leftPad(v), 0) - (chainId * 2) - 8
                              );
                      } else {
                          return uint8(BytesLib.toUint(BytesLib.leftPad(v), 0));
                      }
                  }
              
                  //assemble the given address bytecode. If bytecode exists then the _addr is a contract.
                  function isContract(address _addr) public view returns (bool) {
                      uint256 length;
                      assembly {
                          //retrieve the size of the code on target address, this needs assembly
                          length := extcodesize(_addr)
                      }
                      return (length > 0);
                  }
              
                  // convert bytes to uint8
                  function toUint8(bytes memory _arg) public pure returns (uint8) {
                      return uint8(_arg[0]);
                  }
              
                  function toUint16(bytes memory _arg) public pure returns (uint16) {
                      return (uint16(uint8(_arg[0])) << 8) | uint16(uint8(_arg[1]));
                  }
              }
              
              // File: contracts/common/lib/RLPEncode.sol
              
              // Library for RLP encoding a list of bytes arrays.
              // Modeled after ethereumjs/rlp (https://github.com/ethereumjs/rlp)
              // [Very] modified version of Sam Mayo's library.
              pragma solidity ^0.5.2;
              
              
              library RLPEncode {
                  // Encode an item (bytes memory)
                  function encodeItem(bytes memory self)
                      internal
                      pure
                      returns (bytes memory)
                  {
                      bytes memory encoded;
                      if (self.length == 1 && uint8(self[0] & 0xFF) < 0x80) {
                          encoded = new bytes(1);
                          encoded = self;
                      } else {
                          encoded = BytesLib.concat(encodeLength(self.length, 128), self);
                      }
                      return encoded;
                  }
              
                  // Encode a list of items
                  function encodeList(bytes[] memory self)
                      internal
                      pure
                      returns (bytes memory)
                  {
                      bytes memory encoded;
                      for (uint256 i = 0; i < self.length; i++) {
                          encoded = BytesLib.concat(encoded, encodeItem(self[i]));
                      }
                      return BytesLib.concat(encodeLength(encoded.length, 192), encoded);
                  }
              
                  // Hack to encode nested lists. If you have a list as an item passed here, included
                  // pass = true in that index. E.g.
                  // [item, list, item] --> pass = [false, true, false]
                  // function encodeListWithPasses(bytes[] memory self, bool[] pass) internal pure returns (bytes memory) {
                  //   bytes memory encoded;
                  //   for (uint i=0; i < self.length; i++) {
                  // 		if (pass[i] == true) {
                  // 			encoded = BytesLib.concat(encoded, self[i]);
                  // 		} else {
                  // 			encoded = BytesLib.concat(encoded, encodeItem(self[i]));
                  // 		}
                  //   }
                  //   return BytesLib.concat(encodeLength(encoded.length, 192), encoded);
                  // }
              
                  // Generate the prefix for an item or the entire list based on RLP spec
                  function encodeLength(uint256 L, uint256 offset)
                      internal
                      pure
                      returns (bytes memory)
                  {
                      if (L < 56) {
                          bytes memory prefix = new bytes(1);
                          prefix[0] = bytes1(uint8(L + offset));
                          return prefix;
                      } else {
                          // lenLen is the length of the hex representation of the data length
                          uint256 lenLen;
                          uint256 i = 0x1;
              
                          while (L / i != 0) {
                              lenLen++;
                              i *= 0x100;
                          }
              
                          bytes memory prefix0 = getLengthBytes(offset + 55 + lenLen);
                          bytes memory prefix1 = getLengthBytes(L);
                          return BytesLib.concat(prefix0, prefix1);
                      }
                  }
              
                  function getLengthBytes(uint256 x) internal pure returns (bytes memory b) {
                      // Figure out if we need 1 or two bytes to express the length.
                      // 1 byte gets us to max 255
                      // 2 bytes gets us to max 65535 (no payloads will be larger than this)
                      uint256 nBytes = 1;
                      if (x > 255) {
                          nBytes = 2;
                      }
              
                      b = new bytes(nBytes);
                      // Encode the length and return it
                      for (uint256 i = 0; i < nBytes; i++) {
                          b[i] = bytes1(uint8(x / (2**(8 * (nBytes - 1 - i)))));
                      }
                  }
              }
              
              // File: contracts/root/withdrawManager/WithdrawManagerStorage.sol
              
              pragma solidity ^0.5.2;
              
              
              
              
              
              
              contract ExitsDataStructure {
                  struct Input {
                      address utxoOwner;
                      address predicate;
                      address token;
                  }
              
                  struct PlasmaExit {
                      uint256 receiptAmountOrNFTId;
                      bytes32 txHash;
                      address owner;
                      address token;
                      bool isRegularExit;
                      address predicate;
                      // Mapping from age of input to Input
                      mapping(uint256 => Input) inputs;
                  }
              }
              
              
              contract WithdrawManagerHeader is ExitsDataStructure {
                  event Withdraw(uint256 indexed exitId, address indexed user, address indexed token, uint256 amount);
              
                  event ExitStarted(
                      address indexed exitor,
                      uint256 indexed exitId,
                      address indexed token,
                      uint256 amount,
                      bool isRegularExit
                  );
              
                  event ExitUpdated(uint256 indexed exitId, uint256 indexed age, address signer);
                  event ExitPeriodUpdate(uint256 indexed oldExitPeriod, uint256 indexed newExitPeriod);
              
                  event ExitCancelled(uint256 indexed exitId);
              }
              
              
              contract WithdrawManagerStorage is ProxyStorage, WithdrawManagerHeader {
                  // 0.5 week = 7 * 86400 / 2 = 302400
                  uint256 public HALF_EXIT_PERIOD = 302400;
              
                  // Bonded exits collaterized at 0.1 ETH
                  uint256 internal constant BOND_AMOUNT = 10**17;
              
                  Registry internal registry;
                  RootChain internal rootChain;
              
                  mapping(uint128 => bool) isKnownExit;
                  mapping(uint256 => PlasmaExit) public exits;
                  // mapping with token => (owner => exitId) keccak(token+owner) keccak(token+owner+tokenId)
                  mapping(bytes32 => uint256) public ownerExits;
                  mapping(address => address) public exitsQueues;
                  ExitNFT public exitNft;
              
                  // ERC721, ERC20 and Weth transfers require 155000, 100000, 52000 gas respectively
                  // Processing each exit in a while loop iteration requires ~52000 gas (@todo check if this changed)
                  // uint32 constant internal ITERATION_GAS = 52000;
              
                  // So putting an upper limit of 155000 + 52000 + leeway
                  uint32 public ON_FINALIZE_GAS_LIMIT = 300000;
              
                  uint256 public exitWindow;
              }
              
              // File: contracts/root/predicates/IPredicate.sol
              
              pragma solidity ^0.5.2;
              
              
              
              
              
              
              
              
              interface IPredicate {
                  /**
                 * @notice Verify the deprecation of a state update
                 * @param exit ABI encoded PlasmaExit data
                 * @param inputUtxo ABI encoded Input UTXO data
                 * @param challengeData RLP encoded data of the challenge reference tx that encodes the following fields
                 * headerNumber Header block number of which the reference tx was a part of
                 * blockProof Proof that the block header (in the child chain) is a leaf in the submitted merkle root
                 * blockNumber Block number of which the reference tx is a part of
                 * blockTime Reference tx block time
                 * blocktxRoot Transactions root of block
                 * blockReceiptsRoot Receipts root of block
                 * receipt Receipt of the reference transaction
                 * receiptProof Merkle proof of the reference receipt
                 * branchMask Merkle proof branchMask for the receipt
                 * logIndex Log Index to read from the receipt
                 * tx Challenge transaction
                 * txProof Merkle proof of the challenge tx
                 * @return Whether or not the state is deprecated
                 */
                  function verifyDeprecation(
                      bytes calldata exit,
                      bytes calldata inputUtxo,
                      bytes calldata challengeData
                  ) external returns (bool);
              
                  function interpretStateUpdate(bytes calldata state)
                      external
                      view
                      returns (bytes memory);
                  function onFinalizeExit(bytes calldata data) external;
              }
              
              contract PredicateUtils is ExitsDataStructure, ChainIdMixin {
                  using RLPReader for RLPReader.RLPItem;
              
                  // Bonded exits collaterized at 0.1 ETH
                  uint256 private constant BOND_AMOUNT = 10**17;
              
                  IWithdrawManager internal withdrawManager;
                  IDepositManager internal depositManager;
              
                  modifier onlyWithdrawManager() {
                      require(
                          msg.sender == address(withdrawManager),
                          "ONLY_WITHDRAW_MANAGER"
                      );
                      _;
                  }
              
                  modifier isBondProvided() {
                      require(msg.value == BOND_AMOUNT, "Invalid Bond amount");
                      _;
                  }
              
                  function onFinalizeExit(bytes calldata data) external onlyWithdrawManager {
                      (, address token, address exitor, uint256 tokenId) = decodeExitForProcessExit(
                          data
                      );
                      depositManager.transferAssets(token, exitor, tokenId);
                  }
              
                  function sendBond() internal {
                      address(uint160(address(withdrawManager))).transfer(BOND_AMOUNT);
                  }
              
                  function getAddressFromTx(RLPReader.RLPItem[] memory txList)
                      internal
                      pure
                      returns (address signer, bytes32 txHash)
                  {
                      bytes[] memory rawTx = new bytes[](9);
                      for (uint8 i = 0; i <= 5; i++) {
                          rawTx[i] = txList[i].toBytes();
                      }
                      rawTx[6] = networkId;
                      rawTx[7] = hex""; // [7] and [8] have something to do with v, r, s values
                      rawTx[8] = hex"";
              
                      txHash = keccak256(RLPEncode.encodeList(rawTx));
                      signer = ecrecover(
                          txHash,
                          Common.getV(txList[6].toBytes(), Common.toUint16(networkId)),
                          bytes32(txList[7].toUint()),
                          bytes32(txList[8].toUint())
                      );
                  }
              
                  function decodeExit(bytes memory data)
                      internal
                      pure
                      returns (PlasmaExit memory)
                  {
                      (address owner, address token, uint256 amountOrTokenId, bytes32 txHash, bool isRegularExit) = abi
                          .decode(data, (address, address, uint256, bytes32, bool));
                      return
                          PlasmaExit(
                              amountOrTokenId,
                              txHash,
                              owner,
                              token,
                              isRegularExit,
                              address(0) /* predicate value is not required */
                          );
                  }
              
                  function decodeExitForProcessExit(bytes memory data)
                      internal
                      pure
                      returns (uint256 exitId, address token, address exitor, uint256 tokenId)
                  {
                      (exitId, token, exitor, tokenId) = abi.decode(
                          data,
                          (uint256, address, address, uint256)
                      );
                  }
              
                  function decodeInputUtxo(bytes memory data)
                      internal
                      pure
                      returns (uint256 age, address signer, address predicate, address token)
                  {
                      (age, signer, predicate, token) = abi.decode(
                          data,
                          (uint256, address, address, address)
                      );
                  }
              
              }
              
              contract IErcPredicate is IPredicate, PredicateUtils {
                  enum ExitType {Invalid, OutgoingTransfer, IncomingTransfer, Burnt}
              
                  struct ExitTxData {
                      uint256 amountOrToken;
                      bytes32 txHash;
                      address childToken;
                      address signer;
                      ExitType exitType;
                  }
              
                  struct ReferenceTxData {
                      uint256 closingBalance;
                      uint256 age;
                      address childToken;
                      address rootToken;
                  }
              
                  uint256 internal constant MAX_LOGS = 10;
              
                  constructor(address _withdrawManager, address _depositManager) public {
                      withdrawManager = IWithdrawManager(_withdrawManager);
                      depositManager = IDepositManager(_depositManager);
                  }
              }
              
              // File: contracts/root/withdrawManager/WithdrawManager.sol
              
              pragma solidity ^0.5.2;
              
              
              
              
              
              
              
              
              
              
              
              
              
              
              
              
              
              contract WithdrawManager is WithdrawManagerStorage, IWithdrawManager {
                  using RLPReader for bytes;
                  using RLPReader for RLPReader.RLPItem;
                  using Merkle for bytes32;
              
                  using ExitPayloadReader for bytes;
                  using ExitPayloadReader for ExitPayloadReader.ExitPayload;
                  using ExitPayloadReader for ExitPayloadReader.Receipt;
                  using ExitPayloadReader for ExitPayloadReader.Log;
                  using ExitPayloadReader for ExitPayloadReader.LogTopics;
              
                  modifier isBondProvided() {
                      require(msg.value == BOND_AMOUNT, "Invalid Bond amount");
                      _;
                  }
              
                  modifier isPredicateAuthorized() {
                      require(registry.predicates(msg.sender) != Registry.Type.Invalid, "PREDICATE_NOT_AUTHORIZED");
                      _;
                  }
              
                  modifier checkPredicateAndTokenMapping(address rootToken) {
                      Registry.Type _type = registry.predicates(msg.sender);
                      require(registry.rootToChildToken(rootToken) != address(0x0), "rootToken not supported");
                      if (_type == Registry.Type.ERC20) {
                          require(registry.isERC721(rootToken) == false, "Predicate supports only ERC20 tokens");
                      } else if (_type == Registry.Type.ERC721) {
                          require(registry.isERC721(rootToken) == true, "Predicate supports only ERC721 tokens");
                      } else if (_type == Registry.Type.Custom) {} else {
                          revert("PREDICATE_NOT_AUTHORIZED");
                      }
                      _;
                  }
              
                  /**
                   * @dev Receive bond for bonded exits
                   */
                  function() external payable {}
              
                  function createExitQueue(address token) external {
                      require(msg.sender == address(registry), "UNAUTHORIZED_REGISTRY_ONLY");
                      exitsQueues[token] = address(new PriorityQueue());
                  }
              
                  /**
                   During coverage tests verifyInclusion fails co compile with "stack too deep" error.
                   */
                  struct VerifyInclusionVars {
                      uint256 headerNumber;
                      uint256 blockNumber;
                      uint256 createdAt;
                      uint256 branchMask;
                      bytes32 txRoot;
                      bytes32 receiptRoot;
                      bytes branchMaskBytes;
                  }
              
                  /**
                   * @dev Verify the inclusion of the receipt in the checkpoint
                   * @param data RLP encoded data of the reference tx(s) that encodes the following fields for each tx
                   * headerNumber Header block number of which the reference tx was a part of
                   * blockProof Proof that the block header (in the child chain) is a leaf in the submitted merkle root
                   * blockNumber Block number of which the reference tx is a part of
                   * blockTime Reference tx block time
                   * blocktxRoot Transactions root of block
                   * blockReceiptsRoot Receipts root of block
                   * receipt Receipt of the reference transaction
                   * receiptProof Merkle proof of the reference receipt
                   * branchMask Merkle proof branchMask for the receipt
                   * logIndex Log Index to read from the receipt
                   * @param offset offset in the data array
                   * @param verifyTxInclusion Whether to also verify the inclusion of the raw tx in the txRoot
                   * @return ageOfInput Measure of the position of the receipt and the log in the child chain
                   */
                  function verifyInclusion(
                      bytes calldata data,
                      uint8 offset,
                      bool verifyTxInclusion
                  )
                      external
                      view
                      returns (
                          uint256 /* ageOfInput */
                      )
                  {
                      ExitPayloadReader.ExitPayload memory payload = data.toExitPayload();
                      VerifyInclusionVars memory vars;
              
                      vars.headerNumber = payload.getHeaderNumber();
                      vars.branchMaskBytes = payload.getBranchMaskAsBytes();
                      require(vars.branchMaskBytes[0] == 0, "incorrect mask");
                      vars.txRoot = payload.getTxRoot();
                      vars.receiptRoot = payload.getReceiptRoot();
                      require(
                          MerklePatriciaProof.verify(
                              payload.getReceipt().toBytes(),
                              vars.branchMaskBytes,
                              payload.getReceiptProof(),
                              vars.receiptRoot
                          ),
                          "INVALID_RECEIPT_MERKLE_PROOF"
                      );
              
                      if (verifyTxInclusion) {
                          require(
                              MerklePatriciaProof.verify(
                                  payload.getTx(),
                                  vars.branchMaskBytes,
                                  payload.getTxProof(), 
                                  vars.txRoot
                              ),
                              "INVALID_TX_MERKLE_PROOF"
                          );
                      }
              
                      vars.blockNumber = payload.getBlockNumber();
                      vars.createdAt = checkBlockMembershipInCheckpoint(
                          vars.blockNumber,
                          payload.getBlockTime(),
                          vars.txRoot,
                          vars.receiptRoot,
                          vars.headerNumber,
                          payload.getBlockProof()
                      );
              
                      vars.branchMask = payload.getBranchMaskAsUint();
                      require(
                          vars.branchMask & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000 == 0,
                          "Branch mask should be 32 bits"
                      );
                      // ageOfInput is denoted as
                      // 1 reserve bit (see last 2 lines in comment)
                      // 128 bits for exitableAt timestamp
                      // 95 bits for child block number
                      // 32 bits for receiptPos + logIndex * MAX_LOGS + oIndex
                      // In predicates, the exitId will be evaluated by shifting the ageOfInput left by 1 bit
                      // (Only in erc20Predicate) Last bit is to differentiate whether the sender or receiver of the in-flight tx is starting an exit
                      return (getExitableAt(vars.createdAt) << 127) | (vars.blockNumber << 32) | vars.branchMask;
                  }
              
                  function startExitWithDepositedTokens(
                      uint256 depositId,
                      address token,
                      uint256 amountOrToken
                  ) external payable isBondProvided {
                      // (bytes32 depositHash, uint256 createdAt) = getDepositManager().deposits(depositId);
                      // require(keccak256(abi.encodePacked(msg.sender, token, amountOrToken)) == depositHash, "UNAUTHORIZED_EXIT");
                      // uint256 ageOfInput = getExitableAt(createdAt) << 127 | (depositId % 10000 /* MAX_DEPOSITS */);
                      // uint256 exitId = ageOfInput << 1;
                      // address predicate = registry.isTokenMappedAndGetPredicate(token);
                      // _addExitToQueue(
                      //     msg.sender,
                      //     token,
                      //     amountOrToken,
                      //     bytes32(0), /* txHash */
                      //     false, /* isRegularExit */
                      //     exitId,
                      //     predicate
                      // );
                      // _addInput(
                      //     exitId,
                      //     ageOfInput,
                      //     msg.sender, /* utxoOwner */
                      //     predicate,
                      //     token
                      // );
                  }
              
                  function addExitToQueue(
                      address exitor,
                      address childToken,
                      address rootToken,
                      uint256 exitAmountOrTokenId,
                      bytes32 txHash,
                      bool isRegularExit,
                      uint256 priority
                  ) external checkPredicateAndTokenMapping(rootToken) {
                      require(registry.rootToChildToken(rootToken) == childToken, "INVALID_ROOT_TO_CHILD_TOKEN_MAPPING");
                      _addExitToQueue(exitor, rootToken, exitAmountOrTokenId, txHash, isRegularExit, priority, msg.sender);
                  }
              
                  function challengeExit(
                      uint256 exitId,
                      uint256 inputId,
                      bytes calldata challengeData,
                      address adjudicatorPredicate
                  ) external {
                      PlasmaExit storage exit = exits[exitId];
                      Input storage input = exit.inputs[inputId];
                      require(exit.owner != address(0x0) && input.utxoOwner != address(0x0), "Invalid exit or input id");
                      require(registry.predicates(adjudicatorPredicate) != Registry.Type.Invalid, "INVALID_PREDICATE");
                      require(
                          IPredicate(adjudicatorPredicate).verifyDeprecation(
                              encodeExit(exit),
                              encodeInputUtxo(inputId, input),
                              challengeData
                          ),
                          "Challenge failed"
                      );
                      // In the call to burn(exitId), there is an implicit check that prevents challenging the same exit twice
                      ExitNFT(exitNft).burn(exitId);
              
                      // Send bond amount to challenger
                      msg.sender.send(BOND_AMOUNT);
              
                      // delete exits[exitId];
                      emit ExitCancelled(exitId);
                  }
              
                  function processExits(address _token) public {
                      uint256 exitableAt;
                      uint256 exitId;
              
                      PriorityQueue exitQueue = PriorityQueue(exitsQueues[_token]);
              
                      while (exitQueue.currentSize() > 0 && gasleft() > ON_FINALIZE_GAS_LIMIT) {
                          (exitableAt, exitId) = exitQueue.getMin();
                          exitId = (exitableAt << 128) | exitId;
                          PlasmaExit memory currentExit = exits[exitId];
              
                          // Stop processing exits if the exit that is next is queue is still in its challenge period
                          if (exitableAt > block.timestamp) return;
              
                          exitQueue.delMin();
                          // If the exitNft was deleted as a result of a challenge, skip processing this exit
                          if (!exitNft.exists(exitId)) continue;
                          address exitor = exitNft.ownerOf(exitId);
                          exits[exitId].owner = exitor;
                          exitNft.burn(exitId);
                          // If finalizing a particular exit is reverting, it will block any following exits from being processed.
                          // Hence, call predicate.onFinalizeExit in a revertless manner.
                          // (bool success, bytes memory result) =
                          currentExit.predicate.call(
                              abi.encodeWithSignature("onFinalizeExit(bytes)", encodeExitForProcessExit(exitId))
                          );
              
                          emit Withdraw(exitId, exitor, _token, currentExit.receiptAmountOrNFTId);
              
                          if (!currentExit.isRegularExit) {
                              // return the bond amount if this was a MoreVp style exit
                              address(uint160(exitor)).send(BOND_AMOUNT);
                          }
                      }
                  }
              
                  function processExitsBatch(address[] calldata _tokens) external {
                      for (uint256 i = 0; i < _tokens.length; i++) {
                          processExits(_tokens[i]);
                      }
                  }
              
                  /**
                   * @dev Add a state update (UTXO style input) to an exit
                   * @param exitId Exit ID
                   * @param age age of the UTXO style input
                   * @param utxoOwner User for whom the input acts as a proof-of-funds
                   * (alternate expression) User who could have potentially spent this UTXO
                   * @param token Token (Think of it like Utxo color)
                   */
                  function addInput(
                      uint256 exitId,
                      uint256 age,
                      address utxoOwner,
                      address token
                  ) external isPredicateAuthorized {
                      PlasmaExit storage exitObject = exits[exitId];
                      require(exitObject.owner != address(0x0), "INVALID_EXIT_ID");
                      _addInput(
                          exitId,
                          age,
                          utxoOwner,
                          /* predicate */
                          msg.sender,
                          token
                      );
                  }
              
                  function _addInput(
                      uint256 exitId,
                      uint256 age,
                      address utxoOwner,
                      address predicate,
                      address token
                  ) internal {
                      exits[exitId].inputs[age] = Input(utxoOwner, predicate, token);
                      emit ExitUpdated(exitId, age, utxoOwner);
                  }
              
                  function encodeExit(PlasmaExit storage exit) internal view returns (bytes memory) {
                      return
                          abi.encode(
                              exit.owner,
                              registry.rootToChildToken(exit.token),
                              exit.receiptAmountOrNFTId,
                              exit.txHash,
                              exit.isRegularExit
                          );
                  }
              
                  function encodeExitForProcessExit(uint256 exitId) internal view returns (bytes memory) {
                      PlasmaExit storage exit = exits[exitId];
                      return abi.encode(exitId, exit.token, exit.owner, exit.receiptAmountOrNFTId);
                  }
              
                  function encodeInputUtxo(uint256 age, Input storage input) internal view returns (bytes memory) {
                      return abi.encode(age, input.utxoOwner, input.predicate, registry.rootToChildToken(input.token));
                  }
              
                  function _addExitToQueue(
                      address exitor,
                      address rootToken,
                      uint256 exitAmountOrTokenId,
                      bytes32 txHash,
                      bool isRegularExit,
                      uint256 exitId,
                      address predicate
                  ) internal {
                      require(exits[exitId].token == address(0x0), "EXIT_ALREADY_EXISTS");
                      exits[exitId] = PlasmaExit(
                          exitAmountOrTokenId,
                          txHash,
                          exitor,
                          rootToken,
                          isRegularExit,
                          predicate
                      );
                      PlasmaExit storage _exitObject = exits[exitId];
              
                      bytes32 key = getKey(_exitObject.token, _exitObject.owner, _exitObject.receiptAmountOrNFTId);
              
                      if (isRegularExit) {
                          require(!isKnownExit[uint128(exitId)], "KNOWN_EXIT");
                          isKnownExit[uint128(exitId)] = true;
                      } else {
                          // a user cannot start 2 MoreVP exits for the same erc20 token or nft
                          require(ownerExits[key] == 0, "EXIT_ALREADY_IN_PROGRESS");
                          ownerExits[key] = exitId;
                      }
              
                      PriorityQueue queue = PriorityQueue(exitsQueues[_exitObject.token]);
              
                      // Way priority queue is implemented is such that it expects 2 uint256 params with most significant 128 bits masked out
                      // This is a workaround to split exitId, which otherwise is conclusive in itself
                      // exitId >> 128 gives 128 most significant bits
                      // uint256(uint128(exitId)) gives 128 least significant bits
                      // @todo Fix this mess
                      queue.insert(exitId >> 128, uint256(uint128(exitId)));
              
                      // create exit nft
                      exitNft.mint(_exitObject.owner, exitId);
                      emit ExitStarted(exitor, exitId, rootToken, exitAmountOrTokenId, isRegularExit);
                  }
              
                  function checkBlockMembershipInCheckpoint(
                      uint256 blockNumber,
                      uint256 blockTime,
                      bytes32 txRoot,
                      bytes32 receiptRoot,
                      uint256 headerNumber,
                      bytes memory blockProof
                  )
                      internal
                      view
                      returns (
                          uint256 /* createdAt */
                      )
                  {
                      (bytes32 headerRoot, uint256 startBlock, , uint256 createdAt, ) = rootChain.headerBlocks(headerNumber);
                      require(
                          keccak256(abi.encodePacked(blockNumber, blockTime, txRoot, receiptRoot)).checkMembership(
                              blockNumber - startBlock,
                              headerRoot,
                              blockProof
                          ),
                          "WITHDRAW_BLOCK_NOT_A_PART_OF_SUBMITTED_HEADER"
                      );
                      return createdAt;
                  }
              
                  function getKey(
                      address token,
                      address exitor,
                      uint256 amountOrToken
                  ) internal view returns (bytes32 key) {
                      if (registry.isERC721(token)) {
                          key = keccak256(abi.encodePacked(token, exitor, amountOrToken));
                      } else {
                          // validate amount
                          require(amountOrToken > 0, "CANNOT_EXIT_ZERO_AMOUNTS");
                          key = keccak256(abi.encodePacked(token, exitor));
                      }
                  }
              
                  function getDepositManager() internal view returns (DepositManager) {
                      return DepositManager(address(uint160(registry.getDepositManagerAddress())));
                  }
              
                  function getExitableAt(uint256 createdAt) internal view returns (uint256) {
                      return Math.max(createdAt + 2 * HALF_EXIT_PERIOD, now + HALF_EXIT_PERIOD);
                  }
              
                  // Housekeeping function. @todo remove later
                  function updateExitPeriod(uint256 halfExitPeriod) public onlyOwner {
                      emit ExitPeriodUpdate(HALF_EXIT_PERIOD, halfExitPeriod);
                      HALF_EXIT_PERIOD = halfExitPeriod;
                  }
              }

              File 6 of 9: PriorityQueue
              /**
              Matic network contracts
              */
              
              pragma solidity ^0.5.2;
              
              
              /**
               * @title Ownable
               * @dev The Ownable contract has an owner address, and provides basic authorization control
               * functions, this simplifies the implementation of "user permissions".
               */
              contract Ownable {
                  address private _owner;
              
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
              
                  /**
                   * @dev The Ownable constructor sets the original `owner` of the contract to the sender
                   * account.
                   */
                  constructor () internal {
                      _owner = msg.sender;
                      emit OwnershipTransferred(address(0), _owner);
                  }
              
                  /**
                   * @return the address of the owner.
                   */
                  function owner() public view returns (address) {
                      return _owner;
                  }
              
                  /**
                   * @dev Throws if called by any account other than the owner.
                   */
                  modifier onlyOwner() {
                      require(isOwner());
                      _;
                  }
              
                  /**
                   * @return true if `msg.sender` is the owner of the contract.
                   */
                  function isOwner() public view returns (bool) {
                      return msg.sender == _owner;
                  }
              
                  /**
                   * @dev Allows the current owner to relinquish control of the contract.
                   * It will not be possible to call the functions with the `onlyOwner`
                   * modifier anymore.
                   * @notice Renouncing ownership will leave the contract without an owner,
                   * thereby removing any functionality that is only available to the owner.
                   */
                  function renounceOwnership() public onlyOwner {
                      emit OwnershipTransferred(_owner, address(0));
                      _owner = address(0);
                  }
              
                  /**
                   * @dev Allows the current owner to transfer control of the contract to a newOwner.
                   * @param newOwner The address to transfer ownership to.
                   */
                  function transferOwnership(address newOwner) public onlyOwner {
                      _transferOwnership(newOwner);
                  }
              
                  /**
                   * @dev Transfers control of the contract to a newOwner.
                   * @param newOwner The address to transfer ownership to.
                   */
                  function _transferOwnership(address newOwner) internal {
                      require(newOwner != address(0));
                      emit OwnershipTransferred(_owner, newOwner);
                      _owner = newOwner;
                  }
              }
              
              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;
                  }
              }
              
              /**
               * @title PriorityQueue
               * @dev A priority queue implementation.
               */
              contract PriorityQueue is Ownable {
                  using SafeMath for uint256;
              
                  uint256[] heapList;
                  uint256 public currentSize;
              
                  constructor() public {
                      heapList = [0];
                  }
              
                  /**
                * @dev Inserts an element into the priority queue.
                * @param _priority Priority to insert.
                * @param _value Some additional value.
                */
                  function insert(uint256 _priority, uint256 _value) public onlyOwner {
                      uint256 element = (_priority << 128) | _value;
                      heapList.push(element);
                      currentSize = currentSize.add(1);
                      _percUp(currentSize);
                  }
              
                  /**
                * @dev Returns the top element of the heap.
                * @return The smallest element in the priority queue.
                */
                  function getMin() public view returns (uint256, uint256) {
                      return _splitElement(heapList[1]);
                  }
              
                  /**
                * @dev Deletes the top element of the heap and shifts everything up.
                * @return The smallest element in the priorty queue.
                */
                  function delMin() public onlyOwner returns (uint256, uint256) {
                      uint256 retVal = heapList[1];
                      heapList[1] = heapList[currentSize];
                      delete heapList[currentSize];
                      currentSize = currentSize.sub(1);
                      _percDown(1);
                      heapList.length = heapList.length.sub(1);
                      return _splitElement(retVal);
                  }
              
                  /**
                * @dev Determines the minimum child of a given node in the tree.
                * @param _index Index of the node in the tree.
                * @return The smallest child node.
                */
                  function _minChild(uint256 _index) private view returns (uint256) {
                      if (_index.mul(2).add(1) > currentSize) {
                          return _index.mul(2);
                      } else {
                          if (heapList[_index.mul(2)] < heapList[_index.mul(2).add(1)]) {
                              return _index.mul(2);
                          } else {
                              return _index.mul(2).add(1);
                          }
                      }
                  }
              
                  /**
                 * @dev Bubbles the element at some index up.
                 */
                  function _percUp(uint256 _index) private {
                      uint256 index = _index;
                      uint256 j = index;
                      uint256 newVal = heapList[index];
              
                      while (newVal < heapList[index.div(2)]) {
                          heapList[index] = heapList[index.div(2)];
                          index = index.div(2);
                      }
              
                      if (index != j) {
                          heapList[index] = newVal;
                      }
                  }
              
                  /**
                 * @dev Bubbles the element at some index down.
                 */
                  function _percDown(uint256 _index) private {
                      uint256 index = _index;
                      uint256 j = index;
                      uint256 newVal = heapList[index];
                      uint256 mc = _minChild(index);
                      while (mc <= currentSize && newVal > heapList[mc]) {
                          heapList[index] = heapList[mc];
                          index = mc;
                          mc = _minChild(index);
                      }
              
                      if (index != j) {
                          heapList[index] = newVal;
                      }
                  }
              
                  /**
                 * @dev Split an element into its priority and value.
                 * @param _element Element to decode.
                 * @return A tuple containing the priority and value.
                 */
                  function _splitElement(uint256 _element)
                      private
                      pure
                      returns (uint256, uint256)
                  {
                      uint256 priority = _element >> 128;
                      uint256 value = uint256(uint128(_element));
                      return (priority, value);
                  }
              }

              File 7 of 9: Registry
              /**
              Matic network contracts
              */
              
              pragma solidity ^0.5.2;
              
              
              interface IGovernance {
                  function update(address target, bytes calldata data) external;
              }
              
              contract Governable {
                  IGovernance public governance;
              
                  constructor(address _governance) public {
                      governance = IGovernance(_governance);
                  }
              
                  modifier onlyGovernance() {
                      require(
                          msg.sender == address(governance),
                          "Only governance contract is authorized"
                      );
                      _;
                  }
              }
              
              contract IWithdrawManager {
                  function createExitQueue(address token) external;
              
                  function verifyInclusion(
                      bytes calldata data,
                      uint8 offset,
                      bool verifyTxInclusion
                  ) external view returns (uint256 age);
              
                  function addExitToQueue(
                      address exitor,
                      address childToken,
                      address rootToken,
                      uint256 exitAmountOrTokenId,
                      bytes32 txHash,
                      bool isRegularExit,
                      uint256 priority
                  ) external;
              
                  function addInput(
                      uint256 exitId,
                      uint256 age,
                      address utxoOwner,
                      address token
                  ) external;
              
                  function challengeExit(
                      uint256 exitId,
                      uint256 inputId,
                      bytes calldata challengeData,
                      address adjudicatorPredicate
                  ) external;
              }
              
              contract Registry is Governable {
                  // @todo hardcode constants
                  bytes32 private constant WETH_TOKEN = keccak256("wethToken");
                  bytes32 private constant DEPOSIT_MANAGER = keccak256("depositManager");
                  bytes32 private constant STAKE_MANAGER = keccak256("stakeManager");
                  bytes32 private constant VALIDATOR_SHARE = keccak256("validatorShare");
                  bytes32 private constant WITHDRAW_MANAGER = keccak256("withdrawManager");
                  bytes32 private constant CHILD_CHAIN = keccak256("childChain");
                  bytes32 private constant STATE_SENDER = keccak256("stateSender");
                  bytes32 private constant SLASHING_MANAGER = keccak256("slashingManager");
              
                  address public erc20Predicate;
                  address public erc721Predicate;
              
                  mapping(bytes32 => address) public contractMap;
                  mapping(address => address) public rootToChildToken;
                  mapping(address => address) public childToRootToken;
                  mapping(address => bool) public proofValidatorContracts;
                  mapping(address => bool) public isERC721;
              
                  enum Type {Invalid, ERC20, ERC721, Custom}
                  struct Predicate {
                      Type _type;
                  }
                  mapping(address => Predicate) public predicates;
              
                  event TokenMapped(address indexed rootToken, address indexed childToken);
                  event ProofValidatorAdded(address indexed validator, address indexed from);
                  event ProofValidatorRemoved(address indexed validator, address indexed from);
                  event PredicateAdded(address indexed predicate, address indexed from);
                  event PredicateRemoved(address indexed predicate, address indexed from);
                  event ContractMapUpdated(bytes32 indexed key, address indexed previousContract, address indexed newContract);
              
                  constructor(address _governance) public Governable(_governance) {}
              
                  function updateContractMap(bytes32 _key, address _address) external onlyGovernance {
                      emit ContractMapUpdated(_key, contractMap[_key], _address);
                      contractMap[_key] = _address;
                  }
              
                  /**
                   * @dev Map root token to child token
                   * @param _rootToken Token address on the root chain
                   * @param _childToken Token address on the child chain
                   * @param _isERC721 Is the token being mapped ERC721
                   */
                  function mapToken(
                      address _rootToken,
                      address _childToken,
                      bool _isERC721
                  ) external onlyGovernance {
                      require(_rootToken != address(0x0) && _childToken != address(0x0), "INVALID_TOKEN_ADDRESS");
                      rootToChildToken[_rootToken] = _childToken;
                      childToRootToken[_childToken] = _rootToken;
                      isERC721[_rootToken] = _isERC721;
                      IWithdrawManager(contractMap[WITHDRAW_MANAGER]).createExitQueue(_rootToken);
                      emit TokenMapped(_rootToken, _childToken);
                  }
              
                  function addErc20Predicate(address predicate) public onlyGovernance {
                      require(predicate != address(0x0), "Can not add null address as predicate");
                      erc20Predicate = predicate;
                      addPredicate(predicate, Type.ERC20);
                  }
              
                  function addErc721Predicate(address predicate) public onlyGovernance {
                      erc721Predicate = predicate;
                      addPredicate(predicate, Type.ERC721);
                  }
              
                  function addPredicate(address predicate, Type _type) public onlyGovernance {
                      require(predicates[predicate]._type == Type.Invalid, "Predicate already added");
                      predicates[predicate]._type = _type;
                      emit PredicateAdded(predicate, msg.sender);
                  }
              
                  function removePredicate(address predicate) public onlyGovernance {
                      require(predicates[predicate]._type != Type.Invalid, "Predicate does not exist");
                      delete predicates[predicate];
                      emit PredicateRemoved(predicate, msg.sender);
                  }
              
                  function getValidatorShareAddress() public view returns (address) {
                      return contractMap[VALIDATOR_SHARE];
                  }
              
                  function getWethTokenAddress() public view returns (address) {
                      return contractMap[WETH_TOKEN];
                  }
              
                  function getDepositManagerAddress() public view returns (address) {
                      return contractMap[DEPOSIT_MANAGER];
                  }
              
                  function getStakeManagerAddress() public view returns (address) {
                      return contractMap[STAKE_MANAGER];
                  }
              
                  function getSlashingManagerAddress() public view returns (address) {
                      return contractMap[SLASHING_MANAGER];
                  }
              
                  function getWithdrawManagerAddress() public view returns (address) {
                      return contractMap[WITHDRAW_MANAGER];
                  }
              
                  function getChildChainAndStateSender() public view returns (address, address) {
                      return (contractMap[CHILD_CHAIN], contractMap[STATE_SENDER]);
                  }
              
                  function isTokenMapped(address _token) public view returns (bool) {
                      return rootToChildToken[_token] != address(0x0);
                  }
              
                  function isTokenMappedAndIsErc721(address _token) public view returns (bool) {
                      require(isTokenMapped(_token), "TOKEN_NOT_MAPPED");
                      return isERC721[_token];
                  }
              
                  function isTokenMappedAndGetPredicate(address _token) public view returns (address) {
                      if (isTokenMappedAndIsErc721(_token)) {
                          return erc721Predicate;
                      }
                      return erc20Predicate;
                  }
              
                  function isChildTokenErc721(address childToken) public view returns (bool) {
                      address rootToken = childToRootToken[childToken];
                      require(rootToken != address(0x0), "Child token is not mapped");
                      return isERC721[rootToken];
                  }
              }

              File 8 of 9: ERC20PredicateBurnOnly
              // File: openzeppelin-solidity/contracts/math/SafeMath.sol
              
              pragma solidity ^0.5.2;
              
              /**
               * @title SafeMath
               * @dev Unsigned math operations with safety checks that revert on error
               */
              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;
                  }
              }
              
              // File: contracts/common/lib/BytesLib.sol
              
              pragma solidity ^0.5.2;
              
              
              library BytesLib {
                  function concat(bytes memory _preBytes, bytes memory _postBytes)
                      internal
                      pure
                      returns (bytes memory)
                  {
                      bytes memory tempBytes;
                      assembly {
                          // Get a location of some free memory and store it in tempBytes as
                          // Solidity does for memory variables.
                          tempBytes := mload(0x40)
              
                          // Store the length of the first bytes array at the beginning of
                          // the memory for tempBytes.
                          let length := mload(_preBytes)
                          mstore(tempBytes, length)
              
                          // Maintain a memory counter for the current write location in the
                          // temp bytes array by adding the 32 bytes for the array length to
                          // the starting location.
                          let mc := add(tempBytes, 0x20)
                          // Stop copying when the memory counter reaches the length of the
                          // first bytes array.
                          let end := add(mc, length)
              
                          for {
                              // Initialize a copy counter to the start of the _preBytes data,
                              // 32 bytes into its memory.
                              let cc := add(_preBytes, 0x20)
                          } lt(mc, end) {
                              // Increase both counters by 32 bytes each iteration.
                              mc := add(mc, 0x20)
                              cc := add(cc, 0x20)
                          } {
                              // Write the _preBytes data into the tempBytes memory 32 bytes
                              // at a time.
                              mstore(mc, mload(cc))
                          }
              
                          // Add the length of _postBytes to the current length of tempBytes
                          // and store it as the new length in the first 32 bytes of the
                          // tempBytes memory.
                          length := mload(_postBytes)
                          mstore(tempBytes, add(length, mload(tempBytes)))
              
                          // Move the memory counter back from a multiple of 0x20 to the
                          // actual end of the _preBytes data.
                          mc := end
                          // Stop copying when the memory counter reaches the new combined
                          // length of the arrays.
                          end := add(mc, length)
              
                          for {
                              let cc := add(_postBytes, 0x20)
                          } lt(mc, end) {
                              mc := add(mc, 0x20)
                              cc := add(cc, 0x20)
                          } {
                              mstore(mc, mload(cc))
                          }
              
                          // Update the free-memory pointer by padding our last write location
                          // to 32 bytes: add 31 bytes to the end of tempBytes to move to the
                          // next 32 byte block, then round down to the nearest multiple of
                          // 32. If the sum of the length of the two arrays is zero then add
                          // one before rounding down to leave a blank 32 bytes (the length block with 0).
                          mstore(
                              0x40,
                              and(
                                  add(add(end, iszero(add(length, mload(_preBytes)))), 31),
                                  not(31) // Round down to the nearest 32 bytes.
                              )
                          )
                      }
                      return tempBytes;
                  }
              
                  function slice(bytes memory _bytes, uint256 _start, uint256 _length)
                      internal
                      pure
                      returns (bytes memory)
                  {
                      require(_bytes.length >= (_start + _length));
                      bytes memory tempBytes;
                      assembly {
                          switch iszero(_length)
                              case 0 {
                                  // Get a location of some free memory and store it in tempBytes as
                                  // Solidity does for memory variables.
                                  tempBytes := mload(0x40)
              
                                  // The first word of the slice result is potentially a partial
                                  // word read from the original array. To read it, we calculate
                                  // the length of that partial word and start copying that many
                                  // bytes into the array. The first word we copy will start with
                                  // data we don't care about, but the last `lengthmod` bytes will
                                  // land at the beginning of the contents of the new array. When
                                  // we're done copying, we overwrite the full first word with
                                  // the actual length of the slice.
                                  let lengthmod := and(_length, 31)
              
                                  // The multiplication in the next line is necessary
                                  // because when slicing multiples of 32 bytes (lengthmod == 0)
                                  // the following copy loop was copying the origin's length
                                  // and then ending prematurely not copying everything it should.
                                  let mc := add(
                                      add(tempBytes, lengthmod),
                                      mul(0x20, iszero(lengthmod))
                                  )
                                  let end := add(mc, _length)
              
                                  for {
                                      // The multiplication in the next line has the same exact purpose
                                      // as the one above.
                                      let cc := add(
                                          add(
                                              add(_bytes, lengthmod),
                                              mul(0x20, iszero(lengthmod))
                                          ),
                                          _start
                                      )
                                  } lt(mc, end) {
                                      mc := add(mc, 0x20)
                                      cc := add(cc, 0x20)
                                  } {
                                      mstore(mc, mload(cc))
                                  }
              
                                  mstore(tempBytes, _length)
              
                                  //update free-memory pointer
                                  //allocating the array padded to 32 bytes like the compiler does now
                                  mstore(0x40, and(add(mc, 31), not(31)))
                              }
                              //if we want a zero-length slice let's just return a zero-length array
                              default {
                                  tempBytes := mload(0x40)
                                  mstore(0x40, add(tempBytes, 0x20))
                              }
                      }
              
                      return tempBytes;
                  }
              
                  // Pad a bytes array to 32 bytes
                  function leftPad(bytes memory _bytes) internal pure returns (bytes memory) {
                      // may underflow if bytes.length < 32. Hence using SafeMath.sub
                      bytes memory newBytes = new bytes(SafeMath.sub(32, _bytes.length));
                      return concat(newBytes, _bytes);
                  }
              
                  function toBytes32(bytes memory b) internal pure returns (bytes32) {
                      require(b.length >= 32, "Bytes array should atleast be 32 bytes");
                      bytes32 out;
                      for (uint256 i = 0; i < 32; i++) {
                          out |= bytes32(b[i] & 0xFF) >> (i * 8);
                      }
                      return out;
                  }
              
                  function toBytes4(bytes memory b) internal pure returns (bytes4 result) {
                      assembly {
                          result := mload(add(b, 32))
                      }
                  }
              
                  function fromBytes32(bytes32 x) internal pure returns (bytes memory) {
                      bytes memory b = new bytes(32);
                      for (uint256 i = 0; i < 32; i++) {
                          b[i] = bytes1(uint8(uint256(x) / (2**(8 * (31 - i)))));
                      }
                      return b;
                  }
              
                  function fromUint(uint256 _num) internal pure returns (bytes memory _ret) {
                      _ret = new bytes(32);
                      assembly {
                          mstore(add(_ret, 32), _num)
                      }
                  }
              
                  function toUint(bytes memory _bytes, uint256 _start)
                      internal
                      pure
                      returns (uint256)
                  {
                      require(_bytes.length >= (_start + 32));
                      uint256 tempUint;
                      assembly {
                          tempUint := mload(add(add(_bytes, 0x20), _start))
                      }
                      return tempUint;
                  }
              
                  function toAddress(bytes memory _bytes, uint256 _start)
                      internal
                      pure
                      returns (address)
                  {
                      require(_bytes.length >= (_start + 20));
                      address tempAddress;
                      assembly {
                          tempAddress := div(
                              mload(add(add(_bytes, 0x20), _start)),
                              0x1000000000000000000000000
                          )
                      }
              
                      return tempAddress;
                  }
              }
              
              // File: contracts/common/lib/Common.sol
              
              pragma solidity ^0.5.2;
              
              
              library Common {
                  function getV(bytes memory v, uint16 chainId) public pure returns (uint8) {
                      if (chainId > 0) {
                          return
                              uint8(
                                  BytesLib.toUint(BytesLib.leftPad(v), 0) - (chainId * 2) - 8
                              );
                      } else {
                          return uint8(BytesLib.toUint(BytesLib.leftPad(v), 0));
                      }
                  }
              
                  //assemble the given address bytecode. If bytecode exists then the _addr is a contract.
                  function isContract(address _addr) public view returns (bool) {
                      uint256 length;
                      assembly {
                          //retrieve the size of the code on target address, this needs assembly
                          length := extcodesize(_addr)
                      }
                      return (length > 0);
                  }
              
                  // convert bytes to uint8
                  function toUint8(bytes memory _arg) public pure returns (uint8) {
                      return uint8(_arg[0]);
                  }
              
                  function toUint16(bytes memory _arg) public pure returns (uint16) {
                      return (uint16(uint8(_arg[0])) << 8) | uint16(uint8(_arg[1]));
                  }
              }
              
              // File: openzeppelin-solidity/contracts/math/Math.sol
              
              pragma solidity ^0.5.2;
              
              /**
               * @title Math
               * @dev Assorted math operations
               */
              library Math {
                  /**
                   * @dev Returns the largest of two numbers.
                   */
                  function max(uint256 a, uint256 b) internal pure returns (uint256) {
                      return a >= b ? a : b;
                  }
              
                  /**
                   * @dev Returns the smallest of two numbers.
                   */
                  function min(uint256 a, uint256 b) internal pure returns (uint256) {
                      return a < b ? a : b;
                  }
              
                  /**
                   * @dev Calculates the average of two numbers. Since these are integers,
                   * averages of an even and odd number cannot be represented, and will be
                   * rounded down.
                   */
                  function average(uint256 a, uint256 b) internal pure returns (uint256) {
                      // (a + b) / 2 can overflow, so we distribute
                      return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
                  }
              }
              
              // File: contracts/common/lib/RLPEncode.sol
              
              // Library for RLP encoding a list of bytes arrays.
              // Modeled after ethereumjs/rlp (https://github.com/ethereumjs/rlp)
              // [Very] modified version of Sam Mayo's library.
              pragma solidity ^0.5.2;
              
              
              library RLPEncode {
                  // Encode an item (bytes memory)
                  function encodeItem(bytes memory self)
                      internal
                      pure
                      returns (bytes memory)
                  {
                      bytes memory encoded;
                      if (self.length == 1 && uint8(self[0] & 0xFF) < 0x80) {
                          encoded = new bytes(1);
                          encoded = self;
                      } else {
                          encoded = BytesLib.concat(encodeLength(self.length, 128), self);
                      }
                      return encoded;
                  }
              
                  // Encode a list of items
                  function encodeList(bytes[] memory self)
                      internal
                      pure
                      returns (bytes memory)
                  {
                      bytes memory encoded;
                      for (uint256 i = 0; i < self.length; i++) {
                          encoded = BytesLib.concat(encoded, encodeItem(self[i]));
                      }
                      return BytesLib.concat(encodeLength(encoded.length, 192), encoded);
                  }
              
                  // Hack to encode nested lists. If you have a list as an item passed here, included
                  // pass = true in that index. E.g.
                  // [item, list, item] --> pass = [false, true, false]
                  // function encodeListWithPasses(bytes[] memory self, bool[] pass) internal pure returns (bytes memory) {
                  //   bytes memory encoded;
                  //   for (uint i=0; i < self.length; i++) {
                  // 		if (pass[i] == true) {
                  // 			encoded = BytesLib.concat(encoded, self[i]);
                  // 		} else {
                  // 			encoded = BytesLib.concat(encoded, encodeItem(self[i]));
                  // 		}
                  //   }
                  //   return BytesLib.concat(encodeLength(encoded.length, 192), encoded);
                  // }
              
                  // Generate the prefix for an item or the entire list based on RLP spec
                  function encodeLength(uint256 L, uint256 offset)
                      internal
                      pure
                      returns (bytes memory)
                  {
                      if (L < 56) {
                          bytes memory prefix = new bytes(1);
                          prefix[0] = bytes1(uint8(L + offset));
                          return prefix;
                      } else {
                          // lenLen is the length of the hex representation of the data length
                          uint256 lenLen;
                          uint256 i = 0x1;
              
                          while (L / i != 0) {
                              lenLen++;
                              i *= 0x100;
                          }
              
                          bytes memory prefix0 = getLengthBytes(offset + 55 + lenLen);
                          bytes memory prefix1 = getLengthBytes(L);
                          return BytesLib.concat(prefix0, prefix1);
                      }
                  }
              
                  function getLengthBytes(uint256 x) internal pure returns (bytes memory b) {
                      // Figure out if we need 1 or two bytes to express the length.
                      // 1 byte gets us to max 255
                      // 2 bytes gets us to max 65535 (no payloads will be larger than this)
                      uint256 nBytes = 1;
                      if (x > 255) {
                          nBytes = 2;
                      }
              
                      b = new bytes(nBytes);
                      // Encode the length and return it
                      for (uint256 i = 0; i < nBytes; i++) {
                          b[i] = bytes1(uint8(x / (2**(8 * (nBytes - 1 - i)))));
                      }
                  }
              }
              
              // File: solidity-rlp/contracts/RLPReader.sol
              
              /*
              * @author Hamdi Allam [email protected]
              * Please reach out with any questions or concerns
              */
              pragma solidity ^0.5.0;
              
              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 item RLP encoded bytes
                  */
                  function rlpLen(RLPItem memory item) internal pure returns (uint) {
                      return item.len;
                  }
              
                  /*
                  * @param item RLP encoded bytes
                  */
                  function payloadLen(RLPItem memory item) internal pure returns (uint) {
                      return item.len - _payloadOffset(item.memPtr);
                  }
              
                  /*
                  * @param item RLP encoded list in bytes
                  */
                  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;
                      }
              
                      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;
                  }
              
                  /** 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 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))
                      }
              
                      return result == 0 ? false : 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 offset = _payloadOffset(item.memPtr);
                      uint len = item.len - offset;
              
                      uint result;
                      uint memPtr = item.memPtr + offset;
                      assembly {
                          result := mload(memPtr)
              
                          // shfit to the correct location if neccesary
                          if lt(len, 32) {
                              result := div(result, exp(256, sub(32, len)))
                          }
                      }
              
                      return result;
                  }
              
                  // enforces 32 byte length
                  function toUintStrict(RLPItem memory item) internal pure returns (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 offset = _payloadOffset(item.memPtr);
                      uint len = item.len - offset; // data length
                      bytes memory result = new bytes(len);
              
                      uint destPtr;
                      assembly {
                          destPtr := add(0x20, result)
                      }
              
                      copy(item.memPtr + offset, destPtr, len);
                      return result;
                  }
              
                  /*
                  * Private Helpers
                  */
              
                  // @return number of payload items inside an encoded list.
                  function numItems(RLPItem memory item) private pure returns (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;
                      }
              
                      // 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))
                      }
                  }
              }
              
              // File: contracts/common/lib/ExitPayloadReader.sol
              
              pragma solidity 0.5.17;
              
              
              
              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;
                }
              
                function toExitPayload(bytes memory data)
                      internal
                      pure
                      returns (ExitPayload memory)
                  {
                      RLPReader.RLPItem[] memory payloadData = data
                          .toRlpItem()
                          .toList();
              
                      return ExitPayload(payloadData);
                  }
              
                  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 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();
                  }
              
                  function getTx(ExitPayload memory payload) internal pure returns(bytes memory) {
                    return payload.data[10].toBytes();
                  }
              
                  function getTxProof(ExitPayload memory payload) internal pure returns(bytes memory) {
                    return payload.data[11].toBytes();
                  }
                  
                  // 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];
                  }
              }
              
              // File: contracts/root/withdrawManager/IWithdrawManager.sol
              
              pragma solidity ^0.5.2;
              
              contract IWithdrawManager {
                  function createExitQueue(address token) external;
              
                  function verifyInclusion(
                      bytes calldata data,
                      uint8 offset,
                      bool verifyTxInclusion
                  ) external view returns (uint256 age);
              
                  function addExitToQueue(
                      address exitor,
                      address childToken,
                      address rootToken,
                      uint256 exitAmountOrTokenId,
                      bytes32 txHash,
                      bool isRegularExit,
                      uint256 priority
                  ) external;
              
                  function addInput(
                      uint256 exitId,
                      uint256 age,
                      address utxoOwner,
                      address token
                  ) external;
              
                  function challengeExit(
                      uint256 exitId,
                      uint256 inputId,
                      bytes calldata challengeData,
                      address adjudicatorPredicate
                  ) external;
              }
              
              // File: contracts/root/depositManager/IDepositManager.sol
              
              pragma solidity ^0.5.2;
              
              interface IDepositManager {
                  function depositEther() external payable;
                  function transferAssets(
                      address _token,
                      address _user,
                      uint256 _amountOrNFTId
                  ) external;
                  function depositERC20(address _token, uint256 _amount) external;
                  function depositERC721(address _token, uint256 _tokenId) external;
              }
              
              // File: openzeppelin-solidity/contracts/ownership/Ownable.sol
              
              pragma solidity ^0.5.2;
              
              /**
               * @title Ownable
               * @dev The Ownable contract has an owner address, and provides basic authorization control
               * functions, this simplifies the implementation of "user permissions".
               */
              contract Ownable {
                  address private _owner;
              
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
              
                  /**
                   * @dev The Ownable constructor sets the original `owner` of the contract to the sender
                   * account.
                   */
                  constructor () internal {
                      _owner = msg.sender;
                      emit OwnershipTransferred(address(0), _owner);
                  }
              
                  /**
                   * @return the address of the owner.
                   */
                  function owner() public view returns (address) {
                      return _owner;
                  }
              
                  /**
                   * @dev Throws if called by any account other than the owner.
                   */
                  modifier onlyOwner() {
                      require(isOwner());
                      _;
                  }
              
                  /**
                   * @return true if `msg.sender` is the owner of the contract.
                   */
                  function isOwner() public view returns (bool) {
                      return msg.sender == _owner;
                  }
              
                  /**
                   * @dev Allows the current owner to relinquish control of the contract.
                   * It will not be possible to call the functions with the `onlyOwner`
                   * modifier anymore.
                   * @notice Renouncing ownership will leave the contract without an owner,
                   * thereby removing any functionality that is only available to the owner.
                   */
                  function renounceOwnership() public onlyOwner {
                      emit OwnershipTransferred(_owner, address(0));
                      _owner = address(0);
                  }
              
                  /**
                   * @dev Allows the current owner to transfer control of the contract to a newOwner.
                   * @param newOwner The address to transfer ownership to.
                   */
                  function transferOwnership(address newOwner) public onlyOwner {
                      _transferOwnership(newOwner);
                  }
              
                  /**
                   * @dev Transfers control of the contract to a newOwner.
                   * @param newOwner The address to transfer ownership to.
                   */
                  function _transferOwnership(address newOwner) internal {
                      require(newOwner != address(0));
                      emit OwnershipTransferred(_owner, newOwner);
                      _owner = newOwner;
                  }
              }
              
              // File: contracts/common/misc/ProxyStorage.sol
              
              pragma solidity ^0.5.2;
              
              
              contract ProxyStorage is Ownable {
                  address internal proxyTo;
              }
              
              // File: contracts/common/governance/IGovernance.sol
              
              pragma solidity ^0.5.2;
              
              interface IGovernance {
                  function update(address target, bytes calldata data) external;
              }
              
              // File: contracts/common/governance/Governable.sol
              
              pragma solidity ^0.5.2;
              
              
              contract Governable {
                  IGovernance public governance;
              
                  constructor(address _governance) public {
                      governance = IGovernance(_governance);
                  }
              
                  modifier onlyGovernance() {
                      _assertGovernance();
                      _;
                  }
              
                  function _assertGovernance() private view {
                      require(
                          msg.sender == address(governance),
                          "Only governance contract is authorized"
                      );
                  }
              }
              
              // File: contracts/common/Registry.sol
              
              pragma solidity ^0.5.2;
              
              
              
              
              contract Registry is Governable {
                  // @todo hardcode constants
                  bytes32 private constant WETH_TOKEN = keccak256("wethToken");
                  bytes32 private constant DEPOSIT_MANAGER = keccak256("depositManager");
                  bytes32 private constant STAKE_MANAGER = keccak256("stakeManager");
                  bytes32 private constant VALIDATOR_SHARE = keccak256("validatorShare");
                  bytes32 private constant WITHDRAW_MANAGER = keccak256("withdrawManager");
                  bytes32 private constant CHILD_CHAIN = keccak256("childChain");
                  bytes32 private constant STATE_SENDER = keccak256("stateSender");
                  bytes32 private constant SLASHING_MANAGER = keccak256("slashingManager");
              
                  address public erc20Predicate;
                  address public erc721Predicate;
              
                  mapping(bytes32 => address) public contractMap;
                  mapping(address => address) public rootToChildToken;
                  mapping(address => address) public childToRootToken;
                  mapping(address => bool) public proofValidatorContracts;
                  mapping(address => bool) public isERC721;
              
                  enum Type {Invalid, ERC20, ERC721, Custom}
                  struct Predicate {
                      Type _type;
                  }
                  mapping(address => Predicate) public predicates;
              
                  event TokenMapped(address indexed rootToken, address indexed childToken);
                  event ProofValidatorAdded(address indexed validator, address indexed from);
                  event ProofValidatorRemoved(address indexed validator, address indexed from);
                  event PredicateAdded(address indexed predicate, address indexed from);
                  event PredicateRemoved(address indexed predicate, address indexed from);
                  event ContractMapUpdated(bytes32 indexed key, address indexed previousContract, address indexed newContract);
              
                  constructor(address _governance) public Governable(_governance) {}
              
                  function updateContractMap(bytes32 _key, address _address) external onlyGovernance {
                      emit ContractMapUpdated(_key, contractMap[_key], _address);
                      contractMap[_key] = _address;
                  }
              
                  /**
                   * @dev Map root token to child token
                   * @param _rootToken Token address on the root chain
                   * @param _childToken Token address on the child chain
                   * @param _isERC721 Is the token being mapped ERC721
                   */
                  function mapToken(
                      address _rootToken,
                      address _childToken,
                      bool _isERC721
                  ) external onlyGovernance {
                      require(_rootToken != address(0x0) && _childToken != address(0x0), "INVALID_TOKEN_ADDRESS");
                      rootToChildToken[_rootToken] = _childToken;
                      childToRootToken[_childToken] = _rootToken;
                      isERC721[_rootToken] = _isERC721;
                      IWithdrawManager(contractMap[WITHDRAW_MANAGER]).createExitQueue(_rootToken);
                      emit TokenMapped(_rootToken, _childToken);
                  }
              
                  function addErc20Predicate(address predicate) public onlyGovernance {
                      require(predicate != address(0x0), "Can not add null address as predicate");
                      erc20Predicate = predicate;
                      addPredicate(predicate, Type.ERC20);
                  }
              
                  function addErc721Predicate(address predicate) public onlyGovernance {
                      erc721Predicate = predicate;
                      addPredicate(predicate, Type.ERC721);
                  }
              
                  function addPredicate(address predicate, Type _type) public onlyGovernance {
                      require(predicates[predicate]._type == Type.Invalid, "Predicate already added");
                      predicates[predicate]._type = _type;
                      emit PredicateAdded(predicate, msg.sender);
                  }
              
                  function removePredicate(address predicate) public onlyGovernance {
                      require(predicates[predicate]._type != Type.Invalid, "Predicate does not exist");
                      delete predicates[predicate];
                      emit PredicateRemoved(predicate, msg.sender);
                  }
              
                  function getValidatorShareAddress() public view returns (address) {
                      return contractMap[VALIDATOR_SHARE];
                  }
              
                  function getWethTokenAddress() public view returns (address) {
                      return contractMap[WETH_TOKEN];
                  }
              
                  function getDepositManagerAddress() public view returns (address) {
                      return contractMap[DEPOSIT_MANAGER];
                  }
              
                  function getStakeManagerAddress() public view returns (address) {
                      return contractMap[STAKE_MANAGER];
                  }
              
                  function getSlashingManagerAddress() public view returns (address) {
                      return contractMap[SLASHING_MANAGER];
                  }
              
                  function getWithdrawManagerAddress() public view returns (address) {
                      return contractMap[WITHDRAW_MANAGER];
                  }
              
                  function getChildChainAndStateSender() public view returns (address, address) {
                      return (contractMap[CHILD_CHAIN], contractMap[STATE_SENDER]);
                  }
              
                  function isTokenMapped(address _token) public view returns (bool) {
                      return rootToChildToken[_token] != address(0x0);
                  }
              
                  function isTokenMappedAndIsErc721(address _token) public view returns (bool) {
                      require(isTokenMapped(_token), "TOKEN_NOT_MAPPED");
                      return isERC721[_token];
                  }
              
                  function isTokenMappedAndGetPredicate(address _token) public view returns (address) {
                      if (isTokenMappedAndIsErc721(_token)) {
                          return erc721Predicate;
                      }
                      return erc20Predicate;
                  }
              
                  function isChildTokenErc721(address childToken) public view returns (bool) {
                      address rootToken = childToRootToken[childToken];
                      require(rootToken != address(0x0), "Child token is not mapped");
                      return isERC721[rootToken];
                  }
              }
              
              // File: contracts/common/mixin/ChainIdMixin.sol
              
              pragma solidity ^0.5.2;
              
              contract ChainIdMixin {
                bytes constant public networkId = hex"3A99";
                uint256 constant public CHAINID = 15001;
              }
              
              // File: contracts/root/RootChainStorage.sol
              
              pragma solidity ^0.5.2;
              
              
              
              
              
              contract RootChainHeader {
                  event NewHeaderBlock(
                      address indexed proposer,
                      uint256 indexed headerBlockId,
                      uint256 indexed reward,
                      uint256 start,
                      uint256 end,
                      bytes32 root
                  );
                  // housekeeping event
                  event ResetHeaderBlock(address indexed proposer, uint256 indexed headerBlockId);
                  struct HeaderBlock {
                      bytes32 root;
                      uint256 start;
                      uint256 end;
                      uint256 createdAt;
                      address proposer;
                  }
              }
              
              
              contract RootChainStorage is ProxyStorage, RootChainHeader, ChainIdMixin {
                  bytes32 public heimdallId;
                  uint8 public constant VOTE_TYPE = 2;
              
                  uint16 internal constant MAX_DEPOSITS = 10000;
                  uint256 public _nextHeaderBlock = MAX_DEPOSITS;
                  uint256 internal _blockDepositId = 1;
                  mapping(uint256 => HeaderBlock) public headerBlocks;
                  Registry internal registry;
              }
              
              // File: contracts/staking/stakeManager/IStakeManager.sol
              
              pragma solidity 0.5.17;
              
              contract IStakeManager {
                  // validator replacement
                  function startAuction(
                      uint256 validatorId,
                      uint256 amount,
                      bool acceptDelegation,
                      bytes calldata signerPubkey
                  ) external;
              
                  function confirmAuctionBid(uint256 validatorId, uint256 heimdallFee) external;
              
                  function transferFunds(
                      uint256 validatorId,
                      uint256 amount,
                      address delegator
                  ) external returns (bool);
              
                  function delegationDeposit(
                      uint256 validatorId,
                      uint256 amount,
                      address delegator
                  ) external returns (bool);
              
                  function unstake(uint256 validatorId) external;
              
                  function totalStakedFor(address addr) external view returns (uint256);
              
                  function stakeFor(
                      address user,
                      uint256 amount,
                      uint256 heimdallFee,
                      bool acceptDelegation,
                      bytes memory signerPubkey
                  ) public;
              
                  function checkSignatures(
                      uint256 blockInterval,
                      bytes32 voteHash,
                      bytes32 stateRoot,
                      address proposer,
                      uint[3][] calldata sigs
                  ) external returns (uint256);
              
                  function updateValidatorState(uint256 validatorId, int256 amount) public;
              
                  function ownerOf(uint256 tokenId) public view returns (address);
              
                  function slash(bytes calldata slashingInfoList) external returns (uint256);
              
                  function validatorStake(uint256 validatorId) public view returns (uint256);
              
                  function epoch() public view returns (uint256);
              
                  function getRegistry() public view returns (address);
              
                  function withdrawalDelay() public view returns (uint256);
              
                  function delegatedAmount(uint256 validatorId) public view returns(uint256);
              
                  function decreaseValidatorDelegatedAmount(uint256 validatorId, uint256 amount) public;
              
                  function withdrawDelegatorsReward(uint256 validatorId) public returns(uint256);
              
                  function delegatorsReward(uint256 validatorId) public view returns(uint256);
              
                  function dethroneAndStake(
                      address auctionUser,
                      uint256 heimdallFee,
                      uint256 validatorId,
                      uint256 auctionAmount,
                      bool acceptDelegation,
                      bytes calldata signerPubkey
                  ) external;
              }
              
              // File: contracts/root/IRootChain.sol
              
              pragma solidity ^0.5.2;
              
              
              interface IRootChain {
                  function slash() external;
              
                  function submitHeaderBlock(bytes calldata data, bytes calldata sigs)
                      external;
                  
                  function submitCheckpoint(bytes calldata data, uint[3][] calldata sigs)
                      external;
              
                  function getLastChildBlock() external view returns (uint256);
              
                  function currentHeaderBlock() external view returns (uint256);
              }
              
              // File: contracts/root/RootChain.sol
              
              pragma solidity ^0.5.2;
              
              
              
              
              
              
              
              
              contract RootChain is RootChainStorage, IRootChain {
                  using SafeMath for uint256;
                  using RLPReader for bytes;
                  using RLPReader for RLPReader.RLPItem;
              
                  modifier onlyDepositManager() {
                      require(msg.sender == registry.getDepositManagerAddress(), "UNAUTHORIZED_DEPOSIT_MANAGER_ONLY");
                      _;
                  }
              
                  function submitHeaderBlock(bytes calldata data, bytes calldata sigs) external {
                      revert();
                  }
              
                  function submitCheckpoint(bytes calldata data, uint[3][] calldata sigs) external {
                      (address proposer, uint256 start, uint256 end, bytes32 rootHash, bytes32 accountHash, uint256 _borChainID) = abi
                          .decode(data, (address, uint256, uint256, bytes32, bytes32, uint256));
                      require(CHAINID == _borChainID, "Invalid bor chain id");
              
                      require(_buildHeaderBlock(proposer, start, end, rootHash), "INCORRECT_HEADER_DATA");
              
                      // check if it is better to keep it in local storage instead
                      IStakeManager stakeManager = IStakeManager(registry.getStakeManagerAddress());
                      uint256 _reward = stakeManager.checkSignatures(
                          end.sub(start).add(1),
                          /**  
                              prefix 01 to data 
                              01 represents positive vote on data and 00 is negative vote
                              malicious validator can try to send 2/3 on negative vote so 01 is appended
                           */
                          keccak256(abi.encodePacked(bytes(hex"01"), data)),
                          accountHash,
                          proposer,
                          sigs
                      );
              
                      require(_reward != 0, "Invalid checkpoint");
                      emit NewHeaderBlock(proposer, _nextHeaderBlock, _reward, start, end, rootHash);
                      _nextHeaderBlock = _nextHeaderBlock.add(MAX_DEPOSITS);
                      _blockDepositId = 1;
                  }
              
                  function updateDepositId(uint256 numDeposits) external onlyDepositManager returns (uint256 depositId) {
                      depositId = currentHeaderBlock().add(_blockDepositId);
                      // deposit ids will be (_blockDepositId, _blockDepositId + 1, .... _blockDepositId + numDeposits - 1)
                      _blockDepositId = _blockDepositId.add(numDeposits);
                      require(
                          // Since _blockDepositId is initialized to 1; only (MAX_DEPOSITS - 1) deposits per header block are allowed
                          _blockDepositId <= MAX_DEPOSITS,
                          "TOO_MANY_DEPOSITS"
                      );
                  }
              
                  function getLastChildBlock() external view returns (uint256) {
                      return headerBlocks[currentHeaderBlock()].end;
                  }
              
                  function slash() external {
                      //TODO: future implementation
                  }
              
                  function currentHeaderBlock() public view returns (uint256) {
                      return _nextHeaderBlock.sub(MAX_DEPOSITS);
                  }
              
                  function _buildHeaderBlock(
                      address proposer,
                      uint256 start,
                      uint256 end,
                      bytes32 rootHash
                  ) private returns (bool) {
                      uint256 nextChildBlock;
                      /*
                  The ID of the 1st header block is MAX_DEPOSITS.
                  if _nextHeaderBlock == MAX_DEPOSITS, then the first header block is yet to be submitted, hence nextChildBlock = 0
                  */
                      if (_nextHeaderBlock > MAX_DEPOSITS) {
                          nextChildBlock = headerBlocks[currentHeaderBlock()].end + 1;
                      }
                      if (nextChildBlock != start) {
                          return false;
                      }
              
                      HeaderBlock memory headerBlock = HeaderBlock({
                          root: rootHash,
                          start: nextChildBlock,
                          end: end,
                          createdAt: now,
                          proposer: proposer
                      });
              
                      headerBlocks[_nextHeaderBlock] = headerBlock;
                      return true;
                  }
              
                  // Housekeeping function. @todo remove later
                  function setNextHeaderBlock(uint256 _value) public onlyOwner {
                      require(_value % MAX_DEPOSITS == 0, "Invalid value");
                      for (uint256 i = _value; i < _nextHeaderBlock; i += MAX_DEPOSITS) {
                          delete headerBlocks[i];
                      }
                      _nextHeaderBlock = _value;
                      _blockDepositId = 1;
                      emit ResetHeaderBlock(msg.sender, _nextHeaderBlock);
                  }
              
                  // Housekeeping function. @todo remove later
                  function setHeimdallId(string memory _heimdallId) public onlyOwner {
                      heimdallId = keccak256(abi.encodePacked(_heimdallId));
                  }
              }
              
              // File: openzeppelin-solidity/contracts/introspection/IERC165.sol
              
              pragma solidity ^0.5.2;
              
              /**
               * @title IERC165
               * @dev https://eips.ethereum.org/EIPS/eip-165
               */
              interface IERC165 {
                  /**
                   * @notice Query if a contract implements an interface
                   * @param interfaceId The interface identifier, as specified in ERC-165
                   * @dev Interface identification is specified in ERC-165. This function
                   * uses less than 30,000 gas.
                   */
                  function supportsInterface(bytes4 interfaceId) external view returns (bool);
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC721/IERC721.sol
              
              pragma solidity ^0.5.2;
              
              
              /**
               * @title ERC721 Non-Fungible Token Standard basic interface
               * @dev see https://eips.ethereum.org/EIPS/eip-721
               */
              contract IERC721 is IERC165 {
                  event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
                  event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
                  event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
              
                  function balanceOf(address owner) public view returns (uint256 balance);
                  function ownerOf(uint256 tokenId) public view returns (address owner);
              
                  function approve(address to, uint256 tokenId) public;
                  function getApproved(uint256 tokenId) public view returns (address operator);
              
                  function setApprovalForAll(address operator, bool _approved) public;
                  function isApprovedForAll(address owner, address operator) public view returns (bool);
              
                  function transferFrom(address from, address to, uint256 tokenId) public;
                  function safeTransferFrom(address from, address to, uint256 tokenId) public;
              
                  function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public;
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC721/IERC721Receiver.sol
              
              pragma solidity ^0.5.2;
              
              /**
               * @title ERC721 token receiver interface
               * @dev Interface for any contract that wants to support safeTransfers
               * from ERC721 asset contracts.
               */
              contract IERC721Receiver {
                  /**
                   * @notice Handle the receipt of an NFT
                   * @dev The ERC721 smart contract calls this function on the recipient
                   * after a `safeTransfer`. This function MUST return the function selector,
                   * otherwise the caller will revert the transaction. The selector to be
                   * returned can be obtained as `this.onERC721Received.selector`. This
                   * function MAY throw to revert and reject the transfer.
                   * Note: the ERC721 contract address is always the message sender.
                   * @param operator The address which called `safeTransferFrom` function
                   * @param from The address which previously owned the token
                   * @param tokenId The NFT identifier which is being transferred
                   * @param data Additional data with no specified format
                   * @return bytes4 `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
                   */
                  function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data)
                  public returns (bytes4);
              }
              
              // File: openzeppelin-solidity/contracts/utils/Address.sol
              
              pragma solidity ^0.5.2;
              
              /**
               * Utility library of inline functions on addresses
               */
              library Address {
                  /**
                   * Returns whether the target address is a contract
                   * @dev This function will return false if invoked during the constructor of a contract,
                   * as the code is not actually created until after the constructor finishes.
                   * @param account address of the account to check
                   * @return whether the target address is a contract
                   */
                  function isContract(address account) internal view returns (bool) {
                      uint256 size;
                      // XXX Currently there is no better way to check if there is a contract in an address
                      // than to check the size of the code at that address.
                      // See https://ethereum.stackexchange.com/a/14016/36603
                      // for more details about how this works.
                      // TODO Check this again before the Serenity release, because all addresses will be
                      // contracts then.
                      // solhint-disable-next-line no-inline-assembly
                      assembly { size := extcodesize(account) }
                      return size > 0;
                  }
              }
              
              // File: openzeppelin-solidity/contracts/drafts/Counters.sol
              
              pragma solidity ^0.5.2;
              
              
              /**
               * @title Counters
               * @author Matt Condon (@shrugs)
               * @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number
               * of elements in a mapping, issuing ERC721 ids, or counting request ids
               *
               * Include with `using Counters for Counters.Counter;`
               * Since it is not possible to overflow a 256 bit integer with increments of one, `increment` can skip the SafeMath
               * overflow check, thereby saving gas. This does assume however correct usage, in that the underlying `_value` is never
               * directly accessed.
               */
              library Counters {
                  using SafeMath for uint256;
              
                  struct Counter {
                      // This variable should never be directly accessed by users of the library: interactions must be restricted to
                      // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
                      // this feature: see https://github.com/ethereum/solidity/issues/4637
                      uint256 _value; // default: 0
                  }
              
                  function current(Counter storage counter) internal view returns (uint256) {
                      return counter._value;
                  }
              
                  function increment(Counter storage counter) internal {
                      counter._value += 1;
                  }
              
                  function decrement(Counter storage counter) internal {
                      counter._value = counter._value.sub(1);
                  }
              }
              
              // File: openzeppelin-solidity/contracts/introspection/ERC165.sol
              
              pragma solidity ^0.5.2;
              
              
              /**
               * @title ERC165
               * @author Matt Condon (@shrugs)
               * @dev Implements ERC165 using a lookup table.
               */
              contract ERC165 is IERC165 {
                  bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
                  /*
                   * 0x01ffc9a7 ===
                   *     bytes4(keccak256('supportsInterface(bytes4)'))
                   */
              
                  /**
                   * @dev a mapping of interface id to whether or not it's supported
                   */
                  mapping(bytes4 => bool) private _supportedInterfaces;
              
                  /**
                   * @dev A contract implementing SupportsInterfaceWithLookup
                   * implement ERC165 itself
                   */
                  constructor () internal {
                      _registerInterface(_INTERFACE_ID_ERC165);
                  }
              
                  /**
                   * @dev implement supportsInterface(bytes4) using a lookup table
                   */
                  function supportsInterface(bytes4 interfaceId) external view returns (bool) {
                      return _supportedInterfaces[interfaceId];
                  }
              
                  /**
                   * @dev internal method for registering an interface
                   */
                  function _registerInterface(bytes4 interfaceId) internal {
                      require(interfaceId != 0xffffffff);
                      _supportedInterfaces[interfaceId] = true;
                  }
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC721/ERC721.sol
              
              pragma solidity ^0.5.2;
              
              
              
              
              
              
              
              /**
               * @title ERC721 Non-Fungible Token Standard basic implementation
               * @dev see https://eips.ethereum.org/EIPS/eip-721
               */
              contract ERC721 is ERC165, IERC721 {
                  using SafeMath for uint256;
                  using Address for address;
                  using Counters for Counters.Counter;
              
                  // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
                  // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector`
                  bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
              
                  // Mapping from token ID to owner
                  mapping (uint256 => address) private _tokenOwner;
              
                  // Mapping from token ID to approved address
                  mapping (uint256 => address) private _tokenApprovals;
              
                  // Mapping from owner to number of owned token
                  mapping (address => Counters.Counter) private _ownedTokensCount;
              
                  // Mapping from owner to operator approvals
                  mapping (address => mapping (address => bool)) private _operatorApprovals;
              
                  bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
                  /*
                   * 0x80ac58cd ===
                   *     bytes4(keccak256('balanceOf(address)')) ^
                   *     bytes4(keccak256('ownerOf(uint256)')) ^
                   *     bytes4(keccak256('approve(address,uint256)')) ^
                   *     bytes4(keccak256('getApproved(uint256)')) ^
                   *     bytes4(keccak256('setApprovalForAll(address,bool)')) ^
                   *     bytes4(keccak256('isApprovedForAll(address,address)')) ^
                   *     bytes4(keccak256('transferFrom(address,address,uint256)')) ^
                   *     bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^
                   *     bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)'))
                   */
              
                  constructor () public {
                      // register the supported interfaces to conform to ERC721 via ERC165
                      _registerInterface(_INTERFACE_ID_ERC721);
                  }
              
                  /**
                   * @dev Gets the balance of the specified address
                   * @param owner address to query the balance of
                   * @return uint256 representing the amount owned by the passed address
                   */
                  function balanceOf(address owner) public view returns (uint256) {
                      require(owner != address(0));
                      return _ownedTokensCount[owner].current();
                  }
              
                  /**
                   * @dev Gets the owner of the specified token ID
                   * @param tokenId uint256 ID of the token to query the owner of
                   * @return address currently marked as the owner of the given token ID
                   */
                  function ownerOf(uint256 tokenId) public view returns (address) {
                      address owner = _tokenOwner[tokenId];
                      require(owner != address(0));
                      return owner;
                  }
              
                  /**
                   * @dev Approves another address to transfer the given token ID
                   * The zero address indicates there is no approved address.
                   * There can only be one approved address per token at a given time.
                   * Can only be called by the token owner or an approved operator.
                   * @param to address to be approved for the given token ID
                   * @param tokenId uint256 ID of the token to be approved
                   */
                  function approve(address to, uint256 tokenId) public {
                      address owner = ownerOf(tokenId);
                      require(to != owner);
                      require(msg.sender == owner || isApprovedForAll(owner, msg.sender));
              
                      _tokenApprovals[tokenId] = to;
                      emit Approval(owner, to, tokenId);
                  }
              
                  /**
                   * @dev Gets the approved address for a token ID, or zero if no address set
                   * Reverts if the token ID does not exist.
                   * @param tokenId uint256 ID of the token to query the approval of
                   * @return address currently approved for the given token ID
                   */
                  function getApproved(uint256 tokenId) public view returns (address) {
                      require(_exists(tokenId));
                      return _tokenApprovals[tokenId];
                  }
              
                  /**
                   * @dev Sets or unsets the approval of a given operator
                   * An operator is allowed to transfer all tokens of the sender on their behalf
                   * @param to operator address to set the approval
                   * @param approved representing the status of the approval to be set
                   */
                  function setApprovalForAll(address to, bool approved) public {
                      require(to != msg.sender);
                      _operatorApprovals[msg.sender][to] = approved;
                      emit ApprovalForAll(msg.sender, to, approved);
                  }
              
                  /**
                   * @dev Tells whether an operator is approved by a given owner
                   * @param owner owner address which you want to query the approval of
                   * @param operator operator address which you want to query the approval of
                   * @return bool whether the given operator is approved by the given owner
                   */
                  function isApprovedForAll(address owner, address operator) public view returns (bool) {
                      return _operatorApprovals[owner][operator];
                  }
              
                  /**
                   * @dev Transfers the ownership of a given token ID to another address
                   * Usage of this method is discouraged, use `safeTransferFrom` whenever possible
                   * Requires the msg.sender to be the owner, approved, or operator
                   * @param from current owner of the token
                   * @param to address to receive the ownership of the given token ID
                   * @param tokenId uint256 ID of the token to be transferred
                   */
                  function transferFrom(address from, address to, uint256 tokenId) public {
                      require(_isApprovedOrOwner(msg.sender, tokenId));
              
                      _transferFrom(from, to, tokenId);
                  }
              
                  /**
                   * @dev Safely transfers the ownership of a given token ID to another address
                   * If the target address is a contract, it must implement `onERC721Received`,
                   * which is called upon a safe transfer, and return the magic value
                   * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
                   * the transfer is reverted.
                   * Requires the msg.sender to be the owner, approved, or operator
                   * @param from current owner of the token
                   * @param to address to receive the ownership of the given token ID
                   * @param tokenId uint256 ID of the token to be transferred
                   */
                  function safeTransferFrom(address from, address to, uint256 tokenId) public {
                      safeTransferFrom(from, to, tokenId, "");
                  }
              
                  /**
                   * @dev Safely transfers the ownership of a given token ID to another address
                   * If the target address is a contract, it must implement `onERC721Received`,
                   * which is called upon a safe transfer, and return the magic value
                   * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
                   * the transfer is reverted.
                   * Requires the msg.sender to be the owner, approved, or operator
                   * @param from current owner of the token
                   * @param to address to receive the ownership of the given token ID
                   * @param tokenId uint256 ID of the token to be transferred
                   * @param _data bytes data to send along with a safe transfer check
                   */
                  function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public {
                      transferFrom(from, to, tokenId);
                      require(_checkOnERC721Received(from, to, tokenId, _data));
                  }
              
                  /**
                   * @dev Returns whether the specified token exists
                   * @param tokenId uint256 ID of the token to query the existence of
                   * @return bool whether the token exists
                   */
                  function _exists(uint256 tokenId) internal view returns (bool) {
                      address owner = _tokenOwner[tokenId];
                      return owner != address(0);
                  }
              
                  /**
                   * @dev Returns whether the given spender can transfer a given token ID
                   * @param spender address of the spender to query
                   * @param tokenId uint256 ID of the token to be transferred
                   * @return bool whether the msg.sender is approved for the given token ID,
                   * is an operator of the owner, or is the owner of the token
                   */
                  function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
                      address owner = ownerOf(tokenId);
                      return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
                  }
              
                  /**
                   * @dev Internal function to mint a new token
                   * Reverts if the given token ID already exists
                   * @param to The address that will own the minted token
                   * @param tokenId uint256 ID of the token to be minted
                   */
                  function _mint(address to, uint256 tokenId) internal {
                      require(to != address(0));
                      require(!_exists(tokenId));
              
                      _tokenOwner[tokenId] = to;
                      _ownedTokensCount[to].increment();
              
                      emit Transfer(address(0), to, tokenId);
                  }
              
                  /**
                   * @dev Internal function to burn a specific token
                   * Reverts if the token does not exist
                   * Deprecated, use _burn(uint256) instead.
                   * @param owner owner of the token to burn
                   * @param tokenId uint256 ID of the token being burned
                   */
                  function _burn(address owner, uint256 tokenId) internal {
                      require(ownerOf(tokenId) == owner);
              
                      _clearApproval(tokenId);
              
                      _ownedTokensCount[owner].decrement();
                      _tokenOwner[tokenId] = address(0);
              
                      emit Transfer(owner, address(0), tokenId);
                  }
              
                  /**
                   * @dev Internal function to burn a specific token
                   * Reverts if the token does not exist
                   * @param tokenId uint256 ID of the token being burned
                   */
                  function _burn(uint256 tokenId) internal {
                      _burn(ownerOf(tokenId), tokenId);
                  }
              
                  /**
                   * @dev Internal function to transfer ownership of a given token ID to another address.
                   * As opposed to transferFrom, this imposes no restrictions on msg.sender.
                   * @param from current owner of the token
                   * @param to address to receive the ownership of the given token ID
                   * @param tokenId uint256 ID of the token to be transferred
                   */
                  function _transferFrom(address from, address to, uint256 tokenId) internal {
                      require(ownerOf(tokenId) == from);
                      require(to != address(0));
              
                      _clearApproval(tokenId);
              
                      _ownedTokensCount[from].decrement();
                      _ownedTokensCount[to].increment();
              
                      _tokenOwner[tokenId] = to;
              
                      emit Transfer(from, to, tokenId);
                  }
              
                  /**
                   * @dev Internal function to invoke `onERC721Received` on a target address
                   * The call is not executed if the target address is not a contract
                   * @param from address representing the previous owner of the given token ID
                   * @param to target address that will receive the tokens
                   * @param tokenId uint256 ID of the token to be transferred
                   * @param _data bytes optional data to send along with the call
                   * @return bool whether the call correctly returned the expected magic value
                   */
                  function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)
                      internal returns (bool)
                  {
                      if (!to.isContract()) {
                          return true;
                      }
              
                      bytes4 retval = IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, _data);
                      return (retval == _ERC721_RECEIVED);
                  }
              
                  /**
                   * @dev Private function to clear current approval of a given token ID
                   * @param tokenId uint256 ID of the token to be transferred
                   */
                  function _clearApproval(uint256 tokenId) private {
                      if (_tokenApprovals[tokenId] != address(0)) {
                          _tokenApprovals[tokenId] = address(0);
                      }
                  }
              }
              
              // File: contracts/root/withdrawManager/ExitNFT.sol
              
              pragma solidity ^0.5.2;
              
              
              
              contract ExitNFT is ERC721 {
                  Registry internal registry;
              
                  modifier onlyWithdrawManager() {
                      require(
                          msg.sender == registry.getWithdrawManagerAddress(),
                          "UNAUTHORIZED_WITHDRAW_MANAGER_ONLY"
                      );
                      _;
                  }
              
                  constructor(address _registry) public {
                      registry = Registry(_registry);
                  }
              
                  function mint(address _owner, uint256 _tokenId)
                      external
                      onlyWithdrawManager
                  {
                      _mint(_owner, _tokenId);
                  }
              
                  function burn(uint256 _tokenId) external onlyWithdrawManager {
                      _burn(_tokenId);
                  }
              
                  function exists(uint256 tokenId) public view returns (bool) {
                      return _exists(tokenId);
                  }
              }
              
              // File: contracts/root/withdrawManager/WithdrawManagerStorage.sol
              
              pragma solidity ^0.5.2;
              
              
              
              
              
              
              contract ExitsDataStructure {
                  struct Input {
                      address utxoOwner;
                      address predicate;
                      address token;
                  }
              
                  struct PlasmaExit {
                      uint256 receiptAmountOrNFTId;
                      bytes32 txHash;
                      address owner;
                      address token;
                      bool isRegularExit;
                      address predicate;
                      // Mapping from age of input to Input
                      mapping(uint256 => Input) inputs;
                  }
              }
              
              
              contract WithdrawManagerHeader is ExitsDataStructure {
                  event Withdraw(uint256 indexed exitId, address indexed user, address indexed token, uint256 amount);
              
                  event ExitStarted(
                      address indexed exitor,
                      uint256 indexed exitId,
                      address indexed token,
                      uint256 amount,
                      bool isRegularExit
                  );
              
                  event ExitUpdated(uint256 indexed exitId, uint256 indexed age, address signer);
                  event ExitPeriodUpdate(uint256 indexed oldExitPeriod, uint256 indexed newExitPeriod);
              
                  event ExitCancelled(uint256 indexed exitId);
              }
              
              
              contract WithdrawManagerStorage is ProxyStorage, WithdrawManagerHeader {
                  // 0.5 week = 7 * 86400 / 2 = 302400
                  uint256 public HALF_EXIT_PERIOD = 302400;
              
                  // Bonded exits collaterized at 0.1 ETH
                  uint256 internal constant BOND_AMOUNT = 10**17;
              
                  Registry internal registry;
                  RootChain internal rootChain;
              
                  mapping(uint128 => bool) isKnownExit;
                  mapping(uint256 => PlasmaExit) public exits;
                  // mapping with token => (owner => exitId) keccak(token+owner) keccak(token+owner+tokenId)
                  mapping(bytes32 => uint256) public ownerExits;
                  mapping(address => address) public exitsQueues;
                  ExitNFT public exitNft;
              
                  // ERC721, ERC20 and Weth transfers require 155000, 100000, 52000 gas respectively
                  // Processing each exit in a while loop iteration requires ~52000 gas (@todo check if this changed)
                  // uint32 constant internal ITERATION_GAS = 52000;
              
                  // So putting an upper limit of 155000 + 52000 + leeway
                  uint32 public ON_FINALIZE_GAS_LIMIT = 300000;
              
                  uint256 public exitWindow;
              }
              
              // File: contracts/root/predicates/IPredicate.sol
              
              pragma solidity ^0.5.2;
              
              
              
              
              
              
              
              
              interface IPredicate {
                  /**
                 * @notice Verify the deprecation of a state update
                 * @param exit ABI encoded PlasmaExit data
                 * @param inputUtxo ABI encoded Input UTXO data
                 * @param challengeData RLP encoded data of the challenge reference tx that encodes the following fields
                 * headerNumber Header block number of which the reference tx was a part of
                 * blockProof Proof that the block header (in the child chain) is a leaf in the submitted merkle root
                 * blockNumber Block number of which the reference tx is a part of
                 * blockTime Reference tx block time
                 * blocktxRoot Transactions root of block
                 * blockReceiptsRoot Receipts root of block
                 * receipt Receipt of the reference transaction
                 * receiptProof Merkle proof of the reference receipt
                 * branchMask Merkle proof branchMask for the receipt
                 * logIndex Log Index to read from the receipt
                 * tx Challenge transaction
                 * txProof Merkle proof of the challenge tx
                 * @return Whether or not the state is deprecated
                 */
                  function verifyDeprecation(
                      bytes calldata exit,
                      bytes calldata inputUtxo,
                      bytes calldata challengeData
                  ) external returns (bool);
              
                  function interpretStateUpdate(bytes calldata state)
                      external
                      view
                      returns (bytes memory);
                  function onFinalizeExit(bytes calldata data) external;
              }
              
              contract PredicateUtils is ExitsDataStructure, ChainIdMixin {
                  using RLPReader for RLPReader.RLPItem;
              
                  // Bonded exits collaterized at 0.1 ETH
                  uint256 private constant BOND_AMOUNT = 10**17;
              
                  IWithdrawManager internal withdrawManager;
                  IDepositManager internal depositManager;
              
                  modifier onlyWithdrawManager() {
                      require(
                          msg.sender == address(withdrawManager),
                          "ONLY_WITHDRAW_MANAGER"
                      );
                      _;
                  }
              
                  modifier isBondProvided() {
                      require(msg.value == BOND_AMOUNT, "Invalid Bond amount");
                      _;
                  }
              
                  function onFinalizeExit(bytes calldata data) external onlyWithdrawManager {
                      (, address token, address exitor, uint256 tokenId) = decodeExitForProcessExit(
                          data
                      );
                      depositManager.transferAssets(token, exitor, tokenId);
                  }
              
                  function sendBond() internal {
                      address(uint160(address(withdrawManager))).transfer(BOND_AMOUNT);
                  }
              
                  function getAddressFromTx(RLPReader.RLPItem[] memory txList)
                      internal
                      pure
                      returns (address signer, bytes32 txHash)
                  {
                      bytes[] memory rawTx = new bytes[](9);
                      for (uint8 i = 0; i <= 5; i++) {
                          rawTx[i] = txList[i].toBytes();
                      }
                      rawTx[6] = networkId;
                      rawTx[7] = hex""; // [7] and [8] have something to do with v, r, s values
                      rawTx[8] = hex"";
              
                      txHash = keccak256(RLPEncode.encodeList(rawTx));
                      signer = ecrecover(
                          txHash,
                          Common.getV(txList[6].toBytes(), Common.toUint16(networkId)),
                          bytes32(txList[7].toUint()),
                          bytes32(txList[8].toUint())
                      );
                  }
              
                  function decodeExit(bytes memory data)
                      internal
                      pure
                      returns (PlasmaExit memory)
                  {
                      (address owner, address token, uint256 amountOrTokenId, bytes32 txHash, bool isRegularExit) = abi
                          .decode(data, (address, address, uint256, bytes32, bool));
                      return
                          PlasmaExit(
                              amountOrTokenId,
                              txHash,
                              owner,
                              token,
                              isRegularExit,
                              address(0) /* predicate value is not required */
                          );
                  }
              
                  function decodeExitForProcessExit(bytes memory data)
                      internal
                      pure
                      returns (uint256 exitId, address token, address exitor, uint256 tokenId)
                  {
                      (exitId, token, exitor, tokenId) = abi.decode(
                          data,
                          (uint256, address, address, uint256)
                      );
                  }
              
                  function decodeInputUtxo(bytes memory data)
                      internal
                      pure
                      returns (uint256 age, address signer, address predicate, address token)
                  {
                      (age, signer, predicate, token) = abi.decode(
                          data,
                          (uint256, address, address, address)
                      );
                  }
              
              }
              
              contract IErcPredicate is IPredicate, PredicateUtils {
                  enum ExitType {Invalid, OutgoingTransfer, IncomingTransfer, Burnt}
              
                  struct ExitTxData {
                      uint256 amountOrToken;
                      bytes32 txHash;
                      address childToken;
                      address signer;
                      ExitType exitType;
                  }
              
                  struct ReferenceTxData {
                      uint256 closingBalance;
                      uint256 age;
                      address childToken;
                      address rootToken;
                  }
              
                  uint256 internal constant MAX_LOGS = 10;
              
                  constructor(address _withdrawManager, address _depositManager) public {
                      withdrawManager = IWithdrawManager(_withdrawManager);
                      depositManager = IDepositManager(_depositManager);
                  }
              }
              
              // File: contracts/root/predicates/ERC20PredicateBurnOnly.sol
              
              pragma solidity ^0.5.2;
              
              
              
              
              
              
              
              
              
              
              
              contract ERC20PredicateBurnOnly is IErcPredicate {
                  using RLPReader for bytes;
                  using RLPReader for RLPReader.RLPItem;
                  using SafeMath for uint256;
              
                  using ExitPayloadReader for bytes;
                  using ExitPayloadReader for ExitPayloadReader.ExitPayload;
                  using ExitPayloadReader for ExitPayloadReader.Receipt;
                  using ExitPayloadReader for ExitPayloadReader.Log;
                  using ExitPayloadReader for ExitPayloadReader.LogTopics;
              
                  // keccak256('Withdraw(address,address,uint256,uint256,uint256)')
                  bytes32 constant WITHDRAW_EVENT_SIG = 0xebff2602b3f468259e1e99f613fed6691f3a6526effe6ef3e768ba7ae7a36c4f;
              
                  constructor(
                      address _withdrawManager,
                      address _depositManager
                  ) public IErcPredicate(_withdrawManager, _depositManager) {
                  }
              
                  function startExitWithBurntTokens(bytes calldata data) external {
                      ExitPayloadReader.ExitPayload memory payload = data.toExitPayload();
                      ExitPayloadReader.Receipt memory receipt = payload.getReceipt();
                      uint256 logIndex = payload.getReceiptLogIndex();
                      require(logIndex < MAX_LOGS, "Supporting a max of 10 logs");
                      uint256 age = withdrawManager.verifyInclusion(
                          data,
                          0, /* offset */
                          false /* verifyTxInclusion */
                      );
                      ExitPayloadReader.Log memory log = receipt.getLog();
              
                      // "address" (contract address that emitted the log) field in the receipt
                      address childToken = log.getEmitter();
                      ExitPayloadReader.LogTopics memory topics = log.getTopics();
                      // now, inputItems[i] refers to i-th (0-based) topic in the topics array
                      // event Withdraw(address indexed token, address indexed from, uint256 amountOrTokenId, uint256 input1, uint256 output1)
                      require(
                          bytes32(topics.getField(0).toUint()) == WITHDRAW_EVENT_SIG,
                          "Not a withdraw event signature"
                      );
                      require(
                          msg.sender == address(topics.getField(2).toUint()), // from
                          "Withdrawer and burn exit tx do not match"
                      );
                      address rootToken = address(topics.getField(1).toUint());
                      uint256 exitAmount = BytesLib.toUint(log.getData(), 0); // amountOrTokenId
                      withdrawManager.addExitToQueue(
                          msg.sender,
                          childToken,
                          rootToken,
                          exitAmount,
                          bytes32(0x0),
                          true, /* isRegularExit */
                          age << 1
                      );
                  }
              
                  function verifyDeprecation(
                      bytes calldata exit,
                      bytes calldata inputUtxo,
                      bytes calldata challengeData
                  ) external returns (bool) {}
              
                  function interpretStateUpdate(bytes calldata state)
                      external
                      view
                      returns (bytes memory) {}
              }

              File 9 of 9: DepositManager
              // File: openzeppelin-solidity/contracts/token/ERC721/IERC721Receiver.sol
              
              pragma solidity ^0.5.2;
              
              /**
               * @title ERC721 token receiver interface
               * @dev Interface for any contract that wants to support safeTransfers
               * from ERC721 asset contracts.
               */
              contract IERC721Receiver {
                  /**
                   * @notice Handle the receipt of an NFT
                   * @dev The ERC721 smart contract calls this function on the recipient
                   * after a `safeTransfer`. This function MUST return the function selector,
                   * otherwise the caller will revert the transaction. The selector to be
                   * returned can be obtained as `this.onERC721Received.selector`. This
                   * function MAY throw to revert and reject the transfer.
                   * Note: the ERC721 contract address is always the message sender.
                   * @param operator The address which called `safeTransferFrom` function
                   * @param from The address which previously owned the token
                   * @param tokenId The NFT identifier which is being transferred
                   * @param data Additional data with no specified format
                   * @return bytes4 `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
                   */
                  function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data)
                  public returns (bytes4);
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC721/ERC721Holder.sol
              
              pragma solidity ^0.5.2;
              
              
              contract ERC721Holder is IERC721Receiver {
                  function onERC721Received(address, address, uint256, bytes memory) public returns (bytes4) {
                      return this.onERC721Received.selector;
                  }
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC20/IERC20.sol
              
              pragma solidity ^0.5.2;
              
              /**
               * @title ERC20 interface
               * @dev see https://eips.ethereum.org/EIPS/eip-20
               */
              interface IERC20 {
                  function transfer(address to, uint256 value) external returns (bool);
              
                  function approve(address spender, uint256 value) external returns (bool);
              
                  function transferFrom(address from, address to, uint256 value) external returns (bool);
              
                  function totalSupply() external view returns (uint256);
              
                  function balanceOf(address who) external view returns (uint256);
              
                  function allowance(address owner, address spender) external view returns (uint256);
              
                  event Transfer(address indexed from, address indexed to, uint256 value);
              
                  event Approval(address indexed owner, address indexed spender, uint256 value);
              }
              
              // File: openzeppelin-solidity/contracts/introspection/IERC165.sol
              
              pragma solidity ^0.5.2;
              
              /**
               * @title IERC165
               * @dev https://eips.ethereum.org/EIPS/eip-165
               */
              interface IERC165 {
                  /**
                   * @notice Query if a contract implements an interface
                   * @param interfaceId The interface identifier, as specified in ERC-165
                   * @dev Interface identification is specified in ERC-165. This function
                   * uses less than 30,000 gas.
                   */
                  function supportsInterface(bytes4 interfaceId) external view returns (bool);
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC721/IERC721.sol
              
              pragma solidity ^0.5.2;
              
              
              /**
               * @title ERC721 Non-Fungible Token Standard basic interface
               * @dev see https://eips.ethereum.org/EIPS/eip-721
               */
              contract IERC721 is IERC165 {
                  event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
                  event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
                  event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
              
                  function balanceOf(address owner) public view returns (uint256 balance);
                  function ownerOf(uint256 tokenId) public view returns (address owner);
              
                  function approve(address to, uint256 tokenId) public;
                  function getApproved(uint256 tokenId) public view returns (address operator);
              
                  function setApprovalForAll(address operator, bool _approved) public;
                  function isApprovedForAll(address owner, address operator) public view returns (bool);
              
                  function transferFrom(address from, address to, uint256 tokenId) public;
                  function safeTransferFrom(address from, address to, uint256 tokenId) public;
              
                  function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public;
              }
              
              // File: openzeppelin-solidity/contracts/math/SafeMath.sol
              
              pragma solidity ^0.5.2;
              
              /**
               * @title SafeMath
               * @dev Unsigned math operations with safety checks that revert on error
               */
              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;
                  }
              }
              
              // File: openzeppelin-solidity/contracts/utils/Address.sol
              
              pragma solidity ^0.5.2;
              
              /**
               * Utility library of inline functions on addresses
               */
              library Address {
                  /**
                   * Returns whether the target address is a contract
                   * @dev This function will return false if invoked during the constructor of a contract,
                   * as the code is not actually created until after the constructor finishes.
                   * @param account address of the account to check
                   * @return whether the target address is a contract
                   */
                  function isContract(address account) internal view returns (bool) {
                      uint256 size;
                      // XXX Currently there is no better way to check if there is a contract in an address
                      // than to check the size of the code at that address.
                      // See https://ethereum.stackexchange.com/a/14016/36603
                      // for more details about how this works.
                      // TODO Check this again before the Serenity release, because all addresses will be
                      // contracts then.
                      // solhint-disable-next-line no-inline-assembly
                      assembly { size := extcodesize(account) }
                      return size > 0;
                  }
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC20/SafeERC20.sol
              
              pragma solidity ^0.5.2;
              
              
              
              
              /**
               * @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 ERC20;` 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));
                  }
              
                  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'
                      require((value == 0) || (token.allowance(address(this), spender) == 0));
                      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);
                      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 equal true).
                   * @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.
              
                      // A Solidity high level call has three parts:
                      //  1. The target address is checked to verify it contains contract code
                      //  2. The call itself is made, and success asserted
                      //  3. The return value is decoded, which in turn checks the size of the returned data.
              
                      require(address(token).isContract());
              
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory returndata) = address(token).call(data);
                      require(success);
              
                      if (returndata.length > 0) { // Return data is optional
                          require(abi.decode(returndata, (bool)));
                      }
                  }
              }
              
              // File: contracts/common/governance/IGovernance.sol
              
              pragma solidity ^0.5.2;
              
              interface IGovernance {
                  function update(address target, bytes calldata data) external;
              }
              
              // File: contracts/common/governance/Governable.sol
              
              pragma solidity ^0.5.2;
              
              
              contract Governable {
                  IGovernance public governance;
              
                  constructor(address _governance) public {
                      governance = IGovernance(_governance);
                  }
              
                  modifier onlyGovernance() {
                      _assertGovernance();
                      _;
                  }
              
                  function _assertGovernance() private view {
                      require(
                          msg.sender == address(governance),
                          "Only governance contract is authorized"
                      );
                  }
              }
              
              // File: contracts/root/withdrawManager/IWithdrawManager.sol
              
              pragma solidity ^0.5.2;
              
              contract IWithdrawManager {
                  function createExitQueue(address token) external;
              
                  function verifyInclusion(
                      bytes calldata data,
                      uint8 offset,
                      bool verifyTxInclusion
                  ) external view returns (uint256 age);
              
                  function addExitToQueue(
                      address exitor,
                      address childToken,
                      address rootToken,
                      uint256 exitAmountOrTokenId,
                      bytes32 txHash,
                      bool isRegularExit,
                      uint256 priority
                  ) external;
              
                  function addInput(
                      uint256 exitId,
                      uint256 age,
                      address utxoOwner,
                      address token
                  ) external;
              
                  function challengeExit(
                      uint256 exitId,
                      uint256 inputId,
                      bytes calldata challengeData,
                      address adjudicatorPredicate
                  ) external;
              }
              
              // File: contracts/common/Registry.sol
              
              pragma solidity ^0.5.2;
              
              
              
              
              contract Registry is Governable {
                  // @todo hardcode constants
                  bytes32 private constant WETH_TOKEN = keccak256("wethToken");
                  bytes32 private constant DEPOSIT_MANAGER = keccak256("depositManager");
                  bytes32 private constant STAKE_MANAGER = keccak256("stakeManager");
                  bytes32 private constant VALIDATOR_SHARE = keccak256("validatorShare");
                  bytes32 private constant WITHDRAW_MANAGER = keccak256("withdrawManager");
                  bytes32 private constant CHILD_CHAIN = keccak256("childChain");
                  bytes32 private constant STATE_SENDER = keccak256("stateSender");
                  bytes32 private constant SLASHING_MANAGER = keccak256("slashingManager");
              
                  address public erc20Predicate;
                  address public erc721Predicate;
              
                  mapping(bytes32 => address) public contractMap;
                  mapping(address => address) public rootToChildToken;
                  mapping(address => address) public childToRootToken;
                  mapping(address => bool) public proofValidatorContracts;
                  mapping(address => bool) public isERC721;
              
                  enum Type {Invalid, ERC20, ERC721, Custom}
                  struct Predicate {
                      Type _type;
                  }
                  mapping(address => Predicate) public predicates;
              
                  event TokenMapped(address indexed rootToken, address indexed childToken);
                  event ProofValidatorAdded(address indexed validator, address indexed from);
                  event ProofValidatorRemoved(address indexed validator, address indexed from);
                  event PredicateAdded(address indexed predicate, address indexed from);
                  event PredicateRemoved(address indexed predicate, address indexed from);
                  event ContractMapUpdated(bytes32 indexed key, address indexed previousContract, address indexed newContract);
              
                  constructor(address _governance) public Governable(_governance) {}
              
                  function updateContractMap(bytes32 _key, address _address) external onlyGovernance {
                      emit ContractMapUpdated(_key, contractMap[_key], _address);
                      contractMap[_key] = _address;
                  }
              
                  /**
                   * @dev Map root token to child token
                   * @param _rootToken Token address on the root chain
                   * @param _childToken Token address on the child chain
                   * @param _isERC721 Is the token being mapped ERC721
                   */
                  function mapToken(
                      address _rootToken,
                      address _childToken,
                      bool _isERC721
                  ) external onlyGovernance {
                      require(_rootToken != address(0x0) && _childToken != address(0x0), "INVALID_TOKEN_ADDRESS");
                      rootToChildToken[_rootToken] = _childToken;
                      childToRootToken[_childToken] = _rootToken;
                      isERC721[_rootToken] = _isERC721;
                      IWithdrawManager(contractMap[WITHDRAW_MANAGER]).createExitQueue(_rootToken);
                      emit TokenMapped(_rootToken, _childToken);
                  }
              
                  function addErc20Predicate(address predicate) public onlyGovernance {
                      require(predicate != address(0x0), "Can not add null address as predicate");
                      erc20Predicate = predicate;
                      addPredicate(predicate, Type.ERC20);
                  }
              
                  function addErc721Predicate(address predicate) public onlyGovernance {
                      erc721Predicate = predicate;
                      addPredicate(predicate, Type.ERC721);
                  }
              
                  function addPredicate(address predicate, Type _type) public onlyGovernance {
                      require(predicates[predicate]._type == Type.Invalid, "Predicate already added");
                      predicates[predicate]._type = _type;
                      emit PredicateAdded(predicate, msg.sender);
                  }
              
                  function removePredicate(address predicate) public onlyGovernance {
                      require(predicates[predicate]._type != Type.Invalid, "Predicate does not exist");
                      delete predicates[predicate];
                      emit PredicateRemoved(predicate, msg.sender);
                  }
              
                  function getValidatorShareAddress() public view returns (address) {
                      return contractMap[VALIDATOR_SHARE];
                  }
              
                  function getWethTokenAddress() public view returns (address) {
                      return contractMap[WETH_TOKEN];
                  }
              
                  function getDepositManagerAddress() public view returns (address) {
                      return contractMap[DEPOSIT_MANAGER];
                  }
              
                  function getStakeManagerAddress() public view returns (address) {
                      return contractMap[STAKE_MANAGER];
                  }
              
                  function getSlashingManagerAddress() public view returns (address) {
                      return contractMap[SLASHING_MANAGER];
                  }
              
                  function getWithdrawManagerAddress() public view returns (address) {
                      return contractMap[WITHDRAW_MANAGER];
                  }
              
                  function getChildChainAndStateSender() public view returns (address, address) {
                      return (contractMap[CHILD_CHAIN], contractMap[STATE_SENDER]);
                  }
              
                  function isTokenMapped(address _token) public view returns (bool) {
                      return rootToChildToken[_token] != address(0x0);
                  }
              
                  function isTokenMappedAndIsErc721(address _token) public view returns (bool) {
                      require(isTokenMapped(_token), "TOKEN_NOT_MAPPED");
                      return isERC721[_token];
                  }
              
                  function isTokenMappedAndGetPredicate(address _token) public view returns (address) {
                      if (isTokenMappedAndIsErc721(_token)) {
                          return erc721Predicate;
                      }
                      return erc20Predicate;
                  }
              
                  function isChildTokenErc721(address childToken) public view returns (bool) {
                      address rootToken = childToRootToken[childToken];
                      require(rootToken != address(0x0), "Child token is not mapped");
                      return isERC721[rootToken];
                  }
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC20/ERC20.sol
              
              pragma solidity ^0.5.2;
              
              
              
              /**
               * @title Standard ERC20 token
               *
               * @dev Implementation of the basic standard token.
               * https://eips.ethereum.org/EIPS/eip-20
               * Originally based on code by FirstBlood:
               * https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
               *
               * This implementation emits additional Approval events, allowing applications to reconstruct the allowance status for
               * all accounts just by listening to said events. Note that this isn't required by the specification, and other
               * compliant implementations may not do it.
               */
              contract ERC20 is IERC20 {
                  using SafeMath for uint256;
              
                  mapping (address => uint256) private _balances;
              
                  mapping (address => mapping (address => uint256)) private _allowed;
              
                  uint256 private _totalSupply;
              
                  /**
                   * @dev Total number of tokens in existence
                   */
                  function totalSupply() public view returns (uint256) {
                      return _totalSupply;
                  }
              
                  /**
                   * @dev Gets the balance of the specified address.
                   * @param owner The address to query the balance of.
                   * @return A uint256 representing the amount owned by the passed address.
                   */
                  function balanceOf(address owner) public view returns (uint256) {
                      return _balances[owner];
                  }
              
                  /**
                   * @dev Function to check the amount of tokens that an owner allowed to a spender.
                   * @param owner address The address which owns the funds.
                   * @param spender address The address which will spend the funds.
                   * @return A uint256 specifying the amount of tokens still available for the spender.
                   */
                  function allowance(address owner, address spender) public view returns (uint256) {
                      return _allowed[owner][spender];
                  }
              
                  /**
                   * @dev Transfer token to a specified address
                   * @param to The address to transfer to.
                   * @param value The amount to be transferred.
                   */
                  function transfer(address to, uint256 value) public returns (bool) {
                      _transfer(msg.sender, to, value);
                      return true;
                  }
              
                  /**
                   * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
                   * 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
                   * @param spender The address which will spend the funds.
                   * @param value The amount of tokens to be spent.
                   */
                  function approve(address spender, uint256 value) public returns (bool) {
                      _approve(msg.sender, spender, value);
                      return true;
                  }
              
                  /**
                   * @dev Transfer tokens from one address to another.
                   * Note that while this function emits an Approval event, this is not required as per the specification,
                   * and other compliant implementations may not emit the event.
                   * @param from address The address which you want to send tokens from
                   * @param to address The address which you want to transfer to
                   * @param value uint256 the amount of tokens to be transferred
                   */
                  function transferFrom(address from, address to, uint256 value) public returns (bool) {
                      _transfer(from, to, value);
                      _approve(from, msg.sender, _allowed[from][msg.sender].sub(value));
                      return true;
                  }
              
                  /**
                   * @dev Increase the amount of tokens that an owner allowed to a spender.
                   * approve should be called when _allowed[msg.sender][spender] == 0. To increment
                   * allowed value is better to use this function to avoid 2 calls (and wait until
                   * the first transaction is mined)
                   * From MonolithDAO Token.sol
                   * Emits an Approval event.
                   * @param spender The address which will spend the funds.
                   * @param addedValue The amount of tokens to increase the allowance by.
                   */
                  function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
                      _approve(msg.sender, spender, _allowed[msg.sender][spender].add(addedValue));
                      return true;
                  }
              
                  /**
                   * @dev Decrease the amount of tokens that an owner allowed to a spender.
                   * approve should be called when _allowed[msg.sender][spender] == 0. To decrement
                   * allowed value is better to use this function to avoid 2 calls (and wait until
                   * the first transaction is mined)
                   * From MonolithDAO Token.sol
                   * Emits an Approval event.
                   * @param spender The address which will spend the funds.
                   * @param subtractedValue The amount of tokens to decrease the allowance by.
                   */
                  function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
                      _approve(msg.sender, spender, _allowed[msg.sender][spender].sub(subtractedValue));
                      return true;
                  }
              
                  /**
                   * @dev Transfer token for a specified addresses
                   * @param from The address to transfer from.
                   * @param to The address to transfer to.
                   * @param value The amount to be transferred.
                   */
                  function _transfer(address from, address to, uint256 value) internal {
                      require(to != address(0));
              
                      _balances[from] = _balances[from].sub(value);
                      _balances[to] = _balances[to].add(value);
                      emit Transfer(from, to, value);
                  }
              
                  /**
                   * @dev Internal function that mints an amount of the token and assigns it to
                   * an account. This encapsulates the modification of balances such that the
                   * proper events are emitted.
                   * @param account The account that will receive the created tokens.
                   * @param value The amount that will be created.
                   */
                  function _mint(address account, uint256 value) internal {
                      require(account != address(0));
              
                      _totalSupply = _totalSupply.add(value);
                      _balances[account] = _balances[account].add(value);
                      emit Transfer(address(0), account, value);
                  }
              
                  /**
                   * @dev Internal function that burns an amount of the token of a given
                   * account.
                   * @param account The account whose tokens will be burnt.
                   * @param value The amount that will be burnt.
                   */
                  function _burn(address account, uint256 value) internal {
                      require(account != address(0));
              
                      _totalSupply = _totalSupply.sub(value);
                      _balances[account] = _balances[account].sub(value);
                      emit Transfer(account, address(0), value);
                  }
              
                  /**
                   * @dev Approve an address to spend another addresses' tokens.
                   * @param owner The address that owns the tokens.
                   * @param spender The address that will spend the tokens.
                   * @param value The number of tokens that can be spent.
                   */
                  function _approve(address owner, address spender, uint256 value) internal {
                      require(spender != address(0));
                      require(owner != address(0));
              
                      _allowed[owner][spender] = value;
                      emit Approval(owner, spender, value);
                  }
              
                  /**
                   * @dev Internal function that burns an amount of the token of a given
                   * account, deducting from the sender's allowance for said account. Uses the
                   * internal burn function.
                   * Emits an Approval event (reflecting the reduced allowance).
                   * @param account The account whose tokens will be burnt.
                   * @param value The amount that will be burnt.
                   */
                  function _burnFrom(address account, uint256 value) internal {
                      _burn(account, value);
                      _approve(account, msg.sender, _allowed[account][msg.sender].sub(value));
                  }
              }
              
              // File: contracts/common/tokens/WETH.sol
              
              pragma solidity ^0.5.2;
              
              
              contract WETH is ERC20 {
                  event Deposit(address indexed dst, uint256 wad);
                  event Withdrawal(address indexed src, uint256 wad);
              
                  function deposit() public payable;
              
                  function withdraw(uint256 wad) public;
              
                  function withdraw(uint256 wad, address user) public;
              }
              
              // File: contracts/root/depositManager/IDepositManager.sol
              
              pragma solidity ^0.5.2;
              
              interface IDepositManager {
                  function depositEther() external payable;
                  function transferAssets(
                      address _token,
                      address _user,
                      uint256 _amountOrNFTId
                  ) external;
                  function depositERC20(address _token, uint256 _amount) external;
                  function depositERC721(address _token, uint256 _tokenId) external;
              }
              
              // File: solidity-rlp/contracts/RLPReader.sol
              
              /*
              * @author Hamdi Allam [email protected]
              * Please reach out with any questions or concerns
              */
              pragma solidity ^0.5.0;
              
              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 item RLP encoded bytes
                  */
                  function rlpLen(RLPItem memory item) internal pure returns (uint) {
                      return item.len;
                  }
              
                  /*
                  * @param item RLP encoded bytes
                  */
                  function payloadLen(RLPItem memory item) internal pure returns (uint) {
                      return item.len - _payloadOffset(item.memPtr);
                  }
              
                  /*
                  * @param item RLP encoded list in bytes
                  */
                  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;
                      }
              
                      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;
                  }
              
                  /** 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 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))
                      }
              
                      return result == 0 ? false : 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 offset = _payloadOffset(item.memPtr);
                      uint len = item.len - offset;
              
                      uint result;
                      uint memPtr = item.memPtr + offset;
                      assembly {
                          result := mload(memPtr)
              
                          // shfit to the correct location if neccesary
                          if lt(len, 32) {
                              result := div(result, exp(256, sub(32, len)))
                          }
                      }
              
                      return result;
                  }
              
                  // enforces 32 byte length
                  function toUintStrict(RLPItem memory item) internal pure returns (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 offset = _payloadOffset(item.memPtr);
                      uint len = item.len - offset; // data length
                      bytes memory result = new bytes(len);
              
                      uint destPtr;
                      assembly {
                          destPtr := add(0x20, result)
                      }
              
                      copy(item.memPtr + offset, destPtr, len);
                      return result;
                  }
              
                  /*
                  * Private Helpers
                  */
              
                  // @return number of payload items inside an encoded list.
                  function numItems(RLPItem memory item) private pure returns (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;
                      }
              
                      // 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))
                      }
                  }
              }
              
              // File: openzeppelin-solidity/contracts/ownership/Ownable.sol
              
              pragma solidity ^0.5.2;
              
              /**
               * @title Ownable
               * @dev The Ownable contract has an owner address, and provides basic authorization control
               * functions, this simplifies the implementation of "user permissions".
               */
              contract Ownable {
                  address private _owner;
              
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
              
                  /**
                   * @dev The Ownable constructor sets the original `owner` of the contract to the sender
                   * account.
                   */
                  constructor () internal {
                      _owner = msg.sender;
                      emit OwnershipTransferred(address(0), _owner);
                  }
              
                  /**
                   * @return the address of the owner.
                   */
                  function owner() public view returns (address) {
                      return _owner;
                  }
              
                  /**
                   * @dev Throws if called by any account other than the owner.
                   */
                  modifier onlyOwner() {
                      require(isOwner());
                      _;
                  }
              
                  /**
                   * @return true if `msg.sender` is the owner of the contract.
                   */
                  function isOwner() public view returns (bool) {
                      return msg.sender == _owner;
                  }
              
                  /**
                   * @dev Allows the current owner to relinquish control of the contract.
                   * It will not be possible to call the functions with the `onlyOwner`
                   * modifier anymore.
                   * @notice Renouncing ownership will leave the contract without an owner,
                   * thereby removing any functionality that is only available to the owner.
                   */
                  function renounceOwnership() public onlyOwner {
                      emit OwnershipTransferred(_owner, address(0));
                      _owner = address(0);
                  }
              
                  /**
                   * @dev Allows the current owner to transfer control of the contract to a newOwner.
                   * @param newOwner The address to transfer ownership to.
                   */
                  function transferOwnership(address newOwner) public onlyOwner {
                      _transferOwnership(newOwner);
                  }
              
                  /**
                   * @dev Transfers control of the contract to a newOwner.
                   * @param newOwner The address to transfer ownership to.
                   */
                  function _transferOwnership(address newOwner) internal {
                      require(newOwner != address(0));
                      emit OwnershipTransferred(_owner, newOwner);
                      _owner = newOwner;
                  }
              }
              
              // File: contracts/common/misc/ProxyStorage.sol
              
              pragma solidity ^0.5.2;
              
              
              contract ProxyStorage is Ownable {
                  address internal proxyTo;
              }
              
              // File: contracts/common/mixin/ChainIdMixin.sol
              
              pragma solidity ^0.5.2;
              
              contract ChainIdMixin {
                bytes constant public networkId = hex"3A99";
                uint256 constant public CHAINID = 15001;
              }
              
              // File: contracts/root/RootChainStorage.sol
              
              pragma solidity ^0.5.2;
              
              
              
              
              
              contract RootChainHeader {
                  event NewHeaderBlock(
                      address indexed proposer,
                      uint256 indexed headerBlockId,
                      uint256 indexed reward,
                      uint256 start,
                      uint256 end,
                      bytes32 root
                  );
                  // housekeeping event
                  event ResetHeaderBlock(address indexed proposer, uint256 indexed headerBlockId);
                  struct HeaderBlock {
                      bytes32 root;
                      uint256 start;
                      uint256 end;
                      uint256 createdAt;
                      address proposer;
                  }
              }
              
              
              contract RootChainStorage is ProxyStorage, RootChainHeader, ChainIdMixin {
                  bytes32 public heimdallId;
                  uint8 public constant VOTE_TYPE = 2;
              
                  uint16 internal constant MAX_DEPOSITS = 10000;
                  uint256 public _nextHeaderBlock = MAX_DEPOSITS;
                  uint256 internal _blockDepositId = 1;
                  mapping(uint256 => HeaderBlock) public headerBlocks;
                  Registry internal registry;
              }
              
              // File: contracts/staking/stakeManager/IStakeManager.sol
              
              pragma solidity 0.5.17;
              
              contract IStakeManager {
                  // validator replacement
                  function startAuction(
                      uint256 validatorId,
                      uint256 amount,
                      bool acceptDelegation,
                      bytes calldata signerPubkey
                  ) external;
              
                  function confirmAuctionBid(uint256 validatorId, uint256 heimdallFee) external;
              
                  function transferFunds(
                      uint256 validatorId,
                      uint256 amount,
                      address delegator
                  ) external returns (bool);
              
                  function delegationDeposit(
                      uint256 validatorId,
                      uint256 amount,
                      address delegator
                  ) external returns (bool);
              
                  function unstake(uint256 validatorId) external;
              
                  function totalStakedFor(address addr) external view returns (uint256);
              
                  function stakeFor(
                      address user,
                      uint256 amount,
                      uint256 heimdallFee,
                      bool acceptDelegation,
                      bytes memory signerPubkey
                  ) public;
              
                  function checkSignatures(
                      uint256 blockInterval,
                      bytes32 voteHash,
                      bytes32 stateRoot,
                      address proposer,
                      uint[3][] calldata sigs
                  ) external returns (uint256);
              
                  function updateValidatorState(uint256 validatorId, int256 amount) public;
              
                  function ownerOf(uint256 tokenId) public view returns (address);
              
                  function slash(bytes calldata slashingInfoList) external returns (uint256);
              
                  function validatorStake(uint256 validatorId) public view returns (uint256);
              
                  function epoch() public view returns (uint256);
              
                  function getRegistry() public view returns (address);
              
                  function withdrawalDelay() public view returns (uint256);
              
                  function delegatedAmount(uint256 validatorId) public view returns(uint256);
              
                  function decreaseValidatorDelegatedAmount(uint256 validatorId, uint256 amount) public;
              
                  function withdrawDelegatorsReward(uint256 validatorId) public returns(uint256);
              
                  function delegatorsReward(uint256 validatorId) public view returns(uint256);
              
                  function dethroneAndStake(
                      address auctionUser,
                      uint256 heimdallFee,
                      uint256 validatorId,
                      uint256 auctionAmount,
                      bool acceptDelegation,
                      bytes calldata signerPubkey
                  ) external;
              }
              
              // File: contracts/root/IRootChain.sol
              
              pragma solidity ^0.5.2;
              
              
              interface IRootChain {
                  function slash() external;
              
                  function submitHeaderBlock(bytes calldata data, bytes calldata sigs)
                      external;
                  
                  function submitCheckpoint(bytes calldata data, uint[3][] calldata sigs)
                      external;
              
                  function getLastChildBlock() external view returns (uint256);
              
                  function currentHeaderBlock() external view returns (uint256);
              }
              
              // File: contracts/root/RootChain.sol
              
              pragma solidity ^0.5.2;
              
              
              
              
              
              
              
              
              contract RootChain is RootChainStorage, IRootChain {
                  using SafeMath for uint256;
                  using RLPReader for bytes;
                  using RLPReader for RLPReader.RLPItem;
              
                  modifier onlyDepositManager() {
                      require(msg.sender == registry.getDepositManagerAddress(), "UNAUTHORIZED_DEPOSIT_MANAGER_ONLY");
                      _;
                  }
              
                  function submitHeaderBlock(bytes calldata data, bytes calldata sigs) external {
                      revert();
                  }
              
                  function submitCheckpoint(bytes calldata data, uint[3][] calldata sigs) external {
                      (address proposer, uint256 start, uint256 end, bytes32 rootHash, bytes32 accountHash, uint256 _borChainID) = abi
                          .decode(data, (address, uint256, uint256, bytes32, bytes32, uint256));
                      require(CHAINID == _borChainID, "Invalid bor chain id");
              
                      require(_buildHeaderBlock(proposer, start, end, rootHash), "INCORRECT_HEADER_DATA");
              
                      // check if it is better to keep it in local storage instead
                      IStakeManager stakeManager = IStakeManager(registry.getStakeManagerAddress());
                      uint256 _reward = stakeManager.checkSignatures(
                          end.sub(start).add(1),
                          /**  
                              prefix 01 to data 
                              01 represents positive vote on data and 00 is negative vote
                              malicious validator can try to send 2/3 on negative vote so 01 is appended
                           */
                          keccak256(abi.encodePacked(bytes(hex"01"), data)),
                          accountHash,
                          proposer,
                          sigs
                      );
              
                      require(_reward != 0, "Invalid checkpoint");
                      emit NewHeaderBlock(proposer, _nextHeaderBlock, _reward, start, end, rootHash);
                      _nextHeaderBlock = _nextHeaderBlock.add(MAX_DEPOSITS);
                      _blockDepositId = 1;
                  }
              
                  function updateDepositId(uint256 numDeposits) external onlyDepositManager returns (uint256 depositId) {
                      depositId = currentHeaderBlock().add(_blockDepositId);
                      // deposit ids will be (_blockDepositId, _blockDepositId + 1, .... _blockDepositId + numDeposits - 1)
                      _blockDepositId = _blockDepositId.add(numDeposits);
                      require(
                          // Since _blockDepositId is initialized to 1; only (MAX_DEPOSITS - 1) deposits per header block are allowed
                          _blockDepositId <= MAX_DEPOSITS,
                          "TOO_MANY_DEPOSITS"
                      );
                  }
              
                  function getLastChildBlock() external view returns (uint256) {
                      return headerBlocks[currentHeaderBlock()].end;
                  }
              
                  function slash() external {
                      //TODO: future implementation
                  }
              
                  function currentHeaderBlock() public view returns (uint256) {
                      return _nextHeaderBlock.sub(MAX_DEPOSITS);
                  }
              
                  function _buildHeaderBlock(
                      address proposer,
                      uint256 start,
                      uint256 end,
                      bytes32 rootHash
                  ) private returns (bool) {
                      uint256 nextChildBlock;
                      /*
                  The ID of the 1st header block is MAX_DEPOSITS.
                  if _nextHeaderBlock == MAX_DEPOSITS, then the first header block is yet to be submitted, hence nextChildBlock = 0
                  */
                      if (_nextHeaderBlock > MAX_DEPOSITS) {
                          nextChildBlock = headerBlocks[currentHeaderBlock()].end + 1;
                      }
                      if (nextChildBlock != start) {
                          return false;
                      }
              
                      HeaderBlock memory headerBlock = HeaderBlock({
                          root: rootHash,
                          start: nextChildBlock,
                          end: end,
                          createdAt: now,
                          proposer: proposer
                      });
              
                      headerBlocks[_nextHeaderBlock] = headerBlock;
                      return true;
                  }
              
                  // Housekeeping function. @todo remove later
                  function setNextHeaderBlock(uint256 _value) public onlyOwner {
                      require(_value % MAX_DEPOSITS == 0, "Invalid value");
                      for (uint256 i = _value; i < _nextHeaderBlock; i += MAX_DEPOSITS) {
                          delete headerBlocks[i];
                      }
                      _nextHeaderBlock = _value;
                      _blockDepositId = 1;
                      emit ResetHeaderBlock(msg.sender, _nextHeaderBlock);
                  }
              
                  // Housekeeping function. @todo remove later
                  function setHeimdallId(string memory _heimdallId) public onlyOwner {
                      heimdallId = keccak256(abi.encodePacked(_heimdallId));
                  }
              }
              
              // File: contracts/root/stateSyncer/StateSender.sol
              
              pragma solidity ^0.5.2;
              
              
              
              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: contracts/common/mixin/Lockable.sol
              
              pragma solidity ^0.5.2;
              
              contract Lockable {
                  bool public locked;
              
                  modifier onlyWhenUnlocked() {
                      _assertUnlocked();
                      _;
                  }
              
                  function _assertUnlocked() private view {
                      require(!locked, "locked");
                  }
              
                  function lock() public {
                      locked = true;
                  }
              
                  function unlock() public {
                      locked = false;
                  }
              }
              
              // File: contracts/common/mixin/GovernanceLockable.sol
              
              pragma solidity ^0.5.2;
              
              
              
              contract GovernanceLockable is Lockable, Governable {
                  constructor(address governance) public Governable(governance) {}
              
                  function lock() public onlyGovernance {
                      super.lock();
                  }
              
                  function unlock() public onlyGovernance {
                      super.unlock();
                  }
              }
              
              // File: contracts/root/depositManager/DepositManagerStorage.sol
              
              pragma solidity ^0.5.2;
              
              
              
              
              
              
              
              contract DepositManagerHeader {
                  event NewDepositBlock(address indexed owner, address indexed token, uint256 amountOrNFTId, uint256 depositBlockId);
                  event MaxErc20DepositUpdate(uint256 indexed oldLimit, uint256 indexed newLimit);
              
                  struct DepositBlock {
                      bytes32 depositHash;
                      uint256 createdAt;
                  }
              }
              
              
              contract DepositManagerStorage is ProxyStorage, GovernanceLockable, DepositManagerHeader {
                  Registry public registry;
                  RootChain public rootChain;
                  StateSender public stateSender;
              
                  mapping(uint256 => DepositBlock) public deposits;
              
                  address public childChain;
                  uint256 public maxErc20Deposit = 100 * (10**18);
              }
              
              // File: contracts/root/depositManager/DepositManager.sol
              
              pragma solidity ^0.5.2;
              
              
              
              
              
              
              
              
              
              
              
              
              
              
              contract DepositManager is DepositManagerStorage, IDepositManager, ERC721Holder {
                  using SafeMath for uint256;
                  using SafeERC20 for IERC20;
              
                  modifier isTokenMapped(address _token) {
                      require(registry.isTokenMapped(_token), "TOKEN_NOT_SUPPORTED");
                      _;
                  }
              
                  modifier isPredicateAuthorized() {
                      require(uint8(registry.predicates(msg.sender)) != 0, "Not a valid predicate");
                      _;
                  }
              
                  constructor() public GovernanceLockable(address(0x0)) {}
              
                  // deposit ETH by sending to this contract
                  function() external payable {
                      depositEther();
                  }
              
                  function updateMaxErc20Deposit(uint256 maxDepositAmount) public onlyGovernance {
                      require(maxDepositAmount != 0);
                      emit MaxErc20DepositUpdate(maxErc20Deposit, maxDepositAmount);
                      maxErc20Deposit = maxDepositAmount;
                  }
              
                  function transferAssets(
                      address _token,
                      address _user,
                      uint256 _amountOrNFTId
                  ) external isPredicateAuthorized {
                      address wethToken = registry.getWethTokenAddress();
                      if (registry.isERC721(_token)) {
                          IERC721(_token).transferFrom(address(this), _user, _amountOrNFTId);
                      } else if (_token == wethToken) {
                          WETH t = WETH(_token);
                          t.withdraw(_amountOrNFTId, _user);
                      } else {
                          require(IERC20(_token).transfer(_user, _amountOrNFTId), "TRANSFER_FAILED");
                      }
                  }
              
                  function depositERC20(address _token, uint256 _amount) external {
                      depositERC20ForUser(_token, msg.sender, _amount);
                  }
              
                  function depositERC721(address _token, uint256 _tokenId) external {
                      depositERC721ForUser(_token, msg.sender, _tokenId);
                  }
              
                  function depositBulk(
                      address[] calldata _tokens,
                      uint256[] calldata _amountOrTokens,
                      address _user
                  )
                      external
                      onlyWhenUnlocked // unlike other deposit functions, depositBulk doesn't invoke _safeCreateDepositBlock
                  {
                      require(_tokens.length == _amountOrTokens.length, "Invalid Input");
                      uint256 depositId = rootChain.updateDepositId(_tokens.length);
                      Registry _registry = registry;
              
                      for (uint256 i = 0; i < _tokens.length; i++) {
                          // will revert if token is not mapped
                          if (_registry.isTokenMappedAndIsErc721(_tokens[i])) {
                              _safeTransferERC721(msg.sender, _tokens[i], _amountOrTokens[i]);
                          } else {
                              IERC20(_tokens[i]).safeTransferFrom(msg.sender, address(this), _amountOrTokens[i]);
                          }
              
                          _createDepositBlock(_user, _tokens[i], _amountOrTokens[i], depositId);
                          depositId = depositId.add(1);
                      }
                  }
              
                  /**
                   * @dev Caches childChain and stateSender (frequently used variables) from registry
                   */
                  function updateChildChainAndStateSender() public {
                      (address _childChain, address _stateSender) = registry.getChildChainAndStateSender();
                      require(
                          _stateSender != address(stateSender) || _childChain != childChain,
                          "Atleast one of stateSender or childChain address should change"
                      );
                      childChain = _childChain;
                      stateSender = StateSender(_stateSender);
                  }
              
                  function depositERC20ForUser(
                      address _token,
                      address _user,
                      uint256 _amount
                  ) public {
                      require(_amount <= maxErc20Deposit, "exceed maximum deposit amount");
                      IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);
                      _safeCreateDepositBlock(_user, _token, _amount);
                  }
              
                  function depositERC721ForUser(
                      address _token,
                      address _user,
                      uint256 _tokenId
                  ) public {
                      require(registry.isTokenMappedAndIsErc721(_token), "not erc721");
              
                      _safeTransferERC721(msg.sender, _token, _tokenId);
                      _safeCreateDepositBlock(_user, _token, _tokenId);
                  }
              
                  // @todo: write depositEtherForUser
                  function depositEther() public payable {
                      address wethToken = registry.getWethTokenAddress();
                      WETH t = WETH(wethToken);
                      t.deposit.value(msg.value)();
                      _safeCreateDepositBlock(msg.sender, wethToken, msg.value);
                  }
              
                  function _safeCreateDepositBlock(
                      address _user,
                      address _token,
                      uint256 _amountOrToken
                  ) internal onlyWhenUnlocked isTokenMapped(_token) {
                      _createDepositBlock(
                          _user,
                          _token,
                          _amountOrToken,
                          rootChain.updateDepositId(1) /* returns _depositId */
                      );
                  }
              
                  function _createDepositBlock(
                      address _user,
                      address _token,
                      uint256 _amountOrToken,
                      uint256 _depositId
                  ) internal {
                      deposits[_depositId] = DepositBlock(keccak256(abi.encodePacked(_user, _token, _amountOrToken)), now);
                      stateSender.syncState(childChain, abi.encode(_user, _token, _amountOrToken, _depositId));
                      emit NewDepositBlock(_user, _token, _amountOrToken, _depositId);
                  }
              
                  // Housekeeping function. @todo remove later
                  function updateRootChain(address _rootChain) public onlyOwner {
                      rootChain = RootChain(_rootChain);
                  }
              
                  function _safeTransferERC721(address _user, address _token, uint256 _tokenId) private {
                      IERC721(_token).safeTransferFrom(_user, address(this), _tokenId);
                  }
              }