ETH Price: $2,647.07 (-0.64%)

Transaction Decoder

Block:
15648404 at Sep-30-2022 08:00:59 PM +UTC
Transaction Fee:
0.00388099879854691 ETH $10.27
Gas Used:
148,249 Gas / 26.17892059 Gwei

Emitted Events:

100 MaticToken.Transfer( from=[Sender] 0x2a8cf8defcee248a9238a3254594077c6c3c280e, to=[Receiver] DepositManagerProxy, value=282286890000000000000 )
101 MaticToken.Approval( owner=[Sender] 0x2a8cf8defcee248a9238a3254594077c6c3c280e, spender=[Receiver] DepositManagerProxy, value=115792089237316195423570985008687907853269984665640564039175297117913129639935 )
102 StateSender.StateSynced( id=2342613, contractAddress=0xD9c7C4ED...55Fe34861, data=0x0000000000000000000000002A8CF8DEFCEE248A9238A3254594077C6C3C280E0000000000000000000000007D1AFA7B718FB893DB30A3ABC0CFC608AACFEBB000000000000000000000000000000000000000000000000F4D847ED5BFC4A0000000000000000000000000000000000000000000000000000000000016265191 )
103 DepositManagerProxy.NewDepositBlock( owner=[Sender] 0x2a8cf8defcee248a9238a3254594077c6c3c280e, token=MaticToken, amountOrNFTId=282286890000000000000, depositBlockId=371610001 )

Account State Difference:

  Address   Before After State Difference Code
(bloXroute: Regulated Builder)
4.057384855866773387 Eth4.057688707850925763 Eth0.000303851984152376
0x28e4F3a7...189A5bFbE
(Polygon (Matic): State Syncer)
0x2A8cf8dE...C6C3C280E
0.00795009485027063 Eth
Nonce: 7
0.00406909605172372 Eth
Nonce: 8
0.00388099879854691
0x401F6c98...51321188b
(Polygon (Matic): Plasma Bridge)
0x7D1AfA7B...8AaCfeBB0
0x86E4Dc95...23894C287
(Polygon (Matic): Root Chain Proxy)

Execution Trace

DepositManagerProxy.8b9e4f93( )
  • DepositManager.depositERC20ForUser( _token=0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0, _user=0x2A8cf8dEFcEe248A9238A3254594077C6C3C280E, _amount=282286890000000000000 )
    • MaticToken.transferFrom( from=0x2A8cf8dEFcEe248A9238A3254594077C6C3C280E, to=0x401F6c983eA34274ec46f84D70b31C151321188b, value=282286890000000000000 ) => ( True )
    • Registry.isTokenMapped( _token=0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0 ) => ( True )
    • RootChainProxy.5391f483( )
      • RootChain.updateDepositId( numDeposits=1 ) => ( depositId=371610001 )
        • Registry.STATICCALL( )
        • StateSender.syncState( receiver=0xD9c7C4ED4B66858301D0cb28Cc88bf655Fe34861, data=0x0000000000000000000000002A8CF8DEFCEE248A9238A3254594077C6C3C280E0000000000000000000000007D1AFA7B718FB893DB30A3ABC0CFC608AACFEBB000000000000000000000000000000000000000000000000F4D847ED5BFC4A0000000000000000000000000000000000000000000000000000000000016265191 )
          File 1 of 7: 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 2 of 7: 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 3 of 7: StateSender
          /**
          Matic network contracts
          */
          
          pragma solidity ^0.5.2;
          
          
          contract Ownable {
              address private _owner;
          
              event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          
              /**
               * @dev The Ownable constructor sets the original `owner` of the contract to the sender
               * account.
               */
              constructor () internal {
                  _owner = msg.sender;
                  emit OwnershipTransferred(address(0), _owner);
              }
          
              /**
               * @return the address of the owner.
               */
              function owner() public view returns (address) {
                  return _owner;
              }
          
              /**
               * @dev Throws if called by any account other than the owner.
               */
              modifier onlyOwner() {
                  require(isOwner());
                  _;
              }
          
              /**
               * @return true if `msg.sender` is the owner of the contract.
               */
              function isOwner() public view returns (bool) {
                  return msg.sender == _owner;
              }
          
              /**
               * @dev Allows the current owner to relinquish control of the contract.
               * It will not be possible to call the functions with the `onlyOwner`
               * modifier anymore.
               * @notice Renouncing ownership will leave the contract without an owner,
               * thereby removing any functionality that is only available to the owner.
               */
              function renounceOwnership() public onlyOwner {
                  emit OwnershipTransferred(_owner, address(0));
                  _owner = address(0);
              }
          
              /**
               * @dev Allows the current owner to transfer control of the contract to a newOwner.
               * @param newOwner The address to transfer ownership to.
               */
              function transferOwnership(address newOwner) public onlyOwner {
                  _transferOwnership(newOwner);
              }
          
              /**
               * @dev Transfers control of the contract to a newOwner.
               * @param newOwner The address to transfer ownership to.
               */
              function _transferOwnership(address newOwner) internal {
                  require(newOwner != address(0));
                  emit OwnershipTransferred(_owner, newOwner);
                  _owner = newOwner;
              }
          }
          
          library SafeMath {
              /**
               * @dev Multiplies two unsigned integers, reverts on overflow.
               */
              function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                  // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                  // benefit is lost if 'b' is also tested.
                  // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
                  if (a == 0) {
                      return 0;
                  }
          
                  uint256 c = a * b;
                  require(c / a == b);
          
                  return c;
              }
          
              /**
               * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
               */
              function div(uint256 a, uint256 b) internal pure returns (uint256) {
                  // Solidity only automatically asserts when dividing by 0
                  require(b > 0);
                  uint256 c = a / b;
                  // assert(a == b * c + a % b); // There is no case in which this doesn't hold
          
                  return c;
              }
          
              /**
               * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
               */
              function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                  require(b <= a);
                  uint256 c = a - b;
          
                  return c;
              }
          
              /**
               * @dev Adds two unsigned integers, reverts on overflow.
               */
              function add(uint256 a, uint256 b) internal pure returns (uint256) {
                  uint256 c = a + b;
                  require(c >= a);
          
                  return c;
              }
          
              /**
               * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
               * reverts when dividing by zero.
               */
              function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                  require(b != 0);
                  return a % b;
              }
          }
          
          contract StateSender is Ownable {
              using SafeMath for uint256;
          
              uint256 public counter;
              mapping(address => address) public registrations;
          
              event NewRegistration(
                  address indexed user,
                  address indexed sender,
                  address indexed receiver
              );
              event RegistrationUpdated(
                  address indexed user,
                  address indexed sender,
                  address indexed receiver
              );
              event StateSynced(
                  uint256 indexed id,
                  address indexed contractAddress,
                  bytes data
              );
          
              modifier onlyRegistered(address receiver) {
                  require(registrations[receiver] == msg.sender, "Invalid sender");
                  _;
              }
          
              function syncState(address receiver, bytes calldata data)
                  external
                  onlyRegistered(receiver)
              {
                  counter = counter.add(1);
                  emit StateSynced(counter, receiver, data);
              }
          
              // register new contract for state sync
              function register(address sender, address receiver) public {
                  require(
                      isOwner() || registrations[receiver] == msg.sender,
                      "StateSender.register: Not authorized to register"
                  );
                  registrations[receiver] = sender;
                  if (registrations[receiver] == address(0)) {
                      emit NewRegistration(msg.sender, sender, receiver);
                  } else {
                      emit RegistrationUpdated(msg.sender, sender, receiver);
                  }
              }
          }

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

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

          File 7 of 7: RootChain
          // File: solidity-rlp/contracts/RLPReader.sol
          
          // SPDX-License-Identifier: Apache-2.0
          
          /*
          * @author Hamdi Allam [email protected]
          * Please reach out with any questions or concerns
          */
          pragma solidity >=0.5.0 <0.7.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 except "0x80" is considered true
              function toBoolean(RLPItem memory item) internal pure returns (bool) {
                  require(item.len == 1);
                  uint result;
                  uint memPtr = item.memPtr;
                  assembly {
                      result := byte(0, mload(memPtr))
                  }
          
                  // SEE Github Issue #5.
                  // Summary: Most commonly used RLP libraries (i.e Geth) will encode
                  // "0" as "0x80" instead of as "0". We handle this edge case explicitly
                  // here.
                  if (result == 0 || result == STRING_SHORT_START) {
                      return false;
                  } else {
                      return true;
                  }
              }
          
              function toAddress(RLPItem memory item) internal pure returns (address) {
                  // 1 byte for the length prefix
                  require(item.len == 21);
          
                  return address(toUint(item));
              }
          
              function toUint(RLPItem memory item) internal pure returns (uint) {
                  require(item.len > 0 && item.len <= 33);
          
                  uint 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: 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/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.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));
              }
          }