ETH Price: $2,658.39 (+3.21%)

Transaction Decoder

Block:
12801644 at Jul-10-2021 07:56:18 PM +UTC
Transaction Fee:
0.000415824 ETH $1.11
Gas Used:
34,652 Gas / 12 Gwei

Account State Difference:

  Address   Before After State Difference Code
(Hiveon Pool)
4,352.646832888484803631 Eth4,352.647248712484803631 Eth0.000415824
0x2C548219...58f7124A5
0.00469837 Eth
Nonce: 6
0.004282546 Eth
Nonce: 7
0.000415824

Execution Trace

ValidatorShareProxy.c83ec04d( )
  • Registry.STATICCALL( )
  • ValidatorShare.sellVoucher_new( claimAmount=2500000000000000000000, maximumSharesToBurn=2500000000000000000000 )
    File 1 of 3: ValidatorShareProxy
    // File: openzeppelin-solidity/contracts/ownership/Ownable.sol
    
    pragma solidity ^0.5.2;
    
    /**
     * @title Ownable
     * @dev The Ownable contract has an owner address, and provides basic authorization control
     * functions, this simplifies the implementation of "user permissions".
     */
    contract Ownable {
        address private _owner;
    
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    
        /**
         * @dev The Ownable constructor sets the original `owner` of the contract to the sender
         * account.
         */
        constructor () internal {
            _owner = msg.sender;
            emit OwnershipTransferred(address(0), _owner);
        }
    
        /**
         * @return the address of the owner.
         */
        function owner() public view returns (address) {
            return _owner;
        }
    
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            require(isOwner());
            _;
        }
    
        /**
         * @return true if `msg.sender` is the owner of the contract.
         */
        function isOwner() public view returns (bool) {
            return msg.sender == _owner;
        }
    
        /**
         * @dev Allows the current owner to relinquish control of the contract.
         * It will not be possible to call the functions with the `onlyOwner`
         * modifier anymore.
         * @notice Renouncing ownership will leave the contract without an owner,
         * thereby removing any functionality that is only available to the owner.
         */
        function renounceOwnership() public onlyOwner {
            emit OwnershipTransferred(_owner, address(0));
            _owner = address(0);
        }
    
        /**
         * @dev Allows the current owner to transfer control of the contract to a newOwner.
         * @param newOwner The address to transfer ownership to.
         */
        function transferOwnership(address newOwner) public onlyOwner {
            _transferOwnership(newOwner);
        }
    
        /**
         * @dev Transfers control of the contract to a newOwner.
         * @param newOwner The address to transfer ownership to.
         */
        function _transferOwnership(address newOwner) internal {
            require(newOwner != address(0));
            emit OwnershipTransferred(_owner, newOwner);
            _owner = newOwner;
        }
    }
    
    // File: contracts/common/misc/ProxyStorage.sol
    
    pragma solidity ^0.5.2;
    
    
    contract ProxyStorage is Ownable {
        address internal proxyTo;
    }
    
    // File: contracts/common/misc/ERCProxy.sol
    
    /*
     * SPDX-License-Identitifer:    MIT
     */
    
    pragma solidity ^0.5.2;
    
    // See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-897.md
    
    interface ERCProxy {
        function proxyType() external pure returns (uint256 proxyTypeId);
        function implementation() external view returns (address codeAddr);
    }
    
    // File: contracts/common/misc/DelegateProxy.sol
    
    pragma solidity ^0.5.2;
    
    
    
    contract DelegateProxy is ERCProxy {
        function proxyType() external pure returns (uint256 proxyTypeId) {
            // Upgradeable proxy
            proxyTypeId = 2;
        }
    
        function implementation() external view returns (address);
    
        function delegatedFwd(address _dst, bytes memory _calldata) internal {
            // solium-disable-next-line security/no-inline-assembly
            assembly {
                let result := delegatecall(
                    sub(gas, 10000),
                    _dst,
                    add(_calldata, 0x20),
                    mload(_calldata),
                    0,
                    0
                )
                let size := returndatasize
    
                let ptr := mload(0x40)
                returndatacopy(ptr, 0, size)
    
                // revert instead of invalid() bc if the underlying call failed with invalid() it already wasted gas.
                // if the call returned error data, forward it
                switch result
                    case 0 {
                        revert(ptr, size)
                    }
                    default {
                        return(ptr, size)
                    }
            }
        }
    }
    
    // File: contracts/common/misc/UpgradableProxy.sol
    
    pragma solidity ^0.5.2;
    
    
    contract UpgradableProxy is DelegateProxy {
        event ProxyUpdated(address indexed _new, address indexed _old);
        event OwnerUpdate(address _new, address _old);
    
        bytes32 constant IMPLEMENTATION_SLOT = keccak256("matic.network.proxy.implementation");
        bytes32 constant OWNER_SLOT = keccak256("matic.network.proxy.owner");
    
        constructor(address _proxyTo) public {
            setOwner(msg.sender);
            setImplementation(_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(loadImplementation(), msg.data);
        }
    
        modifier onlyProxyOwner() {
            require(loadOwner() == msg.sender, "NOT_OWNER");
            _;
        }
    
        function owner() external view returns(address) {
            return loadOwner();
        }
    
        function loadOwner() internal view returns(address) {
            address _owner;
            bytes32 position = OWNER_SLOT;
            assembly {
                _owner := sload(position)
            }
            return _owner;
        }
    
        function implementation() external view returns (address) {
            return loadImplementation();
        }
    
        function loadImplementation() internal view returns(address) {
            address _impl;
            bytes32 position = IMPLEMENTATION_SLOT;
            assembly {
                _impl := sload(position)
            }
            return _impl;
        }
    
        function transferOwnership(address newOwner) public onlyProxyOwner {
            require(newOwner != address(0), "ZERO_ADDRESS");
            emit OwnerUpdate(newOwner, loadOwner());
            setOwner(newOwner);
        }
    
        function setOwner(address newOwner) private {
            bytes32 position = OWNER_SLOT;
            assembly {
                sstore(position, newOwner)
            }
        }
    
        function updateImplementation(address _newProxyTo) public onlyProxyOwner {
            require(_newProxyTo != address(0x0), "INVALID_PROXY_ADDRESS");
            require(isContract(_newProxyTo), "DESTINATION_ADDRESS_IS_NOT_A_CONTRACT");
    
            emit ProxyUpdated(_newProxyTo, loadImplementation());
            
            setImplementation(_newProxyTo);
        }
    
        function updateAndCall(address _newProxyTo, bytes memory data) payable public onlyProxyOwner {
            updateImplementation(_newProxyTo);
    
            (bool success, bytes memory returnData) = address(this).call.value(msg.value)(data);
            require(success, string(returnData));
        }
    
        function setImplementation(address _newProxyTo) private {
            bytes32 position = IMPLEMENTATION_SLOT;
            assembly {
                sstore(position, _newProxyTo)
            }
        }
        
        function isContract(address _target) internal view returns (bool) {
            if (_target == address(0)) {
                return false;
            }
    
            uint256 size;
            assembly {
                size := extcodesize(_target)
            }
            return size > 0;
        }
    }
    
    // File: contracts/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: contracts/staking/validatorShare/ValidatorShareProxy.sol
    
    pragma solidity ^0.5.2;
    
    
    
    contract ValidatorShareProxy is UpgradableProxy {
        constructor(address _registry) public UpgradableProxy(_registry) {}
    
        function loadImplementation() internal view returns (address) {
            return Registry(super.loadImplementation()).getValidatorShareAddress();
        }
    }

    File 2 of 3: 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 3 of 3: ValidatorShare
    // 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/IERC20.sol
    
    pragma solidity ^0.5.2;
    
    /**
     * @title ERC20 interface
     * @dev see https://eips.ethereum.org/EIPS/eip-20
     */
    interface IERC20 {
        function transfer(address to, uint256 value) external returns (bool);
    
        function approve(address spender, uint256 value) external returns (bool);
    
        function transferFrom(address from, address to, uint256 value) external returns (bool);
    
        function totalSupply() external view returns (uint256);
    
        function balanceOf(address who) external view returns (uint256);
    
        function allowance(address owner, address spender) external view returns (uint256);
    
        event Transfer(address indexed from, address indexed to, uint256 value);
    
        event Approval(address indexed owner, address indexed spender, uint256 value);
    }
    
    // File: openzeppelin-solidity/contracts/math/SafeMath.sol
    
    pragma solidity ^0.5.2;
    
    /**
     * @title SafeMath
     * @dev Unsigned math operations with safety checks that revert on error
     */
    library SafeMath {
        /**
         * @dev Multiplies two unsigned integers, reverts on overflow.
         */
        function mul(uint256 a, uint256 b) internal pure returns (uint256) {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
            if (a == 0) {
                return 0;
            }
    
            uint256 c = a * b;
            require(c / a == b);
    
            return c;
        }
    
        /**
         * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
         */
        function div(uint256 a, uint256 b) internal pure returns (uint256) {
            // Solidity only automatically asserts when dividing by 0
            require(b > 0);
            uint256 c = a / b;
            // assert(a == b * c + a % b); // There is no case in which this doesn't hold
    
            return c;
        }
    
        /**
         * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
         */
        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
            require(b <= a);
            uint256 c = a - b;
    
            return c;
        }
    
        /**
         * @dev Adds two unsigned integers, reverts on overflow.
         */
        function add(uint256 a, uint256 b) internal pure returns (uint256) {
            uint256 c = a + b;
            require(c >= a);
    
            return c;
        }
    
        /**
         * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
         * reverts when dividing by zero.
         */
        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
            require(b != 0);
            return a % b;
        }
    }
    
    // File: openzeppelin-solidity/contracts/token/ERC20/ERC20.sol
    
    pragma solidity ^0.5.2;
    
    
    
    /**
     * @title Standard ERC20 token
     *
     * @dev Implementation of the basic standard token.
     * https://eips.ethereum.org/EIPS/eip-20
     * Originally based on code by FirstBlood:
     * https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
     *
     * This implementation emits additional Approval events, allowing applications to reconstruct the allowance status for
     * all accounts just by listening to said events. Note that this isn't required by the specification, and other
     * compliant implementations may not do it.
     */
    contract ERC20 is IERC20 {
        using SafeMath for uint256;
    
        mapping (address => uint256) private _balances;
    
        mapping (address => mapping (address => uint256)) private _allowed;
    
        uint256 private _totalSupply;
    
        /**
         * @dev Total number of tokens in existence
         */
        function totalSupply() public view returns (uint256) {
            return _totalSupply;
        }
    
        /**
         * @dev Gets the balance of the specified address.
         * @param owner The address to query the balance of.
         * @return A uint256 representing the amount owned by the passed address.
         */
        function balanceOf(address owner) public view returns (uint256) {
            return _balances[owner];
        }
    
        /**
         * @dev Function to check the amount of tokens that an owner allowed to a spender.
         * @param owner address The address which owns the funds.
         * @param spender address The address which will spend the funds.
         * @return A uint256 specifying the amount of tokens still available for the spender.
         */
        function allowance(address owner, address spender) public view returns (uint256) {
            return _allowed[owner][spender];
        }
    
        /**
         * @dev Transfer token to a specified address
         * @param to The address to transfer to.
         * @param value The amount to be transferred.
         */
        function transfer(address to, uint256 value) public returns (bool) {
            _transfer(msg.sender, to, value);
            return true;
        }
    
        /**
         * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
         * Beware that changing an allowance with this method brings the risk that someone may use both the old
         * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
         * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
         * @param spender The address which will spend the funds.
         * @param value The amount of tokens to be spent.
         */
        function approve(address spender, uint256 value) public returns (bool) {
            _approve(msg.sender, spender, value);
            return true;
        }
    
        /**
         * @dev Transfer tokens from one address to another.
         * Note that while this function emits an Approval event, this is not required as per the specification,
         * and other compliant implementations may not emit the event.
         * @param from address The address which you want to send tokens from
         * @param to address The address which you want to tr vbmansfer 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/ERC20NonTradable.sol
    
    pragma solidity ^0.5.2;
    
    
    contract ERC20NonTradable is ERC20 {
        function _approve(
            address owner,
            address spender,
            uint256 value
        ) internal {
            revert("disabled");
        }
    }
    
    // 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/staking/StakingInfo.sol
    
    pragma solidity ^0.5.2;
    
    
    
    
    
    // dummy interface to avoid cyclic dependency
    contract IStakeManagerLocal {
        enum Status {Inactive, Active, Locked, Unstaked}
    
        struct Validator {
            uint256 amount;
            uint256 reward;
            uint256 activationEpoch;
            uint256 deactivationEpoch;
            uint256 jailTime;
            address signer;
            address contractAddress;
            Status status;
        }
    
        mapping(uint256 => Validator) public validators;
        bytes32 public accountStateRoot;
        uint256 public activeAmount; // delegation amount from validator contract
        uint256 public validatorRewards;
    
        function currentValidatorSetTotalStake() public view returns (uint256);
    
        // signer to Validator mapping
        function signerToValidator(address validatorAddress)
            public
            view
            returns (uint256);
    
        function isValidator(uint256 validatorId) public view returns (bool);
    }
    
    contract StakingInfo is Ownable {
        using SafeMath for uint256;
        mapping(uint256 => uint256) public validatorNonce;
    
        /// @dev Emitted when validator stakes in '_stakeFor()' in StakeManager.
        /// @param signer validator address.
        /// @param validatorId unique integer to identify a validator.
        /// @param nonce to synchronize the events in heimdal.
        /// @param activationEpoch validator's first epoch as proposer.
        /// @param amount staking amount.
        /// @param total total staking amount.
        /// @param signerPubkey public key of the validator
        event Staked(
            address indexed signer,
            uint256 indexed validatorId,
            uint256 nonce,
            uint256 indexed activationEpoch,
            uint256 amount,
            uint256 total,
            bytes signerPubkey
        );
    
        /// @dev Emitted when validator unstakes in 'unstakeClaim()'
        /// @param user address of the validator.
        /// @param validatorId unique integer to identify a validator.
        /// @param amount staking amount.
        /// @param total total staking amount.
        event Unstaked(
            address indexed user,
            uint256 indexed validatorId,
            uint256 amount,
            uint256 total
        );
    
        /// @dev Emitted when validator unstakes in '_unstake()'.
        /// @param user address of the validator.
        /// @param validatorId unique integer to identify a validator.
        /// @param nonce to synchronize the events in heimdal.
        /// @param deactivationEpoch last epoch for validator.
        /// @param amount staking amount.
        event UnstakeInit(
            address indexed user,
            uint256 indexed validatorId,
            uint256 nonce,
            uint256 deactivationEpoch,
            uint256 indexed amount
        );
    
        /// @dev Emitted when the validator public key is updated in 'updateSigner()'.
        /// @param validatorId unique integer to identify a validator.
        /// @param nonce to synchronize the events in heimdal.
        /// @param oldSigner old address of the validator.
        /// @param newSigner new address of the validator.
        /// @param signerPubkey public key of the validator.
        event SignerChange(
            uint256 indexed validatorId,
            uint256 nonce,
            address indexed oldSigner,
            address indexed newSigner,
            bytes signerPubkey
        );
        event Restaked(uint256 indexed validatorId, uint256 amount, uint256 total);
        event Jailed(
            uint256 indexed validatorId,
            uint256 indexed exitEpoch,
            address indexed signer
        );
        event UnJailed(uint256 indexed validatorId, address indexed signer);
        event Slashed(uint256 indexed nonce, uint256 indexed amount);
        event ThresholdChange(uint256 newThreshold, uint256 oldThreshold);
        event DynastyValueChange(uint256 newDynasty, uint256 oldDynasty);
        event ProposerBonusChange(
            uint256 newProposerBonus,
            uint256 oldProposerBonus
        );
    
        event RewardUpdate(uint256 newReward, uint256 oldReward);
    
        /// @dev Emitted when validator confirms the auction bid and at the time of restaking in confirmAuctionBid() and restake().
        /// @param validatorId unique integer to identify a validator.
        /// @param nonce to synchronize the events in heimdal.
        /// @param newAmount the updated stake amount.
        event StakeUpdate(
            uint256 indexed validatorId,
            uint256 indexed nonce,
            uint256 indexed newAmount
        );
        event ClaimRewards(
            uint256 indexed validatorId,
            uint256 indexed amount,
            uint256 indexed totalAmount
        );
        event StartAuction(
            uint256 indexed validatorId,
            uint256 indexed amount,
            uint256 indexed auctionAmount
        );
        event ConfirmAuction(
            uint256 indexed newValidatorId,
            uint256 indexed oldValidatorId,
            uint256 indexed amount
        );
        event TopUpFee(address indexed user, uint256 indexed fee);
        event ClaimFee(address indexed user, uint256 indexed fee);
        // Delegator events
        event ShareMinted(
            uint256 indexed validatorId,
            address indexed user,
            uint256 indexed amount,
            uint256 tokens
        );
        event ShareBurned(
            uint256 indexed validatorId,
            address indexed user,
            uint256 indexed amount,
            uint256 tokens
        );
        event DelegatorClaimedRewards(
            uint256 indexed validatorId,
            address indexed user,
            uint256 indexed rewards
        );
        event DelegatorRestaked(
            uint256 indexed validatorId,
            address indexed user,
            uint256 indexed totalStaked
        );
        event DelegatorUnstaked(
            uint256 indexed validatorId,
            address indexed user,
            uint256 amount
        );
        event UpdateCommissionRate(
            uint256 indexed validatorId,
            uint256 indexed newCommissionRate,
            uint256 indexed oldCommissionRate
        );
    
        Registry public registry;
    
        modifier onlyValidatorContract(uint256 validatorId) {
            address _contract;
            (, , , , , , _contract, ) = IStakeManagerLocal(
                registry.getStakeManagerAddress()
            )
                .validators(validatorId);
            require(_contract == msg.sender,
            "Invalid sender, not validator");
            _;
        }
    
        modifier StakeManagerOrValidatorContract(uint256 validatorId) {
            address _contract;
            address _stakeManager = registry.getStakeManagerAddress();
            (, , , , , , _contract, ) = IStakeManagerLocal(_stakeManager).validators(
                validatorId
            );
            require(_contract == msg.sender || _stakeManager == msg.sender,
            "Invalid sender, not stake manager or validator contract");
            _;
        }
    
        modifier onlyStakeManager() {
            require(registry.getStakeManagerAddress() == msg.sender,
            "Invalid sender, not stake manager");
            _;
        }
        modifier onlySlashingManager() {
            require(registry.getSlashingManagerAddress() == msg.sender,
            "Invalid sender, not slashing manager");
            _;
        }
    
        constructor(address _registry) public {
            registry = Registry(_registry);
        }
    
        function updateNonce(
            uint256[] calldata validatorIds,
            uint256[] calldata nonces
        ) external onlyOwner {
            require(validatorIds.length == nonces.length, "args length mismatch");
    
            for (uint256 i = 0; i < validatorIds.length; ++i) {
                validatorNonce[validatorIds[i]] = nonces[i];
            }
        } 
    
        function logStaked(
            address signer,
            bytes memory signerPubkey,
            uint256 validatorId,
            uint256 activationEpoch,
            uint256 amount,
            uint256 total
        ) public onlyStakeManager {
            validatorNonce[validatorId] = validatorNonce[validatorId].add(1);
            emit Staked(
                signer,
                validatorId,
                validatorNonce[validatorId],
                activationEpoch,
                amount,
                total,
                signerPubkey
            );
        }
    
        function logUnstaked(
            address user,
            uint256 validatorId,
            uint256 amount,
            uint256 total
        ) public onlyStakeManager {
            emit Unstaked(user, validatorId, amount, total);
        }
    
        function logUnstakeInit(
            address user,
            uint256 validatorId,
            uint256 deactivationEpoch,
            uint256 amount
        ) public onlyStakeManager {
            validatorNonce[validatorId] = validatorNonce[validatorId].add(1);
            emit UnstakeInit(
                user,
                validatorId,
                validatorNonce[validatorId],
                deactivationEpoch,
                amount
            );
        }
    
        function logSignerChange(
            uint256 validatorId,
            address oldSigner,
            address newSigner,
            bytes memory signerPubkey
        ) public onlyStakeManager {
            validatorNonce[validatorId] = validatorNonce[validatorId].add(1);
            emit SignerChange(
                validatorId,
                validatorNonce[validatorId],
                oldSigner,
                newSigner,
                signerPubkey
            );
        }
    
        function logRestaked(uint256 validatorId, uint256 amount, uint256 total)
            public
            onlyStakeManager
        {
            emit Restaked(validatorId, amount, total);
        }
    
        function logJailed(uint256 validatorId, uint256 exitEpoch, address signer)
            public
            onlyStakeManager
        {
            emit Jailed(validatorId, exitEpoch, signer);
        }
    
        function logUnjailed(uint256 validatorId, address signer)
            public
            onlyStakeManager
        {
            emit UnJailed(validatorId, signer);
        }
    
        function logSlashed(uint256 nonce, uint256 amount)
            public
            onlySlashingManager
        {
            emit Slashed(nonce, amount);
        }
    
        function logThresholdChange(uint256 newThreshold, uint256 oldThreshold)
            public
            onlyStakeManager
        {
            emit ThresholdChange(newThreshold, oldThreshold);
        }
    
        function logDynastyValueChange(uint256 newDynasty, uint256 oldDynasty)
            public
            onlyStakeManager
        {
            emit DynastyValueChange(newDynasty, oldDynasty);
        }
    
        function logProposerBonusChange(
            uint256 newProposerBonus,
            uint256 oldProposerBonus
        ) public onlyStakeManager {
            emit ProposerBonusChange(newProposerBonus, oldProposerBonus);
        }
    
        function logRewardUpdate(uint256 newReward, uint256 oldReward)
            public
            onlyStakeManager
        {
            emit RewardUpdate(newReward, oldReward);
        }
    
        function logStakeUpdate(uint256 validatorId)
            public
            StakeManagerOrValidatorContract(validatorId)
        {
            validatorNonce[validatorId] = validatorNonce[validatorId].add(1);
            emit StakeUpdate(
                validatorId,
                validatorNonce[validatorId],
                totalValidatorStake(validatorId)
            );
        }
    
        function logClaimRewards(
            uint256 validatorId,
            uint256 amount,
            uint256 totalAmount
        ) public onlyStakeManager {
            emit ClaimRewards(validatorId, amount, totalAmount);
        }
    
        function logStartAuction(
            uint256 validatorId,
            uint256 amount,
            uint256 auctionAmount
        ) public onlyStakeManager {
            emit StartAuction(validatorId, amount, auctionAmount);
        }
    
        function logConfirmAuction(
            uint256 newValidatorId,
            uint256 oldValidatorId,
            uint256 amount
        ) public onlyStakeManager {
            emit ConfirmAuction(newValidatorId, oldValidatorId, amount);
        }
    
        function logTopUpFee(address user, uint256 fee) public onlyStakeManager {
            emit TopUpFee(user, fee);
        }
    
        function logClaimFee(address user, uint256 fee) public onlyStakeManager {
            emit ClaimFee(user, fee);
        }
    
        function getStakerDetails(uint256 validatorId)
            public
            view
            returns (
                uint256 amount,
                uint256 reward,
                uint256 activationEpoch,
                uint256 deactivationEpoch,
                address signer,
                uint256 _status
            )
        {
            IStakeManagerLocal stakeManager = IStakeManagerLocal(
                registry.getStakeManagerAddress()
            );
            address _contract;
            IStakeManagerLocal.Status status;
            (
                amount,
                reward,
                activationEpoch,
                deactivationEpoch,
                ,
                signer,
                _contract,
                status
            ) = stakeManager.validators(validatorId);
            _status = uint256(status);
            if (_contract != address(0x0)) {
                reward += IStakeManagerLocal(_contract).validatorRewards();
            }
        }
    
        function totalValidatorStake(uint256 validatorId)
            public
            view
            returns (uint256 validatorStake)
        {
            address contractAddress;
            (validatorStake, , , , , , contractAddress, ) = IStakeManagerLocal(
                registry.getStakeManagerAddress()
            )
                .validators(validatorId);
            if (contractAddress != address(0x0)) {
                validatorStake += IStakeManagerLocal(contractAddress).activeAmount();
            }
        }
    
        function getAccountStateRoot()
            public
            view
            returns (bytes32 accountStateRoot)
        {
            accountStateRoot = IStakeManagerLocal(registry.getStakeManagerAddress())
                .accountStateRoot();
        }
    
        function getValidatorContractAddress(uint256 validatorId)
            public
            view
            returns (address ValidatorContract)
        {
            (, , , , , , ValidatorContract, ) = IStakeManagerLocal(
                registry.getStakeManagerAddress()
            )
                .validators(validatorId);
        }
    
        // validator Share contract logging func
        function logShareMinted(
            uint256 validatorId,
            address user,
            uint256 amount,
            uint256 tokens
        ) public onlyValidatorContract(validatorId) {
            emit ShareMinted(validatorId, user, amount, tokens);
        }
    
        function logShareBurned(
            uint256 validatorId,
            address user,
            uint256 amount,
            uint256 tokens
        ) public onlyValidatorContract(validatorId) {
            emit ShareBurned(validatorId, user, amount, tokens);
        }
    
        function logDelegatorClaimRewards(
            uint256 validatorId,
            address user,
            uint256 rewards
        ) public onlyValidatorContract(validatorId) {
            emit DelegatorClaimedRewards(validatorId, user, rewards);
        }
    
        function logDelegatorRestaked(
            uint256 validatorId,
            address user,
            uint256 totalStaked
        ) public onlyValidatorContract(validatorId) {
            emit DelegatorRestaked(validatorId, user, totalStaked);
        }
    
        function logDelegatorUnstaked(uint256 validatorId, address user, uint256 amount)
            public
            onlyValidatorContract(validatorId)
        {
            emit DelegatorUnstaked(validatorId, user, amount);
        }
    
        // deprecated
        function logUpdateCommissionRate(
            uint256 validatorId,
            uint256 newCommissionRate,
            uint256 oldCommissionRate
        ) public onlyValidatorContract(validatorId) {
            emit UpdateCommissionRate(
                validatorId,
                newCommissionRate,
                oldCommissionRate
            );
        }
    }
    
    // File: contracts/common/mixin/Initializable.sol
    
    pragma solidity ^0.5.2;
    
    contract Initializable {
        bool inited = false;
    
        modifier initializer() {
            require(!inited, "already inited");
            inited = true;
            
            _;
        }
    }
    
    // File: contracts/staking/EventsHub.sol
    
    pragma solidity ^0.5.2;
    
    
    
    contract IStakeManagerEventsHub {
        struct Validator {
            uint256 amount;
            uint256 reward;
            uint256 activationEpoch;
            uint256 deactivationEpoch;
            uint256 jailTime;
            address signer;
            address contractAddress;
        }
    
        mapping(uint256 => Validator) public validators;
    }
    
    contract EventsHub is Initializable {
        Registry public registry;
    
        modifier onlyValidatorContract(uint256 validatorId) {
            address _contract;
            (, , , , , , _contract) = IStakeManagerEventsHub(registry.getStakeManagerAddress()).validators(validatorId);
            require(_contract == msg.sender, "not validator");
            _;
        }
    
        modifier onlyStakeManager() {
            require(registry.getStakeManagerAddress() == msg.sender,
            "Invalid sender, not stake manager");
            _;
        }
    
        function initialize(Registry _registry) external initializer {
            registry = _registry;
        }
    
        event ShareBurnedWithId(
            uint256 indexed validatorId,
            address indexed user,
            uint256 indexed amount,
            uint256 tokens,
            uint256 nonce
        );
    
        function logShareBurnedWithId(
            uint256 validatorId,
            address user,
            uint256 amount,
            uint256 tokens,
            uint256 nonce
        ) public onlyValidatorContract(validatorId) {
            emit ShareBurnedWithId(validatorId, user, amount, tokens, nonce);
        }
    
        event DelegatorUnstakeWithId(
            uint256 indexed validatorId,
            address indexed user,
            uint256 amount,
            uint256 nonce
        );
    
        function logDelegatorUnstakedWithId(
            uint256 validatorId,
            address user,
            uint256 amount,
            uint256 nonce
        ) public onlyValidatorContract(validatorId) {
            emit DelegatorUnstakeWithId(validatorId, user, amount, nonce);
        }
    
        event RewardParams(
            uint256 rewardDecreasePerCheckpoint,
            uint256 maxRewardedCheckpoints,
            uint256 checkpointRewardDelta
        );
    
        function logRewardParams(
            uint256 rewardDecreasePerCheckpoint,
            uint256 maxRewardedCheckpoints,
            uint256 checkpointRewardDelta
        ) public onlyStakeManager {
            emit RewardParams(rewardDecreasePerCheckpoint, maxRewardedCheckpoints, checkpointRewardDelta);
        }
    
        event UpdateCommissionRate(
            uint256 indexed validatorId,
            uint256 indexed newCommissionRate,
            uint256 indexed oldCommissionRate
        );
    
        function logUpdateCommissionRate(
            uint256 validatorId,
            uint256 newCommissionRate,
            uint256 oldCommissionRate
        ) public onlyStakeManager {
            emit UpdateCommissionRate(
                validatorId,
                newCommissionRate,
                oldCommissionRate
            );
        }
    }
    
    // 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/OwnableLockable.sol
    
    pragma solidity ^0.5.2;
    
    
    
    contract OwnableLockable is Lockable, Ownable {
        function lock() public onlyOwner {
            super.lock();
        }
    
        function unlock() public onlyOwner {
            super.unlock();
        }
    }
    
    // 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/staking/validatorShare/IValidatorShare.sol
    
    pragma solidity 0.5.17;
    
    // note this contract interface is only for stakeManager use
    contract IValidatorShare {
        function withdrawRewards() public;
    
        function unstakeClaimTokens() public;
    
        function getLiquidRewards(address user) public view returns (uint256);
        
        function owner() public view returns (address);
    
        function restake() public returns(uint256, uint256);
    
        function unlock() external;
    
        function lock() external;
    
        function drain(
            address token,
            address payable destination,
            uint256 amount
        ) external;
    
        function slash(uint256 valPow, uint256 delegatedAmount, uint256 totalAmountToSlash) external returns (uint256);
    
        function updateDelegation(bool delegation) external;
    
        function migrateOut(address user, uint256 amount) external;
    
        function migrateIn(address user, uint256 amount) external;
    }
    
    // File: contracts/staking/validatorShare/ValidatorShare.sol
    
    pragma solidity 0.5.17;
    
    
    
    
    
    
    
    
    
    
    contract ValidatorShare is IValidatorShare, ERC20NonTradable, OwnableLockable, Initializable {
        struct DelegatorUnbond {
            uint256 shares;
            uint256 withdrawEpoch;
        }
    
        uint256 constant EXCHANGE_RATE_PRECISION = 100;
        // maximum matic possible, even if rate will be 1 and all matic will be staken in one go, it will result in 10 ^ 58 shares
        uint256 constant EXCHANGE_RATE_HIGH_PRECISION = 10**29;
        uint256 constant MAX_COMMISION_RATE = 100;
        uint256 constant REWARD_PRECISION = 10**25;
    
        StakingInfo public stakingLogger;
        IStakeManager public stakeManager;
        uint256 public validatorId;
        uint256 public validatorRewards_deprecated;
        uint256 public commissionRate_deprecated;
        uint256 public lastCommissionUpdate_deprecated;
        uint256 public minAmount;
    
        uint256 public totalStake_deprecated;
        uint256 public rewardPerShare;
        uint256 public activeAmount;
    
        bool public delegation;
    
        uint256 public withdrawPool;
        uint256 public withdrawShares;
    
        mapping(address => uint256) amountStaked_deprecated; // deprecated, keep for foundation delegators
        mapping(address => DelegatorUnbond) public unbonds;
        mapping(address => uint256) public initalRewardPerShare;
    
        mapping(address => uint256) public unbondNonces;
        mapping(address => mapping(uint256 => DelegatorUnbond)) public unbonds_new;
    
        EventsHub public eventsHub;
    
        // onlyOwner will prevent this contract from initializing, since it's owner is going to be 0x0 address
        function initialize(
            uint256 _validatorId,
            address _stakingLogger,
            address _stakeManager
        ) external initializer {
            validatorId = _validatorId;
            stakingLogger = StakingInfo(_stakingLogger);
            stakeManager = IStakeManager(_stakeManager);
            _transferOwnership(_stakeManager);
            _getOrCacheEventsHub();
    
            minAmount = 10**18;
            delegation = true;
        }
    
        /**
            Public View Methods
        */
    
        function exchangeRate() public view returns (uint256) {
            uint256 totalShares = totalSupply();
            uint256 precision = _getRatePrecision();
            return totalShares == 0 ? precision : stakeManager.delegatedAmount(validatorId).mul(precision).div(totalShares);
        }
    
        function getTotalStake(address user) public view returns (uint256, uint256) {
            uint256 shares = balanceOf(user);
            uint256 rate = exchangeRate();
            if (shares == 0) {
                return (0, rate);
            }
    
            return (rate.mul(shares).div(_getRatePrecision()), rate);
        }
    
        function withdrawExchangeRate() public view returns (uint256) {
            uint256 precision = _getRatePrecision();
            if (validatorId < 8) {
                // fix of potentially broken withdrawals for future unbonding
                // foundation validators have no slashing enabled and thus we can return default exchange rate
                // because without slashing rate will stay constant
                return precision;
            }
    
            uint256 _withdrawShares = withdrawShares;
            return _withdrawShares == 0 ? precision : withdrawPool.mul(precision).div(_withdrawShares);
        }
    
        function getLiquidRewards(address user) public view returns (uint256) {
            return _calculateReward(user, getRewardPerShare());
        }
    
        function getRewardPerShare() public view returns (uint256) {
            return _calculateRewardPerShareWithRewards(stakeManager.delegatorsReward(validatorId));
        }
    
        /**
            Public Methods
         */
    
        function buyVoucher(uint256 _amount, uint256 _minSharesToMint) public returns(uint256 amountToDeposit) {
            _withdrawAndTransferReward(msg.sender);
            
            amountToDeposit = _buyShares(_amount, _minSharesToMint, msg.sender);
            require(stakeManager.delegationDeposit(validatorId, amountToDeposit, msg.sender), "deposit failed");
            
            return amountToDeposit;
        }
    
        function restake() public returns(uint256, uint256) {
            address user = msg.sender;
            uint256 liquidReward = _withdrawReward(user);
            uint256 amountRestaked;
    
            require(liquidReward >= minAmount, "Too small rewards to restake");
    
            if (liquidReward != 0) {
                amountRestaked = _buyShares(liquidReward, 0, user);
    
                if (liquidReward > amountRestaked) {
                    // return change to the user
                    require(
                        stakeManager.transferFunds(validatorId, liquidReward - amountRestaked, user),
                        "Insufficent rewards"
                    );
                    stakingLogger.logDelegatorClaimRewards(validatorId, user, liquidReward - amountRestaked);
                }
    
                (uint256 totalStaked, ) = getTotalStake(user);
                stakingLogger.logDelegatorRestaked(validatorId, user, totalStaked);
            }
            
            return (amountRestaked, liquidReward);
        }
    
        function sellVoucher(uint256 claimAmount, uint256 maximumSharesToBurn) public {
            (uint256 shares, uint256 _withdrawPoolShare) = _sellVoucher(claimAmount, maximumSharesToBurn);
    
            DelegatorUnbond memory unbond = unbonds[msg.sender];
            unbond.shares = unbond.shares.add(_withdrawPoolShare);
            // refresh undond period
            unbond.withdrawEpoch = stakeManager.epoch();
            unbonds[msg.sender] = unbond;
    
            StakingInfo logger = stakingLogger;
            logger.logShareBurned(validatorId, msg.sender, claimAmount, shares);
            logger.logStakeUpdate(validatorId);
        }
    
        function withdrawRewards() public {
            uint256 rewards = _withdrawAndTransferReward(msg.sender);
            require(rewards >= minAmount, "Too small rewards amount");
        }
    
        function migrateOut(address user, uint256 amount) external onlyOwner {
            _withdrawAndTransferReward(user);
            (uint256 totalStaked, uint256 rate) = getTotalStake(user);
            require(totalStaked >= amount, "Migrating too much");
    
            uint256 precision = _getRatePrecision();
            uint256 shares = amount.mul(precision).div(rate);
            _burn(user, shares);
    
            stakeManager.updateValidatorState(validatorId, -int256(amount));
            activeAmount = activeAmount.sub(amount);
    
            stakingLogger.logShareBurned(validatorId, user, amount, shares);
            stakingLogger.logStakeUpdate(validatorId);
            stakingLogger.logDelegatorUnstaked(validatorId, user, amount);
        }
    
        function migrateIn(address user, uint256 amount) external onlyOwner {
            _withdrawAndTransferReward(user);
            _buyShares(amount, 0, user);
        }
    
        function unstakeClaimTokens() public {
            DelegatorUnbond memory unbond = unbonds[msg.sender];
            uint256 amount = _unstakeClaimTokens(unbond);
            delete unbonds[msg.sender];
            stakingLogger.logDelegatorUnstaked(validatorId, msg.sender, amount);
        }
    
        function slash(
            uint256 validatorStake,
            uint256 delegatedAmount,
            uint256 totalAmountToSlash
        ) external onlyOwner returns (uint256) {
            uint256 _withdrawPool = withdrawPool;
            uint256 delegationAmount = delegatedAmount.add(_withdrawPool);
            if (delegationAmount == 0) {
                return 0;
            }
            // total amount to be slashed from delegation pool (active + inactive)
            uint256 _amountToSlash = delegationAmount.mul(totalAmountToSlash).div(validatorStake.add(delegationAmount));
            uint256 _amountToSlashWithdrawalPool = _withdrawPool.mul(_amountToSlash).div(delegationAmount);
    
            // slash inactive pool
            uint256 stakeSlashed = _amountToSlash.sub(_amountToSlashWithdrawalPool);
            stakeManager.decreaseValidatorDelegatedAmount(validatorId, stakeSlashed);
            activeAmount = activeAmount.sub(stakeSlashed);
    
            withdrawPool = withdrawPool.sub(_amountToSlashWithdrawalPool);
            return _amountToSlash;
        }
    
        function updateDelegation(bool _delegation) external onlyOwner {
            delegation = _delegation;
        }
    
        function drain(
            address token,
            address payable destination,
            uint256 amount
        ) external onlyOwner {
            if (token == address(0x0)) {
                destination.transfer(amount);
            } else {
                require(ERC20(token).transfer(destination, amount), "Drain failed");
            }
        }
    
        /**
            New shares exit API
         */
    
        function sellVoucher_new(uint256 claimAmount, uint256 maximumSharesToBurn) public {
            (uint256 shares, uint256 _withdrawPoolShare) = _sellVoucher(claimAmount, maximumSharesToBurn);
    
            uint256 unbondNonce = unbondNonces[msg.sender].add(1);
    
            DelegatorUnbond memory unbond = DelegatorUnbond({
                shares: _withdrawPoolShare,
                withdrawEpoch: stakeManager.epoch()
            });
            unbonds_new[msg.sender][unbondNonce] = unbond;
            unbondNonces[msg.sender] = unbondNonce;
    
            _getOrCacheEventsHub().logShareBurnedWithId(validatorId, msg.sender, claimAmount, shares, unbondNonce);
            stakingLogger.logStakeUpdate(validatorId);
        }
    
        function unstakeClaimTokens_new(uint256 unbondNonce) public {
            DelegatorUnbond memory unbond = unbonds_new[msg.sender][unbondNonce];
            uint256 amount = _unstakeClaimTokens(unbond);
            delete unbonds_new[msg.sender][unbondNonce];
            _getOrCacheEventsHub().logDelegatorUnstakedWithId(validatorId, msg.sender, amount, unbondNonce);
        }
    
        /**
            Private Methods
         */
    
        function _getOrCacheEventsHub() private returns(EventsHub) {
            EventsHub _eventsHub = eventsHub;
            if (_eventsHub == EventsHub(0x0)) {
                _eventsHub = EventsHub(Registry(stakeManager.getRegistry()).contractMap(keccak256("eventsHub")));
                eventsHub = _eventsHub;
            }
            return _eventsHub;
        }
    
        function _sellVoucher(uint256 claimAmount, uint256 maximumSharesToBurn) private returns(uint256, uint256) {
            // first get how much staked in total and compare to target unstake amount
            (uint256 totalStaked, uint256 rate) = getTotalStake(msg.sender);
            require(totalStaked != 0 && totalStaked >= claimAmount, "Too much requested");
    
            // convert requested amount back to shares
            uint256 precision = _getRatePrecision();
            uint256 shares = claimAmount.mul(precision).div(rate);
            require(shares <= maximumSharesToBurn, "too much slippage");
    
            _withdrawAndTransferReward(msg.sender);
    
            _burn(msg.sender, shares);
            stakeManager.updateValidatorState(validatorId, -int256(claimAmount));
            activeAmount = activeAmount.sub(claimAmount);
    
            uint256 _withdrawPoolShare = claimAmount.mul(precision).div(withdrawExchangeRate());
            withdrawPool = withdrawPool.add(claimAmount);
            withdrawShares = withdrawShares.add(_withdrawPoolShare);
    
            return (shares, _withdrawPoolShare);
        }
    
        function _unstakeClaimTokens(DelegatorUnbond memory unbond) private returns(uint256) {
            uint256 shares = unbond.shares;
            require(
                unbond.withdrawEpoch.add(stakeManager.withdrawalDelay()) <= stakeManager.epoch() && shares > 0,
                "Incomplete withdrawal period"
            );
    
            uint256 _amount = withdrawExchangeRate().mul(shares).div(_getRatePrecision());
            withdrawShares = withdrawShares.sub(shares);
            withdrawPool = withdrawPool.sub(_amount);
    
            require(stakeManager.transferFunds(validatorId, _amount, msg.sender), "Insufficent rewards");
    
            return _amount;
        }
    
        function _getRatePrecision() private view returns (uint256) {
            // if foundation validator, use old precision
            if (validatorId < 8) {
                return EXCHANGE_RATE_PRECISION;
            }
    
            return EXCHANGE_RATE_HIGH_PRECISION;
        }
    
        function _calculateRewardPerShareWithRewards(uint256 accumulatedReward) private view returns (uint256) {
            uint256 _rewardPerShare = rewardPerShare;
            if (accumulatedReward != 0) {
                uint256 totalShares = totalSupply();
                
                if (totalShares != 0) {
                    _rewardPerShare = _rewardPerShare.add(accumulatedReward.mul(REWARD_PRECISION).div(totalShares));
                }
            }
    
            return _rewardPerShare;
        }
    
        function _calculateReward(address user, uint256 _rewardPerShare) private view returns (uint256) {
            uint256 shares = balanceOf(user);
            if (shares == 0) {
                return 0;
            }
    
            uint256 _initialRewardPerShare = initalRewardPerShare[user];
    
            if (_initialRewardPerShare == _rewardPerShare) {
                return 0;
            }
    
            return _rewardPerShare.sub(_initialRewardPerShare).mul(shares).div(REWARD_PRECISION);
        }
    
        function _withdrawReward(address user) private returns (uint256) {
            uint256 _rewardPerShare = _calculateRewardPerShareWithRewards(
                stakeManager.withdrawDelegatorsReward(validatorId)
            );
            uint256 liquidRewards = _calculateReward(user, _rewardPerShare);
            
            rewardPerShare = _rewardPerShare;
            initalRewardPerShare[user] = _rewardPerShare;
            return liquidRewards;
        }
    
        function _withdrawAndTransferReward(address user) private returns (uint256) {
            uint256 liquidRewards = _withdrawReward(user);
            if (liquidRewards != 0) {
                require(stakeManager.transferFunds(validatorId, liquidRewards, user), "Insufficent rewards");
                stakingLogger.logDelegatorClaimRewards(validatorId, user, liquidRewards);
            }
            return liquidRewards;
        }
    
        function _buyShares(
            uint256 _amount,
            uint256 _minSharesToMint,
            address user
        ) private onlyWhenUnlocked returns (uint256) {
            require(delegation, "Delegation is disabled");
    
            uint256 rate = exchangeRate();
            uint256 precision = _getRatePrecision();
            uint256 shares = _amount.mul(precision).div(rate);
            require(shares >= _minSharesToMint, "Too much slippage");
            require(unbonds[user].shares == 0, "Ongoing exit");
    
            _mint(user, shares);
    
            // clamp amount of tokens in case resulted shares requires less tokens than anticipated
            _amount = rate.mul(shares).div(precision);
    
            stakeManager.updateValidatorState(validatorId, int256(_amount));
            activeAmount = activeAmount.add(_amount);
    
            StakingInfo logger = stakingLogger;
            logger.logShareMinted(validatorId, user, _amount, shares);
            logger.logStakeUpdate(validatorId);
    
            return _amount;
        }
    
        function _transfer(
            address from,
            address to,
            uint256 value
        ) internal {
            // get rewards for recipient 
            _withdrawAndTransferReward(to);
            // convert rewards to shares
            _withdrawAndTransferReward(from);
            // move shares to recipient
            super._transfer(from, to, value);
        }
    }