ETH Price: $2,686.89 (+3.53%)

Transaction Decoder

Block:
13544192 at Nov-03-2021 01:45:54 PM +UTC
Transaction Fee:
0.006627899143459244 ETH $17.81
Gas Used:
42,644 Gas / 155.423955151 Gwei

Account State Difference:

  Address   Before After State Difference Code
0x13172333...c67cE386B
0.056825104672113901 Eth
Nonce: 32
0.050197205528654657 Eth
Nonce: 33
0.006627899143459244
(Ethermine)
3,202.724852623921653031 Eth3,202.725014773467253031 Eth0.0001621495456

Execution Trace

ValidatorShareProxy.6ab15071( )
  • Registry.STATICCALL( )
  • ValidatorShare.buyVoucher( _amount=174582738740000000000, _minSharesToMint=0 )
    • StakeManagerProxy.7ed4b27c( )
      File 1 of 4: 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 4: 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 4: 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);
          }
      }

      File 4 of 4: StakeManagerProxy
      // 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/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/staking/stakeManager/StakeManagerProxy.sol
      
      pragma solidity ^0.5.2;
      
      
      contract StakeManagerProxy is UpgradableProxy {
          constructor(address _proxyTo) public UpgradableProxy(_proxyTo) {}
      }