text: const Web3 = require('web3');
  const { program } = require('commander');
  const web3 = new Web3();
  const BN = web3.utils.BN;
  
  // web3.eth.subscribe('newBlockHeaders', function(error, result){
  // }).on("data", blockHeader => onNewBlock(blockHeader));
  // function onNewBlock(block) {
  //   let nowTime = new Date();
  //   let tsTime = new Date(block.step * 5 * 1000);
  //   console.log(`New Block: ${block.number}`);
  //   console.log(`Author: ${block.miner}`);
  //   console.log(`Time: ${nowTime.getMinutes()}:${nowTime.getSeconds()}:${nowTime.getMilliseconds()}`);
  //   console.log(`Timestamp: ${tsTime.getMinutes()}:${tsTime.getSeconds()}:${tsTime.getMilliseconds()}`);
  //   console.log();
  // }
  
  const BlockReward = new web3.eth.Contract([{"constant":true,"inputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"name":"snapshotPoolTotalStakeAmount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_tokenMinterContract","type":"address"}],"name":"setTokenMinterContract","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_stakingEpoch","type":"uint256"},{"name":"_poolId","type":"uint256"}],"name":"getValidatorReward","outputs":[{"name":"tokenReward","type":"uint256"},{"name":"nativeReward","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"addBridgeTokenFeeReceivers","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_bridgesAllowed","type":"address[]"}],"name":"setErcToNativeBridgesAllowed","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"name":"epochPoolNativeReward","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_rewardToDistribute","type":"uint256"},{"name":"_blocksCreatedShareNum","type":"uint256[]"},{"name":"_blocksCreatedShareDenom","type":"uint256"},{"name":"_stakingEpoch","type":"uint256"}],"name":"currentPoolRewards","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"addBridgeNativeRewardReceivers","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"blockRewardContractId","outputs":[{"name":"","type":"bytes4"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"name":"_stakingContract","type":"address"},{"name":"_stakingEpoch","type":"uint256"},{"name":"_totalRewardShareNum","type":"uint256"},{"name":"_totalRewardShareDenom","type":"uint256"},{"name":"_validators","type":"uint256[]"}],"name":"currentNativeRewardToDistribute","outputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"uint256"}],"name":"mintedForAccountInBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isInitialized","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_poolId","type":"uint256"}],"name":"validatorRewardPercent","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"mintedForAccount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"ercToNativeBridgesAllowed","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_stakingContract","type":"address"},{"name":"_stakingEpoch","type":"uint256"},{"name":"_totalRewardShareNum","type":"uint256"},{"name":"_totalRewardShareDenom","type":"uint256"},{"name":"_validators","type":"uint256[]"}],"name":"currentTokenRewardToDistribute","outputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"mintedInBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_validatorSet","type":"address"},{"name":"_prevBlockReward","type":"address"}],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"STAKE_TOKEN_INFLATION_RATE","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_poolStakingAddress","type":"address"},{"name":"_staker","type":"address"}],"name":"epochsToClaimRewardFrom","outputs":[{"name":"epochsToClaimFrom","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"addBridgeNativeFeeReceivers","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"mintedTotally","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_stakingEpoch","type":"uint256"},{"name":"_delegatorStaked","type":"uint256"},{"name":"_validatorStaked","type":"uint256"},{"name":"_totalStaked","type":"uint256"},{"name":"_poolReward","type":"uint256"}],"name":"delegatorShare","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"addBridgeTokenRewardReceivers","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_bridgesAllowed","type":"address[]"}],"name":"setNativeToErcBridgesAllowed","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"tokenRewardUndistributed","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_tokens","type":"uint256"},{"name":"_nativeCoins","type":"uint256"},{"name":"_to","type":"address"}],"name":"transferReward","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"name":"snapshotPoolValidatorStakeAmount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_stakingEpoch","type":"uint256"},{"name":"_validatorStaked","type":"uint256"},{"name":"_totalStaked","type":"uint256"},{"name":"_poolReward","type":"uint256"}],"name":"validatorShare","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_delegatorStake","type":"uint256"},{"name":"_stakingEpoch","type":"uint256"},{"name":"_poolId","type":"uint256"}],"name":"getDelegatorReward","outputs":[{"name":"tokenReward","type":"uint256"},{"name":"nativeReward","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_bridgesAllowed","type":"address[]"}],"name":"setErcToErcBridgesAllowed","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"ercToErcBridgesAllowed","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"uint256"},{"name":"","type":"bytes"}],"name":"onTokenTransfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"extraReceiversQueueSize","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"},{"name":"_receiver","type":"address"}],"name":"addExtraReceiver","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"bridgeNativeReward","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"nativeRewardUndistributed","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"nativeToErcBridgesAllowed","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"mintedTotallyByBridge","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"clearBlocksCreated","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"tokenMinterContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"validatorMinRewardPercent","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"validatorSetContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"name":"epochPoolTokenReward","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_poolId","type":"uint256"}],"name":"epochsPoolGotRewardFor","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"name":"blocksCreated","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"benefactors","type":"address[]"},{"name":"kind","type":"uint16[]"}],"name":"reward","outputs":[{"name":"receiversNative","type":"address[]"},{"name":"rewardsNative","type":"uint256[]"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"bridgeTokenReward","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"cumulativeAmount","type":"uint256"},{"indexed":true,"name":"bridge","type":"address"}],"name":"BridgeTokenRewardAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"amount","type":"uint256"},{"indexed":true,"name":"receiver","type":"address"},{"indexed":true,"name":"bridge","type":"address"}],"name":"AddedReceiver","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"cumulativeAmount","type":"uint256"},{"indexed":true,"name":"bridge","type":"address"}],"name":"BridgeNativeRewardAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"receivers","type":"address[]"},{"indexed":false,"name":"rewards","type":"uint256[]"}],"name":"MintedNative","type":"event"}], '0x481c034c6d9441db23Ea48De68BCAe812C5d39bA');
  const Random = new web3.eth.Contract([{"constant":true,"inputs":[],"name":"collectRoundLength","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"punishForUnreveal","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"currentSeed","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"validatorSetContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_numberHash","type":"bytes32"},{"name":"_cipher","type":"bytes"}],"name":"commitHash","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_number","type":"uint256"}],"name":"revealNumber","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_number","type":"uint256"}],"name":"revealSecret","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_punishForUnreveal","type":"bool"}],"name":"setPunishForUnreveal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_collectRoundLength","type":"uint256"},{"name":"_validatorSet","type":"address"},{"name":"_punishForUnreveal","type":"bool"}],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"onFinishCollectRound","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"commitPhaseLength","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"currentCollectRound","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"currentCollectRoundStartBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_collectRound","type":"uint256"},{"name":"_miningAddress","type":"address"}],"name":"getCipher","outputs":[{"name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_collectRound","type":"uint256"},{"name":"_miningAddress","type":"address"}],"name":"getCommit","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_collectRound","type":"uint256"},{"name":"_miningAddress","type":"address"}],"name":"getCommitAndCipher","outputs":[{"name":"","type":"bytes32"},{"name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_collectRound","type":"uint256"},{"name":"_miningAddress","type":"address"}],"name":"isCommitted","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isCommitPhase","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isInitialized","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isRevealPhase","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_miningAddress","type":"address"},{"name":"_numberHash","type":"bytes32"}],"name":"commitHashCallable","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"nextCollectRoundStartBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"nextCommitPhaseStartBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"nextRevealPhaseStartBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_miningAddress","type":"address"},{"name":"_number","type":"uint256"}],"name":"revealNumberCallable","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_miningAddress","type":"address"},{"name":"_number","type":"uint256"}],"name":"revealSecretCallable","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_stakingEpoch","type":"uint256"},{"name":"_miningAddress","type":"address"}],"name":"revealSkips","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_collectRound","type":"uint256"},{"name":"_miningAddress","type":"address"}],"name":"sentReveal","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}], '0x5870b0527DeDB1cFBD9534343Feda1a41Ce47766');
  const Staking = new web3.eth.Contract([{"constant":true,"inputs":[{"name":"","type":"uint256"},{"name":"","type":"address"}],"name":"poolDelegatorIndex","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_totalAmount","type":"uint256"}],"name":"initialValidatorStake","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_poolId","type":"uint256"},{"name":"_delegatorOrZero","type":"address"}],"name":"stakeAmountByCurrentEpoch","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"},{"name":"","type":"address"}],"name":"poolDelegatorInactiveIndex","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"removePools","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"},{"name":"","type":"address"},{"name":"","type":"uint256"}],"name":"rewardWasTaken","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_blockNumber","type":"uint256"}],"name":"setStakingEpochStartBlock","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_fromPoolStakingAddress","type":"address"},{"name":"_toPoolStakingAddress","type":"address"},{"name":"_amount","type":"uint256"}],"name":"moveStake","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"},{"name":"","type":"address"}],"name":"stakeFirstEpoch","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"stakeAmountTotal","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_minStake","type":"uint256"}],"name":"setDelegatorMinStake","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"erc677TokenContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_delegator","type":"address"},{"name":"_offset","type":"uint256"},{"name":"_length","type":"uint256"}],"name":"getDelegatorPools","outputs":[{"name":"result","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_unremovablePoolId","type":"uint256"}],"name":"clearUnremovableValidator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"},{"name":"_miningAddress","type":"address"}],"name":"addPool","outputs":[{"name":"","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"},{"name":"","type":"address"}],"name":"stakeLastEpoch","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isInitialized","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_stakingEpochs","type":"uint256[]"},{"name":"_poolStakingAddress","type":"address"}],"name":"claimReward","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"},{"name":"","type":"address"}],"name":"stakeAmount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"orderedWithdrawAmountTotal","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_minStake","type":"uint256"}],"name":"setCandidateMinStake","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_poolId","type":"uint256"}],"name":"poolDelegators","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_erc677TokenContract","type":"address"}],"name":"setErc677TokenContract","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"candidateMinStake","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getPools","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_poolStakingAddress","type":"address"},{"name":"_staker","type":"address"}],"name":"maxWithdrawAllowed","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"},{"name":"","type":"address"},{"name":"","type":"uint256"}],"name":"delegatorStakeSnapshot","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"poolInactiveIndex","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"stakingEpochStartBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"lastChangeBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"stakingEpoch","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_delegator","type":"address"}],"name":"getDelegatorPoolsLength","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"stakingEpochEndBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_poolStakingAddress","type":"address"},{"name":"_staker","type":"address"}],"name":"maxWithdrawOrderAllowed","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getPoolsLikelihood","outputs":[{"name":"likelihoods","type":"uint256[]"},{"name":"sum","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"stakeWithdrawDisallowPeriod","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"poolToBeElectedIndex","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_poolId","type":"uint256"}],"name":"poolDelegatorsInactive","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_poolId","type":"uint256"}],"name":"removePool","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_staker","type":"address"},{"name":"_amount","type":"uint256"},{"name":"_data","type":"bytes"}],"name":"onTokenTransfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getPoolsToBeElected","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_validatorSetContract","type":"address"},{"name":"_initialIds","type":"uint256[]"},{"name":"_delegatorMinStake","type":"uint256"},{"name":"_candidateMinStake","type":"uint256"},{"name":"_stakingEpochDuration","type":"uint256"},{"name":"_stakingEpochStartBlock","type":"uint256"},{"name":"_stakeWithdrawDisallowPeriod","type":"uint256"}],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_toPoolStakingAddress","type":"address"},{"name":"_amount","type":"uint256"}],"name":"stake","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"_poolStakingAddress","type":"address"},{"name":"_amount","type":"int256"}],"name":"orderWithdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_poolId","type":"uint256"}],"name":"isPoolActive","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"poolToBeRemovedIndex","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"poolIndex","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_poolStakingAddress","type":"address"}],"name":"claimOrderedWithdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getPoolsToBeRemoved","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"},{"name":"","type":"address"}],"name":"orderWithdrawEpoch","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"stakingEpochDuration","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"delegatorMinStake","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getPoolsInactive","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"validatorSetContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"},{"name":"","type":"address"}],"name":"orderedWithdrawAmount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"removeMyPool","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"incrementStakingEpoch","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"MAX_CANDIDATES","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_fromPoolStakingAddress","type":"address"},{"name":"_amount","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"areStakeAndWithdrawAllowed","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_stakingEpochs","type":"uint256[]"},{"name":"_poolStakingAddress","type":"address"},{"name":"_staker","type":"address"}],"name":"getRewardAmount","outputs":[{"name":"tokenRewardSum","type":"uint256"},{"name":"nativeRewardSum","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"fromPoolStakingAddress","type":"address"},{"indexed":true,"name":"staker","type":"address"},{"indexed":true,"name":"stakingEpoch","type":"uint256"},{"indexed":false,"name":"tokensAmount","type":"uint256"},{"indexed":false,"name":"nativeCoinsAmount","type":"uint256"},{"indexed":false,"name":"fromPoolId","type":"uint256"}],"name":"ClaimedReward","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"poolStakingAddress","type":"address"},{"indexed":true,"name":"poolMiningAddress","type":"address"},{"indexed":false,"name":"poolId","type":"uint256"}],"name":"AddedPool","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"fromPoolStakingAddress","type":"address"},{"indexed":true,"name":"staker","type":"address"},{"indexed":true,"name":"stakingEpoch","type":"uint256"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"fromPoolId","type":"uint256"}],"name":"ClaimedOrderedWithdrawal","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"fromPoolStakingAddress","type":"address"},{"indexed":true,"name":"toPoolStakingAddress","type":"address"},{"indexed":true,"name":"staker","type":"address"},{"indexed":true,"name":"stakingEpoch","type":"uint256"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"fromPoolId","type":"uint256"},{"indexed":false,"name":"toPoolId","type":"uint256"}],"name":"MovedStake","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"fromPoolStakingAddress","type":"address"},{"indexed":true,"name":"staker","type":"address"},{"indexed":true,"name":"stakingEpoch","type":"uint256"},{"indexed":false,"name":"amount","type":"int256"},{"indexed":false,"name":"fromPoolId","type":"uint256"}],"name":"OrderedWithdrawal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"toPoolStakingAddress","type":"address"},{"indexed":true,"name":"staker","type":"address"},{"indexed":true,"name":"stakingEpoch","type":"uint256"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"toPoolId","type":"uint256"}],"name":"PlacedStake","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"fromPoolStakingAddress","type":"address"},{"indexed":true,"name":"staker","type":"address"},{"indexed":true,"name":"stakingEpoch","type":"uint256"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"fromPoolId","type":"uint256"}],"name":"WithdrewStake","type":"event"}], '0x2DdB8A7541e6cAA50F74e7FACFF9Fe9da00e0A6c');
  const ValidatorSet = new web3.eth.Contract([{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"miningByStakingAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"stakingAddressById","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"stakingByMiningAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"idByMiningAddress","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"poolDescription","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"blockRewardContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"hasEverBeenStakingAddress","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"unremovableValidator","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"MAX_VALIDATORS","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"lastChangeBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"hasEverBeenMiningAddress","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"miningAddressChangeRequest","outputs":[{"name":"poolId","type":"uint256"},{"name":"newMiningAddress","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"isValidatorById","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"idByStakingAddress","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"lastPoolId","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"validatorSetApplyBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"poolName","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"randomContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"changeRequestCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"miningAddressById","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"stakingContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"poolId","type":"uint256"},{"indexed":true,"name":"oldMiningAddress","type":"address"},{"indexed":true,"name":"newMiningAddress","type":"address"}],"name":"ChangedMiningAddress","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"poolId","type":"uint256"},{"indexed":true,"name":"oldStakingAddress","type":"address"},{"indexed":true,"name":"newStakingAddress","type":"address"}],"name":"ChangedStakingAddress","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"parentHash","type":"bytes32"},{"indexed":false,"name":"newSet","type":"address[]"}],"name":"InitiateChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"reportingValidator","type":"address"},{"indexed":false,"name":"maliciousValidator","type":"address"},{"indexed":false,"name":"blockNumber","type":"uint256"},{"indexed":false,"name":"reportingPoolId","type":"uint256"},{"indexed":false,"name":"maliciousPoolId","type":"uint256"}],"name":"ReportedMalicious","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"poolId","type":"uint256"},{"indexed":false,"name":"name","type":"string"},{"indexed":false,"name":"description","type":"string"}],"name":"SetPoolMetadata","type":"event"},{"constant":false,"inputs":[],"name":"clearUnremovableValidator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"string"},{"name":"_description","type":"string"}],"name":"changeMetadata","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_newMiningAddress","type":"address"}],"name":"changeMiningAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_newStakingAddress","type":"address"}],"name":"changeStakingAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"emitInitiateChange","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"finalizeChange","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_blockRewardContract","type":"address"},{"name":"_randomContract","type":"address"},{"name":"_stakingContract","type":"address"},{"name":"_initialMiningAddresses","type":"address[]"},{"name":"_initialStakingAddresses","type":"address[]"},{"name":"_firstValidatorIsUnremovable","type":"bool"}],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"newValidatorSet","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_miningAddresses","type":"address[]"}],"name":"removeMaliciousValidators","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_maliciousMiningAddress","type":"address"},{"name":"_blockNumber","type":"uint256"},{"name":"","type":"bytes"}],"name":"reportMalicious","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_miningAddress","type":"address"},{"name":"_stakingAddress","type":"address"},{"name":"_name","type":"string"},{"name":"_description","type":"string"}],"name":"addPool","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_miningAddress","type":"address"}],"name":"areDelegatorsBanned","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_poolId","type":"uint256"}],"name":"areIdDelegatorsBanned","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_miningAddress","type":"address"}],"name":"banCounter","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_miningAddress","type":"address"}],"name":"bannedUntil","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_miningAddress","type":"address"}],"name":"bannedDelegatorsUntil","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_miningAddress","type":"address"}],"name":"banReason","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"emitInitiateChangeCallable","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getPendingValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getPendingValidatorsIds","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getValidatorsIds","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"initiateChangeAllowed","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isInitialized","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_miningAddress","type":"address"}],"name":"isValidator","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_miningAddress","type":"address"},{"name":"_reportingValidator","type":"bool"}],"name":"isReportValidatorValid","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_miningAddress","type":"address"}],"name":"isValidatorBanned","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_poolId","type":"uint256"}],"name":"isValidatorIdBanned","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_poolId","type":"uint256"}],"name":"isValidatorOrPending","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_reportingMiningAddress","type":"address"},{"name":"_maliciousMiningAddress","type":"address"},{"name":"_blockNumber","type":"uint256"}],"name":"reportMaliciousCallable","outputs":[{"name":"callable","type":"bool"},{"name":"removeReportingValidator","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_reportingMiningAddress","type":"address"},{"name":"_maliciousMiningAddress","type":"address"},{"name":"_blockNumber","type":"uint256"}],"name":"shouldValidatorReport","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_miningAddress","type":"address"}],"name":"validatorCounter","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"validatorsToBeFinalized","outputs":[{"name":"miningAddresses","type":"address[]"},{"name":"forNewEpoch","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"validatorsToBeFinalizedIds","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"}], '0xB87BE9f7196F2AE084Ca1DE6af5264292976e013');
  const Token = new web3.eth.Contract([{"constant":false,"inputs":[{"name":"_bridge","type":"address"}],"name":"removeBridge","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x04df017d"},{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x06fdde03"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x095ea7b3"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x18160ddd"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x313ce567"},{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x39509351"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"},{"name":"_data","type":"bytes"}],"name":"transferAndCall","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x4000aea0"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"mint","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x40c10f19"},{"constant":false,"inputs":[{"name":"_value","type":"uint256"}],"name":"burn","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x42966c68"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"bridgePointers","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x4bcb88bc"},{"constant":true,"inputs":[],"name":"blockRewardContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x56b54bae"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_subtractedValue","type":"uint256"}],"name":"decreaseApproval","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x66188463"},{"constant":false,"inputs":[{"name":"_token","type":"address"},{"name":"_to","type":"address"}],"name":"claimTokens","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x69ffa08a"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x70a08231"},{"constant":true,"inputs":[{"name":"_address","type":"address"}],"name":"isBridge","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x726600ce"},{"constant":true,"inputs":[],"name":"getTokenInterfacesVersion","outputs":[{"name":"major","type":"uint64"},{"name":"minor","type":"uint64"},{"name":"patch","type":"uint64"}],"payable":false,"stateMutability":"pure","type":"function","signature":"0x859ba28c"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x8da5cb5b"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x95d89b41"},{"constant":false,"inputs":[{"name":"_bridge","type":"address"}],"name":"addBridge","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x9712fdf8"},{"constant":true,"inputs":[],"name":"bridgeList","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function","signature":"0x9da38e2f"},{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xa457c2d7"},{"constant":true,"inputs":[],"name":"F_ADDR","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xc794c769"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_addedValue","type":"uint256"}],"name":"increaseApproval","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xd73dd623"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xdd62ed3e"},{"constant":true,"inputs":[],"name":"stakingContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xee99205c"},{"constant":false,"inputs":[{"name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xf2fde38b"},{"constant":true,"inputs":[],"name":"bridgeCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function","signature":"0xfbb2a53f"},{"inputs":[{"name":"_name","type":"string"},{"name":"_symbol","type":"string"},{"name":"_decimals","type":"uint8"}],"payable":false,"stateMutability":"nonpayable","type":"constructor","signature":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"bridge","type":"address"}],"name":"BridgeAdded","type":"event","signature":"0x3cda433c5679ae4c6a5dea50840e222a42cba3695e4663de4366be8993484221"},{"anonymous":false,"inputs":[{"indexed":true,"name":"bridge","type":"address"}],"name":"BridgeRemoved","type":"event","signature":"0x5d9d5034656cb3ebfb0655057cd7f9b4077a9b42ff42ce223cbac5bc586d2126"},{"anonymous":false,"inputs":[{"indexed":false,"name":"from","type":"address"},{"indexed":false,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"ContractFallbackCallFailed","type":"event","signature":"0x11249f0fc79fc134a15a10d1da8291b79515bf987e036ced05b9ec119614070b"},{"anonymous":false,"inputs":[{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Mint","type":"event","signature":"0x0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d4121396885"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event","signature":"0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0"},{"anonymous":false,"inputs":[{"indexed":true,"name":"burner","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Burn","type":"event","signature":"0xcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca5"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"},{"indexed":false,"name":"data","type":"bytes"}],"name":"Transfer","type":"event","signature":"0xe19260aff97b920c7df27010903aeb9c8d2be5d310a2c67824cf3f15396e4c16"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event","signature":"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event","signature":"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"},{"constant":false,"inputs":[{"name":"_blockRewardContract","type":"address"}],"name":"setBlockRewardContract","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x27a3e16b"},{"constant":false,"inputs":[{"name":"_stakingContract","type":"address"}],"name":"setStakingContract","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x9dd373b9"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"mintReward","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x91c0aabf"},{"constant":false,"inputs":[{"name":"_staker","type":"address"},{"name":"_amount","type":"uint256"}],"name":"stake","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xadc9772e"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0xa9059cbb"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function","signature":"0x23b872dd"}], '0xb7D311E2Eb55F2f68a9440da38e7989210b9A05e');
  
  const knownNames = {
    // miningAddress (or stakingAddress) -> name
    '0x9233042B8E9E03D5DC6454BBBe5aee83818fF103': 'POA Network 1',
    '0x9488f50c33e9616EE3B5B09CD3A9c603A108db4a': 'POA Network 2',
    '0x06d563905b085A6B3070C819BDB96a44E5665005': 'POA Network 4',
  };
  
  const validators = [];
  
  main();
  
  // function findMapLocation(slot, key1, key2) {
  //   const slot2 = web3.utils.keccak256(web3.eth.abi.encodeParameters(['uint256','uint256'], [key1, slot]));
  //   return web3.utils.keccak256(web3.eth.abi.encodeParameters(['uint256','uint256'], [key2, slot2]));
  // }
  
  function findMapLocation(slot, key) {
    return web3.utils.keccak256(web3.eth.abi.encodeParameters(['uint256','uint256'], [key, slot]));
  }
  
  async function main() {
    // web3.setProvider('https://rpc.xdaichain.com');
    // console.log(await web3.eth.getStorageAt('0x42b20D645a372c2150962E6D4D3bB5d4F3231143', web3.utils.toBN(web3.utils.keccak256(web3.eth.abi.encodeParameters(['uint256'], ['0']))).add(new BN(0))));
    // return;
  
    // web3.setProvider('https://rpc.xdaichain.com');
    // console.log(await ValidatorSet.methods.upgradeToAndCall('0xBD93Dd777692DD2B622dc2ed155CB3B654fd75dA', '0x0e9296ca').estimateGas({from: '0x20dcD414A3D6E57e92373871715FB9343c43B1a3'}));
    // return;
  
    // web3.setProvider('https://rpc.xdaichain.com');
    // const events = await ValidatorSet.getPastEvents('ReportedMalicious', {fromBlock: 15000000, toBlock: 15530350});
    // console.log(`events.length = ${events.length}`);
    // for (let i = 0; i < events.length; i++) {
    //   console.log('ReportedMalicious:');
    //   console.log(`  reportingValidator = ${events[i].returnValues.reportingValidator}`);
    //   console.log(`  maliciousValidator = ${events[i].returnValues.maliciousValidator}`);
    //   console.log(`  blockNumber = ${events[i].returnValues.blockNumber}`);
    //   console.log('');
    // }
    // return;
  
    program.name("npm run watch").usage("-- <options>");
    program.option('-r, --rpc <rpc>', 'RPC Node URL', 'https://rpc.xdaichain.com');
    program.option('-g, --general', 'Shows general info (ignored for --epoch-delegators and --apy).');
    program.option('-e, --epoch-delegators <epoch>', 'Shows active delegator list (excluding pending) for the given staking epoch,\nsorted by stake amount. Requires using RPC from an archive node.');
    program.option('-u, --show-unique', 'Shows unique delegator list for the current staking epoch\n(used with --general, ignored for --epoch-delegators and --apy).');
    program.option('-a, --apy', 'Shows approximate Annual Percentage Yield for the current moment. Requires using RPC from an archive node.');
    program.parse(process.argv);
  
    if (!program.general && !program.epochDelegators && !program.apy) {
      program.help();
    }
  
    web3.setProvider(program.rpc);
  
    // let prodStat = {};
    // const startBlock = 11687654;
    //
    // for (let i = startBlock-2184; i <= startBlock; i++) {
    //   console.log(i);
    //   const block = await web3.eth.getBlock(i);
    //   const miner = block.miner.toLowerCase();
    //   if (!prodStat.hasOwnProperty(miner)) {
    //     prodStat[miner] = 0;
    //   }
    //   prodStat[miner]++;
    //
    //   console.log();
    //   for (let j = 0; j < validators.length; j++) {
    //     const miner = validators[j].miningAddress.toLowerCase();
    //     const produced = prodStat[miner];
    //
    //     console.log(`${miner} : ${produced}`);
    //   }
    //   console.log();
    // }
    //
    // return;
  
    const currentBlock = await web3.eth.getBlock('latest');
    let averageBlockTime;
    if (!program.epochDelegators) {
      const oldBlock = await web3.eth.getBlock(currentBlock.number - 50);
      console.log(`CURRENT BLOCK ${currentBlock.number}`);
      console.log();
  
      // Calculate average block time
      averageBlockTime = (currentBlock.timestamp - oldBlock.timestamp) / (currentBlock.number - oldBlock.number);
      console.log(`averageBlockTime = ${averageBlockTime} sec`);
      console.log();
    }
  
    const methods = [
      [Staking.methods.getPools, []],
      [Staking.methods.getPoolsToBeElected, []],
      [Staking.methods.getPoolsToBeRemoved, []],
      [Staking.methods.stakingEpoch, []],
      [Staking.methods.stakingEpochStartBlock, []],
      [Staking.methods.stakingEpochEndBlock, []],
      [Staking.methods.stakingEpochDuration, []],
      [ValidatorSet.methods.validatorSetApplyBlock, []],
      [ValidatorSet.methods.getValidators, []],
      [ValidatorSet.methods.getPendingValidators, []],
      [ValidatorSet.methods.unremovableValidator, []],
      [Token.methods.totalSupply, []],
      [Token.methods.balanceOf, [Staking.options.address]],
      [Token.methods.balanceOf, [BlockReward.options.address]],
      [web3.eth.getBalance, [BlockReward.options.address, currentBlock.number]],
    ];
  
    let promises = [];
    let batch = new web3.BatchRequest();
    methods.forEach(item => {
      promises.push(new Promise((resolve, reject) => {
        const method = item[0];
        const arguments = item[1];
        let request;
        function requestCallback(err, result) {
          if (err) reject(err);
          else resolve(result);
        }
        if (method == web3.eth.getBalance) {
          request = method.request(...arguments, requestCallback);
        } else {
          request = method(...arguments).call.request({}, currentBlock.number, requestCallback);
        }
        batch.add(request);
      }));
    });
    await batch.execute();
    const [
      getPools,
      getPoolsToBeElected,
      getPoolsToBeRemoved,
      stakingEpoch,
      stakingEpochStartBlock,
      stakingEpochEndBlock,
      stakingEpochDuration,
      validatorSetApplyBlock,
      getValidators,
      getPendingValidators,
      unremovableValidator,
      totalSupply,
      stakingBalance,
      blockRewardTokenBalance,
      blockRewardNativeBalance
    ] = await Promise.all(promises);
  
    await getCurrentValidators(getValidators, currentBlock.number);
  
    if (program.epochDelegators) {
      console.log(`Current staking epoch: ${stakingEpoch}`);
      console.log(`Requested staking epoch: ${program.epochDelegators}`);
      console.log();
      await showActiveDelegatorsByStakingEpoch(
        program.epochDelegators,
        stakingEpochDuration,
        stakingEpoch,
        stakingEpochStartBlock,
        currentBlock.number
      );
      process.exit(0);
    } else if (program.apy) {
    	await showAPY(
        stakingEpoch,
        stakingEpochStartBlock,
        stakingEpochDuration,
        currentBlock.number,
        averageBlockTime,
        validatorSetApplyBlock
      );
    	process.exit(0);
    }
  
    // Calculate approximate stakingEpochEndTime
    const stakingEpochEndTime = new Date();
    stakingEpochEndTime.setTime((Math.floor(Date.now() / 1000) + Math.ceil((stakingEpochEndBlock - currentBlock.number) * averageBlockTime)) * 1000);
  
    console.log('Blocks creation and random reveal skipping statistics for the current staking epoch');
    const { blocksCreated, revealSkips } = await getBlocksCreatedAndRevealSkips(stakingEpoch, currentBlock.number);
    const maxValidatorNameLength = validators.reduce((acc, val) => Math.max(val.name.length, acc), 0);
    const dashes = '-'.repeat(102 + maxValidatorNameLength);
    let blocksCreatedTotal = 0, revealSkipsTotal = 0;
    console.log(dashes);
    console.log(`| ${'Name'.padEnd(maxValidatorNameLength, ' ')} | Staking address | Mining address                             | Blocks created | Reveals skipped | `);
    console.log(dashes);
    let validatorIndexes = [...Array(validators.length).keys()];
    validatorIndexes.sort((a, b) => blocksCreated[b] - blocksCreated[a]);
    validatorIndexes.forEach(i => {
      console.log(`| ${validators[i].name.padEnd(maxValidatorNameLength, ' ')} | ${validators[i].stakingAddressShort.toLowerCase().padEnd(15, ' ')} | ${validators[i].miningAddress} | ${blocksCreated[i].padStart(14, ' ')} | ${revealSkips[i].padStart(15, ' ')} | `);
      blocksCreatedTotal += blocksCreated[i] - 0;
      revealSkipsTotal += revealSkips[i] - 0;
    });
    console.log(dashes);
    console.log(`| ${'Total'.padEnd(maxValidatorNameLength, ' ')} |                 |                                            | ${blocksCreatedTotal.toString().padStart(14, ' ')} | ${revealSkipsTotal.toString().padStart(15, ' ')} | `);
    console.log(dashes);
    console.log();
  
    console.log('Staking');
    console.log(`  getPools(${getPools.length}):`);
    getPools.forEach((poolId, index) => {
      console.log(`    ${(index + 1).toString().padStart(2, ' ')}. ${poolId}`);
    });
    console.log(`  getPoolsToBeElected(${getPoolsToBeElected.length}):`);
    getPoolsToBeElected.forEach((poolId, index) => {
      console.log(`    ${(index + 1).toString().padStart(2, ' ')}. ${poolId}`);
    });
    if (getPoolsToBeRemoved.length) {
      console.log(`  getPoolsToBeRemoved(${getPoolsToBeRemoved.length}):`);
      getPoolsToBeRemoved.forEach((poolId, index) => {
        console.log(`    ${(index + 1).toString().padStart(2, ' ')}. ${poolId}`);
      });
    }
    let {
      poolDelegators,
      allPoolsDelegatorsUnique,
      poolDelegatorsInactive
    } = await getPoolDelegators(currentBlock.number);
    const {
      stakeAmountTotal,
      orderedWithdrawAmountTotal,
      orderedWithdrawAmountTotalThisEpoch,
      howMuchStakerOrderedOnThisEpoch,
      howMuchDelegatorHolds
    } = await getAmounts(poolDelegators, stakingEpochStartBlock, currentBlock.number);
    const {
      totalDelegators,
      totalOrderedWithdrawDelegators,
      orderedWithdrawDelegatorsUnique
    } = await countDelegators(
      stakingEpoch,
      poolDelegators,
      poolDelegatorsInactive,
      howMuchDelegatorHolds,
      currentBlock.number
    );
    allPoolsDelegatorsUnique = Array.from(new Set(allPoolsDelegatorsUnique.concat(orderedWithdrawDelegatorsUnique)));
    console.log(`  stakingEpoch:               ${stakingEpoch}`);
    console.log(`  stakingEpochStartBlock:     ${stakingEpochStartBlock}`);
    console.log(`  stakingEpochEndBlock:       ${stakingEpochEndBlock} (at ~ ${stakingEpochEndTime.toUTCString()})`);
    console.log(`  orderedWithdrawAmountTotal: ${web3.utils.fromWei(orderedWithdrawAmountTotal)} (incl. ${web3.utils.fromWei(orderedWithdrawAmountTotalThisEpoch)} on this epoch)`);
    console.log(`  stakeAmountTotal:           ${web3.utils.fromWei(stakeAmountTotal)} (excl. orderedWithdrawAmountTotal)`);
    console.log(`  totalDelegators:            ${totalDelegators} (incl. ${totalOrderedWithdrawDelegators} who want to exit after this epoch; incl. pending delegators)`);
    console.log(`  totalDelegatorsUnique:      ${allPoolsDelegatorsUnique.length} (incl. ${orderedWithdrawDelegatorsUnique.length} who want to exit after this epoch; incl. pending delegators)`);
    if (program.showUnique) {
      const uniqueDelegatorsList = [];
      console.log('  uniqueDelegatorsList:');
      allPoolsDelegatorsUnique.forEach(delegator => {
        const delegatorLowercased = delegator.toLowerCase();
        const orderedAmount = howMuchStakerOrderedOnThisEpoch[delegatorLowercased] || new BN(0);
        const holdAmount = (howMuchDelegatorHolds[delegatorLowercased] || new BN(0)).add(orderedAmount);
        const exits = orderedWithdrawDelegatorsUnique.some(d => d.toLowerCase() == delegatorLowercased);
        uniqueDelegatorsList.push({ delegator, holdAmount, orderedAmount, exits });
      });
      uniqueDelegatorsList.sort((a, b) => {
        const cmpResult = b.holdAmount.cmp(a.holdAmount);
        if (cmpResult != 0) {
          return cmpResult;
        } else {
          return a.exits >= b.exits ? 1 : -1;
        }
      });
      uniqueDelegatorsList.forEach(({ delegator, holdAmount, orderedAmount, exits }, index) => {
        const delegatorLowercased = delegator.toLowerCase();
        let includingOrdered = '';
        if (!orderedAmount.isZero()) {
          includingOrdered = `, ordered ${toFixed(web3.utils.fromWei(orderedAmount), 2)} on this epoch`;
        }
        let wantsToExit = '';
        if (exits) {
          wantsToExit = ', wants to exit'
        }
        console.log(`    ${(index + 1).toString().padStart(2, ' ')}. ${delegator} (stakes ${toFixed(web3.utils.fromWei(holdAmount), 2)} tokens${includingOrdered}${wantsToExit})`);
      });
    }
    console.log();
  
    console.log('ValidatorSet');
    console.log(`  getValidators(${getValidators.length}):`);
    getValidators.forEach((miningAddress, index) => {
      console.log(`    ${(index + 1).toString().padStart(2, ' ')}. ${miningAddress}`);
    });
    console.log(`  getPendingValidators(${getPendingValidators.length}):`);
    getPendingValidators.forEach((miningAddress, index) => {
      console.log(`    ${(index + 1).toString().padStart(2, ' ')}. ${miningAddress}`);
    });
    console.log(`  validatorSetApplyBlock = ${validatorSetApplyBlock}`);
    console.log(`  unremovableValidator = ${unremovableValidator}`);
    console.log();
  
    console.log('Balance');
    console.log('  Native');
    console.log(`    blockRewardBalance = ${blockRewardNativeBalance}`);
    console.log('  Token');
    console.log(`    blockRewardBalance = ${blockRewardTokenBalance}`);
    console.log(`    stakingBalance = ${stakingBalance}`);
    console.log(`    totalSupply = ${totalSupply}`);
  
    console.log('==================================================================');
    process.exit(0);
  }
  
  async function countDelegators(stakingEpoch, poolDelegators, poolDelegatorsInactive, howMuchDelegatorHolds, blockNumber) {
    let totalDelegators = 0;
    for (let i = 0; i < validators.length; i++) {
      totalDelegators += poolDelegators[i].length;
    }
    const promises = [];
    const batch = new web3.BatchRequest();
    validators.forEach((validator, i) => {
      poolDelegatorsInactive[i].forEach(delegator => {
        promises.push(new Promise((resolve, reject) => {
          batch.add(Staking.methods.orderWithdrawEpoch(validator.poolId, delegator).call.request({}, blockNumber, (err, result) => {
            if (err) reject(err);
            else resolve({ epoch: result, delegator });
          }));
        }));
      });
    });
    await batch.execute();
    const orderWithdrawEpoch = await Promise.all(promises);
    const allPoolsDelegatorsInactive = [];
    let totalOrderedWithdrawDelegators = 0;
    orderWithdrawEpoch.forEach(result => {
      if (result.epoch == stakingEpoch) {
        totalDelegators++;
        totalOrderedWithdrawDelegators++;
        if (!howMuchDelegatorHolds.hasOwnProperty(result.delegator.toLowerCase())) {
          allPoolsDelegatorsInactive.push(result.delegator);
        }
      }
    });
    const orderedWithdrawDelegatorsUnique = Array.from(new Set(allPoolsDelegatorsInactive));
    return {
      totalDelegators,
      totalOrderedWithdrawDelegators,
      orderedWithdrawDelegatorsUnique
    };
  }
  
  async function getAmounts(poolDelegators, stakingEpochStartBlock, blockNumber) {
    // Calculate total amount of tokens currently staked into
    // the current validator pools and total amount of tokens
    // ordered for withdrawal from the current validator pools
    let promises = [];
    let batch = new web3.BatchRequest();
    validators.forEach((validator, i) => {
      promises.push(new Promise((resolve, reject) => {
        batch.add(Staking.methods.stakeAmountTotal(validator.poolId).call.request({}, blockNumber, (err, result) => {
          if (err) reject(err);
          else resolve({ stakeAmountTotal: result, i });
        }));
      }));
      promises.push(new Promise((resolve, reject) => {
        batch.add(Staking.methods.orderedWithdrawAmountTotal(validator.poolId).call.request({}, blockNumber, (err, result) => {
          if (err) reject(err);
          else resolve({ orderedWithdrawAmountTotal: result, i });
        }));
      }));
    });
    await batch.execute();
    let items = await Promise.all(promises);
    let stakeAmountTotal = [], orderedWithdrawAmountTotal = [];
    items.forEach(result => {
      if (result.hasOwnProperty('stakeAmountTotal')) {
        stakeAmountTotal[result.i] = result.stakeAmountTotal;
      } else {
        orderedWithdrawAmountTotal[result.i] = result.orderedWithdrawAmountTotal;
      }
    });
    stakeAmountTotal = stakeAmountTotal.reduce((acc, val) => acc.add(new BN(val)), new BN(0));
    orderedWithdrawAmountTotal = orderedWithdrawAmountTotal.reduce((acc, val) => acc.add(new BN(val)), new BN(0));
  
    // Determine how much tokens each staker ordered to withdraw
    // during the current staking epoch from the current validator pools
    // and calculate the corresponding total ordered amount
    let orderedWithdrawAmountTotalThisEpoch = new BN(0);
    const howMuchStakerOrderedOnThisEpoch = {};
    const events = await Staking.getPastEvents('OrderedWithdrawal', { fromBlock: stakingEpochStartBlock, toBlock: blockNumber });
    events.forEach(event => {
      const eventAmount = new BN(event.returnValues.amount);
      if (validators.some(v => v.poolId == event.returnValues.fromPoolId)) {
        const stakerAddress = event.returnValues.staker.toLowerCase();
        orderedWithdrawAmountTotalThisEpoch = orderedWithdrawAmountTotalThisEpoch.add(eventAmount);
        if (howMuchStakerOrderedOnThisEpoch.hasOwnProperty(stakerAddress)) {
          howMuchStakerOrderedOnThisEpoch[stakerAddress] = howMuchStakerOrderedOnThisEpoch[stakerAddress].add(eventAmount);
        } else {
          howMuchStakerOrderedOnThisEpoch[stakerAddress] = eventAmount;
        }
      }
    });
  
    // Determine how much tokens each delegator currently holds
    // in the current validator pools
    const howMuchDelegatorHolds = {};
    promises = [];
    batch = new web3.BatchRequest();
    validators.forEach((validator, i) => {
      poolDelegators[i].forEach((delegator, j) => {
        promises.push(new Promise((resolve, reject) => {
          batch.add(Staking.methods.stakeAmount(validator.poolId, delegator).call.request({}, blockNumber, (err, result) => {
            if (err) reject(err);
            else resolve({ i, j, stakeAmount: result });
          }));
        }));
      });
    });
    await batch.execute();
    items = await Promise.all(promises);
    items.forEach(item => {
      const delegator = poolDelegators[item.i][item.j].toLowerCase();
      const stakeAmount = new BN(item.stakeAmount);
      if (howMuchDelegatorHolds.hasOwnProperty(delegator)) {
        howMuchDelegatorHolds[delegator] = howMuchDelegatorHolds[delegator].add(stakeAmount);
      } else {
        howMuchDelegatorHolds[delegator] = stakeAmount;
      }
    });
  
    return {
      stakeAmountTotal,
      orderedWithdrawAmountTotal,
      orderedWithdrawAmountTotalThisEpoch,
      howMuchStakerOrderedOnThisEpoch,
      howMuchDelegatorHolds
    };
  }
  
  async function getBlocksCreatedAndRevealSkips(stakingEpoch, blockNumber) {
    const promises = [];
    const batch = new web3.BatchRequest();
    validators.forEach((validator, i) => {
      promises.push(new Promise((resolve, reject) => {
        batch.add(BlockReward.methods.blocksCreated(stakingEpoch, validator.poolId).call.request({}, blockNumber, (err, result) => {
          if (err) reject(err);
          else resolve({ blocksCreated: result, i });
        }));
      }));
      promises.push(new Promise((resolve, reject) => {
        batch.add(Random.methods.revealSkips(stakingEpoch, validator.miningAddress).call.request({}, blockNumber, (err, result) => {
          if (err) reject(err);
          else resolve({ revealSkips: result, i });
        }));
      }));
    });
    await batch.execute();
    const blocksCreatedAndRevealSkips = await Promise.all(promises);
    const blocksCreated = [], revealSkips = [];
    blocksCreatedAndRevealSkips.forEach(result => {
      if (result.hasOwnProperty('blocksCreated')) {
        blocksCreated[result.i] = result.blocksCreated;
      } else {
        revealSkips[result.i] = result.revealSkips;
      }
    });
    return { blocksCreated, revealSkips };
  }
  
  async function getCurrentValidators(miningAddresses, blockNumber) {
    let promises = [];
    let batch = new web3.BatchRequest();
    miningAddresses.forEach(miningAddress => {
      promises.push(new Promise((resolve, reject) => {
        batch.add(ValidatorSet.methods.stakingByMiningAddress(miningAddress).call.request({}, blockNumber, (err, result) => {
          if (err) reject(err);
          else resolve(result);
        }));
      }));
    });
    await batch.execute();
    const stakingAddresses = await Promise.all(promises);
  
    promises = [];
    batch = new web3.BatchRequest();
    miningAddresses.forEach(miningAddress => {
      promises.push(new Promise((resolve, reject) => {
        batch.add(ValidatorSet.methods.idByMiningAddress(miningAddress).call.request({}, blockNumber, (err, result) => {
          if (err) reject(err);
          else resolve(result);
        }));
      }));
    });
    await batch.execute();
    const poolIds = await Promise.all(promises);
  
    promises = [];
    batch = new web3.BatchRequest();
    poolIds.forEach(poolId => {
      promises.push(new Promise((resolve, reject) => {
        batch.add(ValidatorSet.methods.poolName(poolId).call.request({}, blockNumber, (err, result) => {
          if (err) reject(err);
          else resolve(result);
        }));
      }));
    });
    await batch.execute();
    const poolNames = await Promise.all(promises);
  
    miningAddresses.forEach((miningAddress, index) => {
      const stakingAddress = stakingAddresses[index];
      const stakingAddressShort = stakingAddress.slice(0, 6) + '-' + stakingAddress.slice(-6);
      const name = knownNames[miningAddress] || poolNames[index] || stakingAddressShort;
      knownNames[miningAddress] = name;
      if (knownNames[miningAddress]) {
        knownNames[stakingAddress] = name;
      }
      validators.push({ miningAddress, stakingAddress, stakingAddressShort, name, poolId: poolIds[index] });
    });
  }
  
  async function getPoolDelegators(blockNumber) {
    const promises = [];
    const batch = new web3.BatchRequest();
    validators.forEach((validator, i) => {
      promises.push(new Promise((resolve, reject) => {
        batch.add(Staking.methods.poolDelegators(validator.poolId).call.request({}, blockNumber, (err, result) => {
          if (err) reject(err);
          else resolve({ poolDelegators: result, i });
        }));
      }));
      promises.push(new Promise((resolve, reject) => {
        batch.add(Staking.methods.poolDelegatorsInactive(validator.poolId).call.request({}, blockNumber, (err, result) => {
          if (err) reject(err);
          else resolve({ poolDelegatorsInactive: result, i });
        }));
      }));
    });
    await batch.execute();
    const items = await Promise.all(promises);
    const poolDelegators = [], poolDelegatorsInactive = [];
    items.forEach(result => {
      if (result.hasOwnProperty('poolDelegators')) {
        poolDelegators[result.i] = result.poolDelegators;
      } else {
        poolDelegatorsInactive[result.i] = result.poolDelegatorsInactive;
      }
    });
    let allPoolsDelegators = [];
    for (let i = 0; i < validators.length; i++) {
      allPoolsDelegators = allPoolsDelegators.concat(poolDelegators[i]);
    }
    const allPoolsDelegatorsUnique = Array.from(new Set(allPoolsDelegators));
    return { poolDelegators, allPoolsDelegatorsUnique, poolDelegatorsInactive };
  }
  
  async function showActiveDelegatorsByStakingEpoch(stakingEpoch, stakingEpochDuration, currentEpochNumber, currentEpochStartBlock, currentBlockNumber) {
    if (stakingEpoch > currentEpochNumber) {
      console.log('Unable to find any delegators for the given staking epoch');
      return;
    }
    const { startBlock, endBlock } = blocksByStakingEpochNumber(stakingEpoch, stakingEpochDuration, currentEpochNumber, currentEpochStartBlock);
    const blockNumber = endBlock > currentBlockNumber ? currentBlockNumber : endBlock;
  
    let promises = [];
    let batch = new web3.BatchRequest();
    promises.push(new Promise((resolve, reject) => {
      batch.add(ValidatorSet.methods.getValidators().call.request({}, blockNumber, (err, result) => {
        if (err) reject(err);
        else resolve(result);
      }));
    }));
    promises.push(new Promise((resolve, reject) => {
      batch.add(ValidatorSet.methods.getValidatorsIds().call.request({}, blockNumber, (err, result) => {
        if (err) reject(err);
        else resolve(result);
      }));
    }));
    await batch.execute();
    const [miningAddresses, poolIds] = await Promise.all(promises);
    
    promises = [];
    batch = new web3.BatchRequest();
    poolIds.forEach(poolId => {
      promises.push(new Promise((resolve, reject) => {
        batch.add(ValidatorSet.methods.stakingAddressById(poolId).call.request({}, blockNumber, (err, result) => {
          if (err) reject(err);
          else resolve(result);
        }));
      }));
    });
    await batch.execute();
    const stakingAddresses = await Promise.all(promises);
    const miningAddressById = {};
    const stakingAddressById = {};
    promises = [];
    batch = new web3.BatchRequest();
    stakingAddresses.forEach((stakingAddress, index) => {
    	const poolId = poolIds[index];
      miningAddressById[poolId] = miningAddresses[index];
      stakingAddressById[poolId] = stakingAddress;
      promises.push(new Promise((resolve, reject) => {
        batch.add(Staking.methods.poolDelegators(poolId).call.request({}, startBlock - 1, (err, delegators) => {
          if (err) reject(err);
          else resolve({ poolId, delegators });
        }));
      }));
    });
    await batch.execute();
    const poolDelegators = await Promise.all(promises);
    promises = [];
    batch = new web3.BatchRequest();
    poolDelegators.forEach(({ poolId, delegators }) => {
      delegators.forEach(delegator => {
        promises.push(new Promise((resolve, reject) => {
          batch.add(Staking.methods.stakeAmount(poolId, delegator).call.request({}, startBlock - 1, (err, amount) => {
            if (err) reject(err);
            else resolve({ poolId, delegator, amount: new BN(amount) });
          }));
        }));
      });
    });
    await batch.execute();
    const amounts = await Promise.all(promises);
    amounts.sort((a, b) => b.amount.cmp(a.amount));
    const delegatorAmountByPool = {};
    const delegatorTotalAmount = {};
    amounts.forEach(({ poolId, delegator, amount }) => {
    	if (!delegatorAmountByPool.hasOwnProperty(poolId)) {
        delegatorAmountByPool[poolId] = {};
      }
      delegatorAmountByPool[poolId][delegator] = amount;
      if (delegatorTotalAmount.hasOwnProperty(delegator)) {
        delegatorTotalAmount[delegator] = delegatorTotalAmount[delegator].add(amount);
      } else {
        delegatorTotalAmount[delegator] = amount;
      }
    });
    const delegators = Object.keys(delegatorTotalAmount);
    if (delegators.length) {
      console.log(`Delegator list for the staking epoch # ${stakingEpoch} (does not include pending delegators):`);
      console.log();
      delegators.sort((a, b) => delegatorTotalAmount[b].cmp(delegatorTotalAmount[a]));
      console.log('------------------------------------------------------------------------------------');
      console.log('|     | Delegator                                  | Amount                        |');
      console.log('------------------------------------------------------------------------------------');
      delegators.forEach((delegator, index) => {
        console.log(`| ${(index + 1).toString().padStart(3, ' ')} | ${delegator} | ${web3.utils.fromWei(delegatorTotalAmount[delegator]).padEnd(29, ' ')} |`);
      });
      console.log('------------------------------------------------------------------------------------');
      console.log();
      console.log();
  
      console.log(`Delegators by each validator pool:`);
      console.log();
      console.log();
      for (const poolId in delegatorAmountByPool) {
        const stakingAddress = stakingAddressById[poolId];
        let poolTotalAmount = new BN(0);
        const name = knownNames[stakingAddress] || knownNames[miningAddressById[poolId]];
        if (name) {
          console.log(`${name} [stakingAddress: ${stakingAddress}, poolId: ${poolId}]`);
        } else {
          console.log(`Staking address: ${stakingAddress}, poolId: ${poolId}`);
        }
        console.log('------------------------------------------------------------------------------------');
        console.log('|     | Delegator                                  | Amount                        |');
        console.log('------------------------------------------------------------------------------------');
        let index = 1;
        for (const delegator in delegatorAmountByPool[poolId]) {
          const amount = delegatorAmountByPool[poolId][delegator];
          console.log(`| ${(index++).toString().padStart(3, ' ')} | ${delegator} | ${web3.utils.fromWei(amount).padEnd(29, ' ')} |`);
          poolTotalAmount = poolTotalAmount.add(amount);
        }
        console.log('------------------------------------------------------------------------------------');
        console.log(`|     | Total:                                     | ${web3.utils.fromWei(poolTotalAmount).padEnd(29, ' ')} |`);
        console.log('------------------------------------------------------------------------------------');
        console.log();
        console.log();
      }
    } else {
      console.log('Unable to find any delegators for the given staking epoch');
    }
  }
  
  async function showAPY(currentStakingEpoch, currentStakingEpochStartBlock, stakingEpochDuration, currentBlockNumber, averageBlockTime, validatorSetApplyBlock) {
    if (currentStakingEpoch == 0) {
      console.log('Current approximate Annual Percentage Yield (APY) cannot be displayed for the initial (zero) staking epoch.');
      return;
    }
    if (validatorSetApplyBlock == 0) {
      console.log('Current approximate Annual Percentage Yield (APY) cannot be displayed at the very beginning of the staking epoch as the new validator set is not finalized yet. Please, try a bit later in a few blocks.');
      return;
    }
    const apyStartBlockNumber = validatorSetApplyBlock - 0 + validators.length * 10; // skip a few AuRa rounds
    if (currentBlockNumber < apyStartBlockNumber) {
      console.log(`Current approximate Annual Percentage Yield (APY) cannot be displayed at the very beginning of the staking epoch as we currently do not know blocks creating statistics. Please, try after block ${apyStartBlockNumber}`);
      return;
    }
    console.log(`Current approximate Annual Percentage Yield (APY) is calculated based on the current staking amounts (excluding pending amounts) for the current staking epoch ${currentStakingEpoch}.`);
    console.log();
    const epochsPerYear = new BN(Math.floor(31536000 / averageBlockTime / stakingEpochDuration));
    const rewardToDistribute = await BlockReward.methods.currentTokenRewardToDistribute(Staking.options.address, currentStakingEpoch, 0, 0, []).call({}, currentBlockNumber);
    console.log(`Reward to distribute (among all validator pools): ${web3.utils.fromWei(rewardToDistribute[0])} tokens`);
    console.log(`Predicted number of staking epochs in calendar year: ${epochsPerYear}`);
    console.log();
    console.log();
    const poolRewards = await BlockReward.methods.currentPoolRewards(rewardToDistribute[0], [], 0, currentStakingEpoch).call({}, currentBlockNumber);
  
    let promises = [];
    let batch = new web3.BatchRequest();
    validators.forEach(v => {
      promises.push(new Promise((resolve, reject) => {
        batch.add(Staking.methods.poolDelegators(v.poolId).call.request({}, currentStakingEpochStartBlock - 1, (err, delegators) => {
          if (err) reject(err);
          else resolve(delegators);
        }));
      }));
    });
    await batch.execute();
    const poolDelegators = await Promise.all(promises);
  
    promises = [];
    batch = new web3.BatchRequest();
    poolDelegators.forEach((delegators, validatorIndex) => {
      const poolId = validators[validatorIndex].poolId;
      const stakingAddress = validators[validatorIndex].stakingAddress;
      promises.push(new Promise((resolve, reject) => {
        batch.add(Staking.methods.stakeAmount(poolId, '0x0000000000000000000000000000000000000000').call.request({}, currentStakingEpochStartBlock - 1, (err, amount) => {
          if (err) reject(err);
          else resolve({ validatorIndex, staker: stakingAddress, amount: new BN(amount) });
        }));
      }));
      delegators.forEach(delegator => {
        promises.push(new Promise((resolve, reject) => {
          batch.add(Staking.methods.stakeAmount(poolId, delegator).call.request({}, currentStakingEpochStartBlock - 1, (err, amount) => {
            if (err) reject(err);
            else resolve({ validatorIndex, staker: delegator, amount: new BN(amount) });
          }));
        }));
      });
    });
    await batch.execute();
    const amounts = await Promise.all(promises);
  
    const stakeAmounts = [];
    amounts.forEach(({ validatorIndex, staker, amount }) => {
      if (!stakeAmounts[validatorIndex]) {
        stakeAmounts[validatorIndex] = {};
      }
      stakeAmounts[validatorIndex][staker] = amount;
    });
  
    promises = [];
    batch = new web3.BatchRequest();
    poolDelegators.forEach((delegators, validatorIndex) => {
      const stakingAddress = validators[validatorIndex].stakingAddress;
      const validatorStaked = stakeAmounts[validatorIndex][stakingAddress];
      const totalStaked = delegators.reduce((acc, delegator) => acc.add(stakeAmounts[validatorIndex][delegator]), validatorStaked);
      const poolReward = poolRewards[validatorIndex];
      promises.push(new Promise((resolve, reject) => {
        batch.add(BlockReward.methods.validatorShare(currentStakingEpoch, validatorStaked.toString(), totalStaked.toString(), poolReward).call.request({}, currentBlockNumber, (err, reward) => {
          if (err) reject(err);
          else resolve({ validatorIndex, staker: stakingAddress, reward: new BN(reward) });
        }));
      }));
      delegators.forEach(delegator => {
        const delegatorStaked = stakeAmounts[validatorIndex][delegator];
        promises.push(new Promise((resolve, reject) => {
          batch.add(BlockReward.methods.delegatorShare(currentStakingEpoch, delegatorStaked.toString(), validatorStaked.toString(), totalStaked.toString(), poolReward).call.request({}, currentBlockNumber, (err, reward) => {
            if (err) reject(err);
            else resolve({ validatorIndex, staker: delegator, reward: new BN(reward) });
          }));
        }));
      });
    });
    await batch.execute();
    const rewards = await Promise.all(promises);
  
    const stakerRewards = [];
    rewards.forEach(({ validatorIndex, staker, reward }) => {
      if (!stakerRewards[validatorIndex]) {
        stakerRewards[validatorIndex] = {};
      }
      stakerRewards[validatorIndex][staker] = reward;
    });
  
    poolDelegators.forEach((delegators, validatorIndex) => {
      const poolId = validators[validatorIndex].poolId;
      const miningAddress = validators[validatorIndex].miningAddress;
      const stakingAddress = validators[validatorIndex].stakingAddress;
      let rewardSum = new BN(0);
      let stakeAmountSum = new BN(0);
  
      let reward = stakerRewards[validatorIndex][stakingAddress];
      let stakeAmount = stakeAmounts[validatorIndex][stakingAddress];
      let percentagePerYear = parseFloat(web3.utils.fromWei(reward.mul(new BN('100000000000000000000')).div(stakeAmount).mul(epochsPerYear)));
      percentagePerYear = (Math.floor(percentagePerYear * 100) / 100).toString() + '%';
      rewardSum = rewardSum.add(reward);
      stakeAmountSum = stakeAmountSum.add(stakeAmount);
      console.log(`Validator ${knownNames[miningAddress]} [stakingAddress: ${stakingAddress}, poolId: ${poolId}]`);
      console.log('-----------------------------------------------------------------------------------------------------------------------------');
      console.log('|     | Staker                                     | Non-pending stake, tokens     | Predicted reward, tokens      | ~APY   |');
      console.log('-----------------------------------------------------------------------------------------------------------------------------');
      console.log(`|   1 | ${stakingAddress} | ${web3.utils.fromWei(stakeAmount).padEnd(29, ' ')} | ${web3.utils.fromWei(reward).padEnd(29, ' ')} | ${percentagePerYear.padEnd(6, ' ')} |`);
      
      delegators.sort((a, b) => stakerRewards[validatorIndex][b].cmp(stakerRewards[validatorIndex][a]));
      delegators.forEach((delegator, index) => {
        reward = stakerRewards[validatorIndex][delegator];
        stakeAmount = stakeAmounts[validatorIndex][delegator];
        percentagePerYear = parseFloat(web3.utils.fromWei(reward.mul(new BN('100000000000000000000')).div(stakeAmount).mul(epochsPerYear)));
        percentagePerYear = (Math.floor(percentagePerYear * 100) / 100).toString() + '%';
        rewardSum = rewardSum.add(reward);
        stakeAmountSum = stakeAmountSum.add(stakeAmount);
        console.log(`| ${(index + 2).toString().padStart(3, ' ')} | ${delegator} | ${web3.utils.fromWei(stakeAmount).padEnd(29, ' ')} | ${web3.utils.fromWei(reward).padEnd(29, ' ')} | ${percentagePerYear.padEnd(6, ' ')} |`);
      });
  
      percentagePerYear = parseFloat(web3.utils.fromWei(rewardSum.mul(new BN('100000000000000000000')).div(stakeAmountSum).mul(epochsPerYear)));
      percentagePerYear = (Math.floor(percentagePerYear * 100) / 100).toString() + '%';
      console.log('-----------------------------------------------------------------------------------------------------------------------------');
      console.log(`|     | Total:                                     | ${web3.utils.fromWei(stakeAmountSum).padEnd(29, ' ')} | ${web3.utils.fromWei(rewardSum).padEnd(29, ' ')} | ${percentagePerYear.padEnd(6, ' ')} |`);
      console.log('-----------------------------------------------------------------------------------------------------------------------------');
      console.log();
      console.log();
    });
  }
  
  function blocksByStakingEpochNumber(stakingEpoch, stakingEpochDuration, currentEpochNumber, currentEpochStartBlock) {
    const diff = currentEpochNumber - stakingEpoch;
    const startBlock = currentEpochStartBlock - stakingEpochDuration * diff;
    const endBlock = stakingEpochDuration - 1 + startBlock;
    return { startBlock, endBlock };
  }
  
  function toFixed(x, n) {
    const v = (typeof x === 'string' ? x : x.toString()).split('.');
    if (n <= 0) return v[0];
    let f = v[1] || '';
    if (f.length > n) return `${v[0]}.${f.substr(0,n)}`;
    while (f.length < n) f += '0';
    return `${v[0]}.${f}`
  }
  