ETH Price: $2,263.28 (+7.45%)

Transaction Decoder

Block:
19030612 at Jan-18-2024 01:56:35 AM +UTC
Transaction Fee:
0.00970968615913992 ETH $21.98
Gas Used:
158,295 Gas / 61.339184176 Gwei

Account State Difference:

  Address   Before After State Difference Code
(Titan Builder)
71.178950082637361979 Eth71.184341121079017684 Eth0.005391038441655705
0x794e44D1...20D0c5888
3.645698965742725864 Eth
Nonce: 1642
3.635989279583585944 Eth
Nonce: 1643
0.00970968615913992

Execution Trace

RootChainProxy.4e43e495( )
  • RootChain.4e43e495( )
    File 1 of 2: 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 2 of 2: RootChain
    // File: solidity-rlp/contracts/RLPReader.sol
    
    // SPDX-License-Identifier: Apache-2.0
    
    /*
    * @author Hamdi Allam hamdi.allam97@gmail.com
    * 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));
        }
    }