From 240ad0a33c11f567807d365d6fc6e0ffeef20fc7 Mon Sep 17 00:00:00 2001 From: saneery Date: Mon, 22 Apr 2019 12:46:57 +0300 Subject: [PATCH 01/13] add staking contracts abi --- .../explorer/validator/metadata_retriever.ex | 2 +- .../poa}/metadata.json | 0 .../poa}/validators.json | 0 .../priv/contracts_abi/pos/staking.json | 925 ++++++++++++++++++ .../priv/contracts_abi/pos/validators.json | 492 ++++++++++ 5 files changed, 1418 insertions(+), 1 deletion(-) rename apps/explorer/priv/{validator_contracts_abi => contracts_abi/poa}/metadata.json (100%) rename apps/explorer/priv/{validator_contracts_abi => contracts_abi/poa}/validators.json (100%) create mode 100644 apps/explorer/priv/contracts_abi/pos/staking.json create mode 100644 apps/explorer/priv/contracts_abi/pos/validators.json diff --git a/apps/explorer/lib/explorer/validator/metadata_retriever.ex b/apps/explorer/lib/explorer/validator/metadata_retriever.ex index 682b1e906e..4d262fca61 100644 --- a/apps/explorer/lib/explorer/validator/metadata_retriever.ex +++ b/apps/explorer/lib/explorer/validator/metadata_retriever.ex @@ -69,7 +69,7 @@ defmodule Explorer.Validator.MetadataRetriever do # sobelow_skip ["Traversal"] defp contract_abi(file_name) do :explorer - |> Application.app_dir("priv/validator_contracts_abi/#{file_name}") + |> Application.app_dir("priv/contracts_abi/poa/#{file_name}") |> File.read!() |> Jason.decode!() end diff --git a/apps/explorer/priv/validator_contracts_abi/metadata.json b/apps/explorer/priv/contracts_abi/poa/metadata.json similarity index 100% rename from apps/explorer/priv/validator_contracts_abi/metadata.json rename to apps/explorer/priv/contracts_abi/poa/metadata.json diff --git a/apps/explorer/priv/validator_contracts_abi/validators.json b/apps/explorer/priv/contracts_abi/poa/validators.json similarity index 100% rename from apps/explorer/priv/validator_contracts_abi/validators.json rename to apps/explorer/priv/contracts_abi/poa/validators.json diff --git a/apps/explorer/priv/contracts_abi/pos/staking.json b/apps/explorer/priv/contracts_abi/pos/staking.json new file mode 100644 index 0000000000..7bcbcfb18c --- /dev/null +++ b/apps/explorer/priv/contracts_abi/pos/staking.json @@ -0,0 +1,925 @@ +[ + { + "constant": true, + "inputs": [], + "name": "STAKE_UNIT", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "MAX_DELEGATORS_PER_POOL", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "MAX_CANDIDATES", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "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" + } + ], + "name": "Claimed", + "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" + } + ], + "name": "Staked", + "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" + } + ], + "name": "StakeMoved", + "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" + } + ], + "name": "WithdrawalOrdered", + "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" + } + ], + "name": "Withdrawn", + "type": "event" + }, + { + "constant": false, + "inputs": [ + { + "name": "_unremovableStakingAddress", + "type": "address" + } + ], + "name": "clearUnremovableValidator", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "incrementStakingEpoch", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_stakingAddress", + "type": "address" + } + ], + "name": "removePool", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "removePool", + "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": false, + "inputs": [ + { + "name": "_toPoolStakingAddress", + "type": "address" + }, + { + "name": "_amount", + "type": "uint256" + } + ], + "name": "stake", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_fromPoolStakingAddress", + "type": "address" + }, + { + "name": "_amount", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_poolStakingAddress", + "type": "address" + }, + { + "name": "_amount", + "type": "int256" + } + ], + "name": "orderWithdraw", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_poolStakingAddress", + "type": "address" + } + ], + "name": "claimOrderedWithdraw", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_erc20TokenContract", + "type": "address" + } + ], + "name": "setErc20TokenContract", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_minStake", + "type": "uint256" + } + ], + "name": "setCandidateMinStake", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_minStake", + "type": "uint256" + } + ], + "name": "setDelegatorMinStake", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getPools", + "outputs": [ + { + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getPoolsInactive", + "outputs": [ + { + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getPoolsLikelihood", + "outputs": [ + { + "name": "likelihoods", + "type": "int256[]" + }, + { + "name": "sum", + "type": "int256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getPoolsToBeElected", + "outputs": [ + { + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getPoolsToBeRemoved", + "outputs": [ + { + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "areStakeAndWithdrawAllowed", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "erc20TokenContract", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getCandidateMinStake", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getDelegatorMinStake", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_stakingAddress", + "type": "address" + } + ], + "name": "isPoolActive", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "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": "_poolStakingAddress", + "type": "address" + }, + { + "name": "_staker", + "type": "address" + } + ], + "name": "maxWithdrawOrderAllowed", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "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": "_poolStakingAddress", + "type": "address" + }, + { + "name": "_staker", + "type": "address" + } + ], + "name": "orderedWithdrawAmount", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_poolStakingAddress", + "type": "address" + } + ], + "name": "orderedWithdrawAmountTotal", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_poolStakingAddress", + "type": "address" + }, + { + "name": "_staker", + "type": "address" + } + ], + "name": "orderWithdrawEpoch", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_poolStakingAddress", + "type": "address" + } + ], + "name": "stakeAmountTotal", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_poolStakingAddress", + "type": "address" + } + ], + "name": "poolDelegators", + "outputs": [ + { + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_poolStakingAddress", + "type": "address" + }, + { + "name": "_delegator", + "type": "address" + } + ], + "name": "poolDelegatorIndex", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_poolStakingAddress", + "type": "address" + }, + { + "name": "_delegator", + "type": "address" + } + ], + "name": "poolDelegatorInactiveIndex", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_stakingAddress", + "type": "address" + } + ], + "name": "poolIndex", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_stakingAddress", + "type": "address" + } + ], + "name": "poolInactiveIndex", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_stakingAddress", + "type": "address" + } + ], + "name": "poolToBeElectedIndex", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_stakingAddress", + "type": "address" + } + ], + "name": "poolToBeRemovedIndex", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_poolStakingAddress", + "type": "address" + }, + { + "name": "_staker", + "type": "address" + } + ], + "name": "stakeAmount", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_poolStakingAddress", + "type": "address" + }, + { + "name": "_staker", + "type": "address" + } + ], + "name": "stakeAmountByCurrentEpoch", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_poolStakingAddress", + "type": "address" + }, + { + "name": "_staker", + "type": "address" + } + ], + "name": "stakeAmountMinusOrderedWithdraw", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_poolStakingAddress", + "type": "address" + } + ], + "name": "stakeAmountTotalMinusOrderedWithdraw", + "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": "validatorSetContract", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/apps/explorer/priv/contracts_abi/pos/validators.json b/apps/explorer/priv/contracts_abi/pos/validators.json new file mode 100644 index 0000000000..0f0fd038c6 --- /dev/null +++ b/apps/explorer/priv/contracts_abi/pos/validators.json @@ -0,0 +1,492 @@ +[ + { + "constant": false, + "inputs": [], + "name": "newValidatorSet", + "outputs": [ + { + "name": "", + "type": "bool" + }, + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "MAX_VALIDATORS", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "indexed": true, + "name": "parentHash", + "type": "bytes32" + }, + { + "indexed": false, + "name": "newSet", + "type": "address[]" + } + ], + "name": "InitiateChange", + "type": "event", + "anonymous": false + }, + { + "inputs": [], + "name": "clearUnremovableValidator", + "type": "function", + "constant": false, + "outputs": [], + "payable": false, + "stateMutability": "nonpayable" + }, + { + "inputs": [], + "name": "emitInitiateChange", + "type": "function", + "constant": false, + "outputs": [], + "payable": false, + "stateMutability": "nonpayable" + }, + { + "inputs": [], + "name": "finalizeChange", + "type": "function", + "constant": false, + "outputs": [], + "payable": false, + "stateMutability": "nonpayable" + }, + { + "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", + "type": "function", + "constant": false, + "outputs": [], + "payable": false, + "stateMutability": "nonpayable" + }, + { + "inputs": [ + { + "name": "_miningAddress", + "type": "address" + }, + { + "name": "_stakingAddress", + "type": "address" + } + ], + "name": "setStakingAddress", + "type": "function", + "constant": false, + "outputs": [], + "payable": false, + "stateMutability": "nonpayable" + }, + { + "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": "blockRewardContract", + "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": "emitInitiateChangeCallable", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getPreviousValidators", + "outputs": [ + { + "name": "", + "type": "address[]" + } + ], + "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": "getQueueValidators", + "outputs": [ + { + "name": "", + "type": "address[]" + }, + { + "name": "", + "type": "bool" + } + ], + "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": "initiateChangeAllowed", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_miningAddress", + "type": "address" + } + ], + "name": "isReportValidatorValid", + "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": "isValidatorOnPreviousEpoch", + "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": "_stakingAddress", + "type": "address" + } + ], + "name": "miningByStakingAddress", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "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": "_miningAddress", + "type": "address" + } + ], + "name": "stakingByMiningAddress", + "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" + }, + { + "constant": true, + "inputs": [], + "name": "unremovableValidator", + "outputs": [ + { + "name": "stakingAddress", + "type": "address" + } + ], + "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": "_miningAddress", + "type": "address" + } + ], + "name": "validatorIndex", + "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" + } +] \ No newline at end of file From 2be2577e825081f353239df83322a150b9ef3d72 Mon Sep 17 00:00:00 2001 From: saneery Date: Mon, 22 Apr 2019 12:50:41 +0300 Subject: [PATCH 02/13] add staking pools reader --- .../lib/explorer/staking/pools_reader.ex | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 apps/explorer/lib/explorer/staking/pools_reader.ex diff --git a/apps/explorer/lib/explorer/staking/pools_reader.ex b/apps/explorer/lib/explorer/staking/pools_reader.ex new file mode 100644 index 0000000000..0aaffaf8e9 --- /dev/null +++ b/apps/explorer/lib/explorer/staking/pools_reader.ex @@ -0,0 +1,84 @@ +defmodule Explorer.Staking.PoolsReader do + @moduledoc """ + Reads staking pools using Smart Contract functions from the blockchain. + """ + alias Explorer.SmartContract.Reader + + @spec get_pools() :: [String.t()] + def get_pools do + get_active_pools() ++ get_inactive_pools() + end + + @spec get_active_pools() :: [String.t()] + def get_active_pools do + {:ok, [active_pools]} = call_staking_method("getPools", []) + active_pools + end + + @spec get_inactive_pools() :: [String.t()] + def get_inactive_pools do + {:ok, [inactive_pools]} = call_staking_method("getPoolsInactive", []) + inactive_pools + end + + @spec pool_data(String.t()) :: {:ok, map()} | :error + def pool_data(staking_address) do + with {:ok, [mining_address]} <- call_validators_method("miningByStakingAddress", [staking_address]), + {:ok, [is_active]} <- call_staking_method("isPoolActive", [staking_address]), + {:ok, [delegator_addresses]} <- call_staking_method("poolDelegators", [staking_address]), + {:ok, [staked_amount]} <- call_staking_method("stakeAmountTotalMinusOrderedWithdraw", [staking_address]), + {:ok, [is_validator]} <- call_validators_method("isValidator", [mining_address]), + {:ok, [was_validator_count]} <- call_validators_method("validatorCounter", [mining_address]), + {:ok, [is_banned]} <- call_validators_method("isValidatorBanned", [mining_address]), + {:ok, [banned_unitil]} <- call_validators_method("bannedUntil", [mining_address]), + {:ok, [was_banned_count]} <- call_validators_method("banCounter", [mining_address]) do + { + :ok, + %{ + staking_address: staking_address, + mining_address: mining_address, + is_active: is_active, + delegator_addresses: delegator_addresses, + staked_amount: staked_amount, + is_validator: is_validator, + was_validator_count: was_validator_count, + is_banned: is_banned, + banned_unitil: banned_unitil, + was_banned_count: was_banned_count + } + } + else + _ -> + :error + end + end + + defp call_staking_method(method, params) do + %{^method => resp} = + Reader.query_contract(config(:staking_contract_address), abi("staking.json"), %{ + method => params + }) + + resp + end + + defp call_validators_method(method, params) do + %{^method => resp} = + Reader.query_contract(config(:validators_contract_address), abi("validators.json"), %{ + method => params + }) + + resp + end + + defp config(key) do + Application.get_env(:explorer, __MODULE__, [])[key] + end + + defp abi(file_name) do + :explorer + |> Application.app_dir("priv/contracts_abi/pos/#{file_name}") + |> File.read!() + |> Jason.decode!() + end +end From e9c61945e6cc65e70ae190d32534398731053ec1 Mon Sep 17 00:00:00 2001 From: saneery Date: Mon, 22 Apr 2019 12:51:13 +0300 Subject: [PATCH 03/13] add staking pools fetcher --- apps/indexer/config/config.exs | 1 + apps/indexer/lib/indexer/block/fetcher.ex | 7 +- .../lib/indexer/block/realtime/fetcher.ex | 4 +- .../lib/indexer/fetcher/staking_pools.ex | 135 ++++++++++++++++++ apps/indexer/lib/indexer/supervisor.ex | 4 +- 5 files changed, 148 insertions(+), 3 deletions(-) create mode 100644 apps/indexer/lib/indexer/fetcher/staking_pools.ex diff --git a/apps/indexer/config/config.exs b/apps/indexer/config/config.exs index 181323c0a3..04c2edf7c8 100644 --- a/apps/indexer/config/config.exs +++ b/apps/indexer/config/config.exs @@ -38,6 +38,7 @@ config :indexer, # config :indexer, Indexer.Fetcher.ReplacedTransaction.Supervisor, disabled?: true # config :indexer, Indexer.Fetcher.BlockReward.Supervisor, disabled?: true +config :indexer, Indexer.Fetcher.StakingPools.Supervisor, disabled?: true config :indexer, Indexer.Tracer, service: :indexer, diff --git a/apps/indexer/lib/indexer/block/fetcher.ex b/apps/indexer/lib/indexer/block/fetcher.ex index 241142f4f6..9f9a9474f4 100644 --- a/apps/indexer/lib/indexer/block/fetcher.ex +++ b/apps/indexer/lib/indexer/block/fetcher.ex @@ -22,7 +22,8 @@ defmodule Indexer.Block.Fetcher do ReplacedTransaction, Token, TokenBalance, - UncleBlock + UncleBlock, + StakingPools } alias Indexer.Tracer @@ -280,6 +281,10 @@ defmodule Indexer.Block.Fetcher do def async_import_token_balances(_), do: :ok + def async_import_staking_pools do + StakingPools.async_fetch() + end + def async_import_uncles(%{block_second_degree_relations: block_second_degree_relations}) do block_second_degree_relations |> Enum.map(& &1.uncle_hash) diff --git a/apps/indexer/lib/indexer/block/realtime/fetcher.ex b/apps/indexer/lib/indexer/block/realtime/fetcher.ex index 9fce118964..8778677f33 100644 --- a/apps/indexer/lib/indexer/block/realtime/fetcher.ex +++ b/apps/indexer/lib/indexer/block/realtime/fetcher.ex @@ -20,7 +20,8 @@ defmodule Indexer.Block.Realtime.Fetcher do async_import_tokens: 1, async_import_token_balances: 1, async_import_uncles: 1, - fetch_and_import_range: 2 + fetch_and_import_range: 2, + async_import_staking_pools: 0 ] alias Ecto.Changeset @@ -350,6 +351,7 @@ defmodule Indexer.Block.Realtime.Fetcher do async_import_token_balances(imported) async_import_uncles(imported) async_import_replaced_transactions(imported) + async_import_staking_pools() end defp balances( diff --git a/apps/indexer/lib/indexer/fetcher/staking_pools.ex b/apps/indexer/lib/indexer/fetcher/staking_pools.ex new file mode 100644 index 0000000000..246f53bdf7 --- /dev/null +++ b/apps/indexer/lib/indexer/fetcher/staking_pools.ex @@ -0,0 +1,135 @@ +defmodule Indexer.Fetcher.StakingPools do + @moduledoc """ + Fetches staking pools and send to be imported in `Address.Name` table + """ + + use Indexer.Fetcher + use Spandex.Decorators + + require Logger + + alias Explorer.Chain + alias Indexer.BufferedTask + alias Explorer.Staking.PoolsReader + + @behaviour BufferedTask + + @defaults [ + flush_interval: 300, + max_batch_size: 100, + max_concurrency: 10, + task_supervisor: Indexer.Fetcher.StakingPools.TaskSupervisor + ] + + @max_retries 3 + + @spec async_fetch() :: :ok + def async_fetch() do + pools = + PoolsReader.get_pools() + |> Enum.map(&entry/1) + + pid = GenServer.whereis(__MODULE__) + + if pid && Process.alive?(pid) do + BufferedTask.buffer(__MODULE__, pools, :infinity) + end + end + + @doc false + def child_spec([init_options, gen_server_options]) do + merged_init_opts = + @defaults + |> Keyword.merge(init_options) + |> Keyword.put(:state, {0, []}) + + Supervisor.child_spec({BufferedTask, [{__MODULE__, merged_init_opts}, gen_server_options]}, id: __MODULE__) + end + + @impl BufferedTask + def init(_initial, reducer, acc) do + PoolsReader.get_pools() + |> Enum.map(&entry/1) + |> Enum.reduce(acc, &reducer.(&1, &2)) + end + + @impl BufferedTask + def run(pools, _json_rpc_named_arguments) do + failed_list = + pools + |> Enum.map(&Map.put(&1, :retries_count, &1.retries_count + 1)) + |> fetch_from_blockchain() + |> import_pools() + + if failed_list == [] do + :ok + else + {:retry, [failed_list]} + end + end + + defp entry(pool_address) do + %{ + staking_address: pool_address, + retries_count: 0 + } + end + + defp fetch_from_blockchain(addresses) do + addresses + |> Enum.filter(&(&1.retries_count <= @max_retries)) + |> Enum.map(fn %{staking_address: staking_address} = pool -> + case PoolsReader.pool_data(staking_address) do + {:ok, data} -> + Map.merge(pool, data) + + error -> + Map.put(pool, :error, error) + end + end) + end + + defp import_pools(pools) do + {failed, success} = + Enum.reduce(pools, {[], []}, fn + %{error: _error, staking_address: address}, {failed, success} -> + {[address | failed], success} + + pool, {failed, success} -> + {failed, [changeset(pool) | success]} + end) + + import_params = %{ + staking_pools: %{params: success}, + timeout: :infinity + } + + case Chain.import(import_params) do + {:ok, _} -> + :ok + + {:error, reason} -> + Logger.debug(fn -> ["failed to import staking pools: ", inspect(reason)] end, + error_count: Enum.count(pools) + ) + end + + failed + end + + defp changeset(%{staking_address: staking_address} = pool) do + {:ok, mining_address} = Chain.Hash.Address.cast(pool[:mining_address]) + + data = + pool + |> Map.delete(:staking_address) + |> Map.put(:mining_address, mining_address) + + %{ + name: "anonymous", + primary: true, + address_hash: staking_address, + metadata: data + } + end +end diff --git a/apps/indexer/lib/indexer/supervisor.ex b/apps/indexer/lib/indexer/supervisor.ex index 113aa56576..c84a1b4460 100644 --- a/apps/indexer/lib/indexer/supervisor.ex +++ b/apps/indexer/lib/indexer/supervisor.ex @@ -19,7 +19,8 @@ defmodule Indexer.Supervisor do Token, TokenBalance, TokenUpdater, - UncleBlock + UncleBlock, + StakingPools } alias Indexer.Temporary.{ @@ -121,6 +122,7 @@ defmodule Indexer.Supervisor do {TokenBalance.Supervisor, [[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]}, {ReplacedTransaction.Supervisor, [[memory_monitor: memory_monitor]]}, + {StakingPools.Supervisor, [[memory_monitor: memory_monitor]]}, # Out-of-band fetchers {CoinBalanceOnDemand.Supervisor, [json_rpc_named_arguments]}, From 5acda9df9851c24b202909d804493dd733633d21 Mon Sep 17 00:00:00 2001 From: saneery Date: Mon, 22 Apr 2019 12:51:35 +0300 Subject: [PATCH 04/13] add staking pools importer --- apps/explorer/config/config.exs | 4 + .../chain/import/runner/staking_pools.ex | 88 +++++++++++++++++++ .../chain/import/stage/address_referencing.ex | 3 +- 3 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 apps/explorer/lib/explorer/chain/import/runner/staking_pools.ex diff --git a/apps/explorer/config/config.exs b/apps/explorer/config/config.exs index dea1be2b46..07e6224f15 100644 --- a/apps/explorer/config/config.exs +++ b/apps/explorer/config/config.exs @@ -51,6 +51,10 @@ else config :explorer, Explorer.Validator.MetadataProcessor, enabled: false end +config :explorer, Explorer.Staking.PoolsReader, + validators_contract_address: System.get_env("POS_VALIDATORS_CONTRACT"), + staking_contract_address: System.get_env("POS_STAKING_CONTRACT") + if System.get_env("SUPPLY_MODULE") == "TokenBridge" do config :explorer, supply: Explorer.Chain.Supply.TokenBridge end diff --git a/apps/explorer/lib/explorer/chain/import/runner/staking_pools.ex b/apps/explorer/lib/explorer/chain/import/runner/staking_pools.ex new file mode 100644 index 0000000000..aaf5d7242e --- /dev/null +++ b/apps/explorer/lib/explorer/chain/import/runner/staking_pools.ex @@ -0,0 +1,88 @@ +defmodule Explorer.Chain.Import.Runner.StakingPools do + @moduledoc """ + Bulk imports staking pools to Address.Name tabe. + """ + + require Ecto.Query + + alias Ecto.{Changeset, Multi, Repo} + alias Explorer.Chain.{Address, Import} + + import Ecto.Query, only: [from: 2] + + @behaviour Import.Runner + + # milliseconds + @timeout 60_000 + + @type imported :: [Address.Name.t()] + + @impl Import.Runner + def ecto_schema_module, do: Address.Name + + @impl Import.Runner + def option_key, do: :staking_pools + + @impl Import.Runner + def imported_table_row do + %{ + value_type: "[#{ecto_schema_module()}.t()]", + value_description: "List of `t:#{ecto_schema_module()}.t/0`s" + } + end + + @impl Import.Runner + def run(multi, changes_list, %{timestamps: timestamps} = options) do + insert_options = + options + |> Map.get(option_key(), %{}) + |> Map.take(~w(on_conflict timeout)a) + |> Map.put_new(:timeout, @timeout) + |> Map.put(:timestamps, timestamps) + + multi + |> Multi.run(:insert_staking_pools, fn repo, _ -> + insert(repo, changes_list, insert_options) + end) + end + + @impl Import.Runner + def timeout, do: @timeout + + @spec insert(Repo.t(), [map()], %{ + optional(:on_conflict) => Import.Runner.on_conflict(), + required(:timeout) => timeout, + required(:timestamps) => Import.timestamps() + }) :: + {:ok, [Address.Name.t()]} + | {:error, [Changeset.t()]} + defp insert(repo, changes_list, %{timeout: timeout, timestamps: timestamps} = options) when is_list(changes_list) do + on_conflict = Map.get_lazy(options, :on_conflict, &default_on_conflict/0) + + {:ok, _} = + Import.insert_changes_list( + repo, + changes_list, + conflict_target: {:unsafe_fragment, "(address_hash) where \"primary\" = true"}, + on_conflict: on_conflict, + for: Address.Name, + returning: [:address_hash], + timeout: timeout, + timestamps: timestamps + ) + end + + defp default_on_conflict do + from( + name in Address.Name, + update: [ + set: [ + name: fragment("EXCLUDED.name"), + metadata: fragment("EXCLUDED.metadata"), + inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", name.inserted_at), + updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", name.updated_at) + ] + ] + ) + end +end diff --git a/apps/explorer/lib/explorer/chain/import/stage/address_referencing.ex b/apps/explorer/lib/explorer/chain/import/stage/address_referencing.ex index f6aac010b0..0e42bdc1f9 100644 --- a/apps/explorer/lib/explorer/chain/import/stage/address_referencing.ex +++ b/apps/explorer/lib/explorer/chain/import/stage/address_referencing.ex @@ -24,7 +24,8 @@ defmodule Explorer.Chain.Import.Stage.AddressReferencing do Runner.Tokens, Runner.TokenTransfers, Runner.Address.CurrentTokenBalances, - Runner.Address.TokenBalances + Runner.Address.TokenBalances, + Runner.StakingPools ] @impl Stage From 40e03b7e5ccbd877b037b48b73a44a0cba117a15 Mon Sep 17 00:00:00 2001 From: saneery Date: Mon, 22 Apr 2019 14:18:50 +0300 Subject: [PATCH 05/13] add pools reader test --- .../explorer/staking/pools_reader_test.exs | 238 ++++++++++++++++++ 1 file changed, 238 insertions(+) create mode 100644 apps/explorer/test/explorer/staking/pools_reader_test.exs diff --git a/apps/explorer/test/explorer/staking/pools_reader_test.exs b/apps/explorer/test/explorer/staking/pools_reader_test.exs new file mode 100644 index 0000000000..10b9644e60 --- /dev/null +++ b/apps/explorer/test/explorer/staking/pools_reader_test.exs @@ -0,0 +1,238 @@ +defmodule Explorer.Token.PoolsReaderTest do + use EthereumJSONRPC.Case + use Explorer.DataCase + + alias Explorer.Staking.PoolsReader + + import Mox + + setup :verify_on_exit! + setup :set_mox_global + + describe "get_pools_list" do + test "get_active_pools success" do + get_pools_from_blockchain() + + result = PoolsReader.get_active_pools() + + assert Enum.count(result) == 3 + end + + test "get_active_pools error" do + fetch_from_blockchain_with_error() + + assert_raise MatchError, fn -> + PoolsReader.get_active_pools() + end + end + end + + describe "get_pools_data" do + test "get_pool_data success" do + get_pool_data_from_blockchain() + + address = <<11, 47, 94, 47, 60, 189, 134, 78, 170, 44, 100, 46, 55, 105, 193, 88, 35, 97, 202, 246>> + + response = { + :ok, + %{ + banned_unitil: 0, + delegator_addresses: [], + is_active: true, + is_banned: false, + is_validator: true, + mining_address: + <<187, 202, 168, 212, 130, 137, 187, 31, 252, 249, 128, 141, 154, 164, 177, 210, 21, 5, 76, 120>>, + staked_amount: 0, + staking_address: <<11, 47, 94, 47, 60, 189, 134, 78, 170, 44, 100, 46, 55, 105, 193, 88, 35, 97, 202, 246>>, + was_banned_count: 0, + was_validator_count: 2 + } + } + + assert response = PoolsReader.pool_data(address) + end + + test "get_pool_data error" do + fetch_from_blockchain_with_error() + + address = <<11, 47, 94, 47, 60, 189, 134, 78, 170, 44, 100, 46, 55, 105, 193, 88, 35, 97, 202, 246>> + + assert :error = PoolsReader.pool_data(address) + end + end + + defp get_pools_from_blockchain() do + expect( + EthereumJSONRPC.Mox, + :json_rpc, + fn [%{id: id, method: "eth_call", params: _}], _options -> + {:ok, + [ + %{ + id: id, + jsonrpc: "2.0", + result: + "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000b2f5e2f3cbd864eaa2c642e3769c1582361caf6000000000000000000000000aa94b687d3f9552a453b81b2834ca53778980dc0000000000000000000000000312c230e7d6db05224f60208a656e3541c5c42ba" + } + ]} + end + ) + end + + defp fetch_from_blockchain_with_error() do + expect( + EthereumJSONRPC.Mox, + :json_rpc, + fn [%{id: id, method: "eth_call", params: _}], _options -> + {:ok, + [ + %{ + error: %{code: -32015, data: "Reverted 0x", message: "VM execution error."}, + id: id, + jsonrpc: "2.0" + } + ]} + end + ) + end + + defp get_pool_data_from_blockchain() do + expect( + EthereumJSONRPC.Mox, + :json_rpc, + 9, + fn requests, _opts -> + {:ok, + Enum.map(requests, fn + # miningByStakingAddress + %{ + id: id, + method: "eth_call", + params: [ + %{data: "0x005351750000000000000000000000000b2f5e2f3cbd864eaa2c642e3769c1582361caf6", to: _}, + "latest" + ] + } -> + %{ + id: id, + result: "0x000000000000000000000000bbcaa8d48289bb1ffcf9808d9aa4b1d215054c78" + } + + # isPoolActive + %{ + id: id, + method: "eth_call", + params: [ + %{data: "0xa711e6a10000000000000000000000000b2f5e2f3cbd864eaa2c642e3769c1582361caf6", to: _}, + "latest" + ] + } -> + %{ + id: id, + result: "0x0000000000000000000000000000000000000000000000000000000000000001" + } + + # poolDelegators + %{ + id: id, + method: "eth_call", + params: [ + %{data: "0x9ea8082b0000000000000000000000000b2f5e2f3cbd864eaa2c642e3769c1582361caf6", to: _}, + "latest" + ] + } -> + %{ + id: id, + result: + "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000" + } + + # stakeAmountTotalMinusOrderedWithdraw + %{ + id: id, + method: "eth_call", + params: [ + %{data: "0x234fbf2b0000000000000000000000000b2f5e2f3cbd864eaa2c642e3769c1582361caf6", to: _}, + "latest" + ] + } -> + %{ + id: id, + result: "0x0000000000000000000000000000000000000000000000000000000000000000" + } + + # isValidator + %{ + id: id, + method: "eth_call", + params: [ + %{data: "0xfacd743b000000000000000000000000bbcaa8d48289bb1ffcf9808d9aa4b1d215054c78", to: _}, + "latest" + ] + } -> + %{ + id: id, + result: "0x0000000000000000000000000000000000000000000000000000000000000001" + } + + # validatorCounter + %{ + id: id, + method: "eth_call", + params: [ + %{data: "0xb41832e4000000000000000000000000bbcaa8d48289bb1ffcf9808d9aa4b1d215054c78", to: _}, + "latest" + ] + } -> + %{ + id: id, + result: "0x0000000000000000000000000000000000000000000000000000000000000002" + } + + # isValidatorBanned + %{ + id: id, + method: "eth_call", + params: [ + %{data: "0xa92252ae000000000000000000000000bbcaa8d48289bb1ffcf9808d9aa4b1d215054c78", to: _}, + "latest" + ] + } -> + %{ + id: id, + result: "0x0000000000000000000000000000000000000000000000000000000000000000" + } + + # bannedUntil + %{ + id: id, + method: "eth_call", + params: [ + %{data: "0x5836d08a000000000000000000000000bbcaa8d48289bb1ffcf9808d9aa4b1d215054c78", to: _}, + "latest" + ] + } -> + %{ + id: id, + result: "0x0000000000000000000000000000000000000000000000000000000000000000" + } + + # banCounter + %{ + id: id, + method: "eth_call", + params: [ + %{data: "0x1d0cd4c6000000000000000000000000bbcaa8d48289bb1ffcf9808d9aa4b1d215054c78", to: _}, + "latest" + ] + } -> + %{ + id: id, + result: "0x0000000000000000000000000000000000000000000000000000000000000000" + } + end)} + end + ) + end +end From fb522efd578fc3b376e0a691c7244c66efc4de4a Mon Sep 17 00:00:00 2001 From: saneery Date: Mon, 22 Apr 2019 14:36:53 +0300 Subject: [PATCH 06/13] add staking pools importer test --- .../lib/explorer/staking/pools_reader.ex | 3 +- .../import/runner/staking_pools_test.exs | 94 +++++++++++++++++++ .../explorer/staking/pools_reader_test.exs | 4 +- 3 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 apps/explorer/test/explorer/chain/import/runner/staking_pools_test.exs diff --git a/apps/explorer/lib/explorer/staking/pools_reader.ex b/apps/explorer/lib/explorer/staking/pools_reader.ex index 0aaffaf8e9..198730d183 100644 --- a/apps/explorer/lib/explorer/staking/pools_reader.ex +++ b/apps/explorer/lib/explorer/staking/pools_reader.ex @@ -26,6 +26,7 @@ defmodule Explorer.Staking.PoolsReader do with {:ok, [mining_address]} <- call_validators_method("miningByStakingAddress", [staking_address]), {:ok, [is_active]} <- call_staking_method("isPoolActive", [staking_address]), {:ok, [delegator_addresses]} <- call_staking_method("poolDelegators", [staking_address]), + delegators_count = Enum.count(delegator_addresses), {:ok, [staked_amount]} <- call_staking_method("stakeAmountTotalMinusOrderedWithdraw", [staking_address]), {:ok, [is_validator]} <- call_validators_method("isValidator", [mining_address]), {:ok, [was_validator_count]} <- call_validators_method("validatorCounter", [mining_address]), @@ -38,7 +39,7 @@ defmodule Explorer.Staking.PoolsReader do staking_address: staking_address, mining_address: mining_address, is_active: is_active, - delegator_addresses: delegator_addresses, + delegators_count: delegators_count, staked_amount: staked_amount, is_validator: is_validator, was_validator_count: was_validator_count, diff --git a/apps/explorer/test/explorer/chain/import/runner/staking_pools_test.exs b/apps/explorer/test/explorer/chain/import/runner/staking_pools_test.exs new file mode 100644 index 0000000000..d5bc6ecfca --- /dev/null +++ b/apps/explorer/test/explorer/chain/import/runner/staking_pools_test.exs @@ -0,0 +1,94 @@ +defmodule Explorer.Chain.Import.Runner.StakingPoolsTest do + use Explorer.DataCase + + alias Ecto.Multi + alias Explorer.Chain.Import.Runner.StakingPools + + describe "run/1" do + test "insert new pools list" do + pools = [ + %{ + address_hash: %Explorer.Chain.Hash{ + byte_count: 20, + bytes: <<11, 47, 94, 47, 60, 189, 134, 78, 170, 44, 100, 46, 55, 105, 193, 88, 35, 97, 202, 246>> + }, + metadata: %{ + banned_unitil: 0, + delegators_count: 0, + is_active: true, + is_banned: false, + is_validator: true, + mining_address: %Explorer.Chain.Hash{ + byte_count: 20, + bytes: <<187, 202, 168, 212, 130, 137, 187, 31, 252, 249, 128, 141, 154, 164, 177, 210, 21, 5, 76, 120>> + }, + retries_count: 1, + staked_amount: 0, + was_banned_count: 0, + was_validator_count: 1 + }, + name: "anonymous", + primary: true + }, + %{ + address_hash: %Explorer.Chain.Hash{ + byte_count: 20, + bytes: <<170, 148, 182, 135, 211, 249, 85, 42, 69, 59, 129, 178, 131, 76, 165, 55, 120, 152, 13, 192>> + }, + metadata: %{ + banned_unitil: 0, + delegators_count: 0, + is_active: true, + is_banned: false, + is_validator: true, + mining_address: %Explorer.Chain.Hash{ + byte_count: 20, + bytes: <<117, 223, 66, 56, 58, 254, 107, 245, 25, 74, 168, 250, 14, 155, 61, 95, 158, 134, 148, 65>> + }, + retries_count: 1, + staked_amount: 0, + was_banned_count: 0, + was_validator_count: 1 + }, + name: "anonymous", + primary: true + }, + %{ + address_hash: %Explorer.Chain.Hash{ + byte_count: 20, + bytes: <<49, 44, 35, 14, 125, 109, 176, 82, 36, 246, 2, 8, 166, 86, 227, 84, 28, 92, 66, 186>> + }, + metadata: %{ + banned_unitil: 0, + delegators_count: 0, + is_active: true, + is_banned: false, + is_validator: true, + mining_address: %Explorer.Chain.Hash{ + byte_count: 20, + bytes: <<82, 45, 243, 150, 174, 112, 160, 88, 189, 105, 119, 132, 8, 99, 15, 219, 2, 51, 137, 178>> + }, + retries_count: 1, + staked_amount: 0, + was_banned_count: 0, + was_validator_count: 1 + }, + name: "anonymous", + primary: true + } + ] + + assert {:ok, %{insert_staking_pools: list}} = run_changes(pools) + assert Enum.count(list) == Enum.count(pools) + end + end + + defp run_changes(changes) do + Multi.new() + |> StakingPools.run(changes, %{ + timeout: :infinity, + timestamps: %{inserted_at: DateTime.utc_now(), updated_at: DateTime.utc_now()} + }) + |> Repo.transaction() + end +end diff --git a/apps/explorer/test/explorer/staking/pools_reader_test.exs b/apps/explorer/test/explorer/staking/pools_reader_test.exs index 10b9644e60..7783b34648 100644 --- a/apps/explorer/test/explorer/staking/pools_reader_test.exs +++ b/apps/explorer/test/explorer/staking/pools_reader_test.exs @@ -37,7 +37,7 @@ defmodule Explorer.Token.PoolsReaderTest do :ok, %{ banned_unitil: 0, - delegator_addresses: [], + delegators_count: 0, is_active: true, is_banned: false, is_validator: true, @@ -50,7 +50,7 @@ defmodule Explorer.Token.PoolsReaderTest do } } - assert response = PoolsReader.pool_data(address) + assert PoolsReader.pool_data(address) == response end test "get_pool_data error" do From 009a54466e04bb0a5b24f0dc74d01f74acb8e010 Mon Sep 17 00:00:00 2001 From: saneery Date: Mon, 22 Apr 2019 16:02:47 +0300 Subject: [PATCH 07/13] fix staking pools fetcher --- apps/indexer/lib/indexer/fetcher/staking_pools.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/indexer/lib/indexer/fetcher/staking_pools.ex b/apps/indexer/lib/indexer/fetcher/staking_pools.ex index 246f53bdf7..4086c5c576 100644 --- a/apps/indexer/lib/indexer/fetcher/staking_pools.ex +++ b/apps/indexer/lib/indexer/fetcher/staking_pools.ex @@ -64,11 +64,11 @@ defmodule Indexer.Fetcher.StakingPools do if failed_list == [] do :ok else - {:retry, [failed_list]} + {:retry, failed_list} end end - defp entry(pool_address) do + def entry(pool_address) do %{ staking_address: pool_address, retries_count: 0 From b158d71219f8c6a716cc1139c2f624093b23ee20 Mon Sep 17 00:00:00 2001 From: saneery Date: Mon, 22 Apr 2019 16:09:30 +0300 Subject: [PATCH 08/13] add staking pools fetcher test --- .../lib/indexer/fetcher/staking_pools.ex | 8 +- .../indexer/fetcher/staking_pools_test.exs | 205 ++++++++++++++++++ 2 files changed, 209 insertions(+), 4 deletions(-) create mode 100644 apps/indexer/test/indexer/fetcher/staking_pools_test.exs diff --git a/apps/indexer/lib/indexer/fetcher/staking_pools.ex b/apps/indexer/lib/indexer/fetcher/staking_pools.ex index 4086c5c576..f380dc0e17 100644 --- a/apps/indexer/lib/indexer/fetcher/staking_pools.ex +++ b/apps/indexer/lib/indexer/fetcher/staking_pools.ex @@ -25,13 +25,13 @@ defmodule Indexer.Fetcher.StakingPools do @spec async_fetch() :: :ok def async_fetch() do - pools = - PoolsReader.get_pools() - |> Enum.map(&entry/1) - pid = GenServer.whereis(__MODULE__) if pid && Process.alive?(pid) do + pools = + PoolsReader.get_pools() + |> Enum.map(&entry/1) + BufferedTask.buffer(__MODULE__, pools, :infinity) end end diff --git a/apps/indexer/test/indexer/fetcher/staking_pools_test.exs b/apps/indexer/test/indexer/fetcher/staking_pools_test.exs new file mode 100644 index 0000000000..2172fca188 --- /dev/null +++ b/apps/indexer/test/indexer/fetcher/staking_pools_test.exs @@ -0,0 +1,205 @@ +defmodule Indexer.Fetcher.StakingPoolsTest do + use EthereumJSONRPC.Case + use Explorer.DataCase + + import Mox + + alias Indexer.Fetcher.StakingPools + alias Explorer.Staking.PoolsReader + alias Explorer.Chain.Address + + @moduletag :capture_log + + setup :verify_on_exit! + + describe "init/3" do + test "returns pools addresses" do + get_pools_from_blockchain(2) + + list = StakingPools.init([], &[&1 | &2], []) + + assert Enum.count(list) == 6 + end + end + + describe "run/3" do + test "one success import from pools" do + get_pools_from_blockchain(1) + + list = + PoolsReader.get_active_pools() + |> Enum.map(&StakingPools.entry/1) + + success_address = + list + |> List.first() + |> Map.get(:staking_address) + + get_pool_data_from_blockchain() + + assert {:retry, retry_list} = StakingPools.run(list, nil) + assert Enum.count(retry_list) == 2 + + pool = Explorer.Repo.get_by(Address.Name, address_hash: success_address) + assert pool.name == "anonymous" + end + end + + defp get_pools_from_blockchain(n) do + expect( + EthereumJSONRPC.Mox, + :json_rpc, + n, + fn [%{id: id, method: "eth_call", params: _}], _options -> + {:ok, + [ + %{ + id: id, + jsonrpc: "2.0", + result: + "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000b2f5e2f3cbd864eaa2c642e3769c1582361caf6000000000000000000000000aa94b687d3f9552a453b81b2834ca53778980dc0000000000000000000000000312c230e7d6db05224f60208a656e3541c5c42ba" + } + ]} + end + ) + end + + defp get_pool_data_from_blockchain() do + expect( + EthereumJSONRPC.Mox, + :json_rpc, + 11, + fn requests, _opts -> + {:ok, + Enum.map(requests, fn + # miningByStakingAddress + %{ + id: id, + method: "eth_call", + params: [ + %{data: "0x005351750000000000000000000000000b2f5e2f3cbd864eaa2c642e3769c1582361caf6", to: _}, + "latest" + ] + } -> + %{ + id: id, + result: "0x000000000000000000000000bbcaa8d48289bb1ffcf9808d9aa4b1d215054c78" + } + + # isPoolActive + %{ + id: id, + method: "eth_call", + params: [ + %{data: "0xa711e6a10000000000000000000000000b2f5e2f3cbd864eaa2c642e3769c1582361caf6", to: _}, + "latest" + ] + } -> + %{ + id: id, + result: "0x0000000000000000000000000000000000000000000000000000000000000001" + } + + # poolDelegators + %{ + id: id, + method: "eth_call", + params: [ + %{data: "0x9ea8082b0000000000000000000000000b2f5e2f3cbd864eaa2c642e3769c1582361caf6", to: _}, + "latest" + ] + } -> + %{ + id: id, + result: + "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000" + } + + # stakeAmountTotalMinusOrderedWithdraw + %{ + id: id, + method: "eth_call", + params: [ + %{data: "0x234fbf2b0000000000000000000000000b2f5e2f3cbd864eaa2c642e3769c1582361caf6", to: _}, + "latest" + ] + } -> + %{ + id: id, + result: "0x0000000000000000000000000000000000000000000000000000000000000000" + } + + # isValidator + %{ + id: id, + method: "eth_call", + params: [ + %{data: "0xfacd743b000000000000000000000000bbcaa8d48289bb1ffcf9808d9aa4b1d215054c78", to: _}, + "latest" + ] + } -> + %{ + id: id, + result: "0x0000000000000000000000000000000000000000000000000000000000000001" + } + + # validatorCounter + %{ + id: id, + method: "eth_call", + params: [ + %{data: "0xb41832e4000000000000000000000000bbcaa8d48289bb1ffcf9808d9aa4b1d215054c78", to: _}, + "latest" + ] + } -> + %{ + id: id, + result: "0x0000000000000000000000000000000000000000000000000000000000000002" + } + + # isValidatorBanned + %{ + id: id, + method: "eth_call", + params: [ + %{data: "0xa92252ae000000000000000000000000bbcaa8d48289bb1ffcf9808d9aa4b1d215054c78", to: _}, + "latest" + ] + } -> + %{ + id: id, + result: "0x0000000000000000000000000000000000000000000000000000000000000000" + } + + # bannedUntil + %{ + id: id, + method: "eth_call", + params: [ + %{data: "0x5836d08a000000000000000000000000bbcaa8d48289bb1ffcf9808d9aa4b1d215054c78", to: _}, + "latest" + ] + } -> + %{ + id: id, + result: "0x0000000000000000000000000000000000000000000000000000000000000000" + } + + # banCounter + %{ + id: id, + method: "eth_call", + params: [ + %{data: "0x1d0cd4c6000000000000000000000000bbcaa8d48289bb1ffcf9808d9aa4b1d215054c78", to: _}, + "latest" + ] + } -> + %{ + id: id, + result: "0x0000000000000000000000000000000000000000000000000000000000000000" + } + end)} + end + ) + end +end From fc79dfb9ebf7502f3b9364ef075e5e2d8e103c66 Mon Sep 17 00:00:00 2001 From: saneery Date: Mon, 22 Apr 2019 16:13:41 +0300 Subject: [PATCH 09/13] fix credo warnings --- apps/indexer/lib/indexer/block/fetcher.ex | 4 ++-- apps/indexer/lib/indexer/fetcher/staking_pools.ex | 4 ++-- apps/indexer/lib/indexer/supervisor.ex | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/indexer/lib/indexer/block/fetcher.ex b/apps/indexer/lib/indexer/block/fetcher.ex index 9f9a9474f4..299bf8f9e0 100644 --- a/apps/indexer/lib/indexer/block/fetcher.ex +++ b/apps/indexer/lib/indexer/block/fetcher.ex @@ -20,10 +20,10 @@ defmodule Indexer.Block.Fetcher do ContractCode, InternalTransaction, ReplacedTransaction, + StakingPools, Token, TokenBalance, - UncleBlock, - StakingPools + UncleBlock } alias Indexer.Tracer diff --git a/apps/indexer/lib/indexer/fetcher/staking_pools.ex b/apps/indexer/lib/indexer/fetcher/staking_pools.ex index f380dc0e17..e5affe0177 100644 --- a/apps/indexer/lib/indexer/fetcher/staking_pools.ex +++ b/apps/indexer/lib/indexer/fetcher/staking_pools.ex @@ -9,8 +9,8 @@ defmodule Indexer.Fetcher.StakingPools do require Logger alias Explorer.Chain - alias Indexer.BufferedTask alias Explorer.Staking.PoolsReader + alias Indexer.BufferedTask @behaviour BufferedTask @@ -24,7 +24,7 @@ defmodule Indexer.Fetcher.StakingPools do @max_retries 3 @spec async_fetch() :: :ok - def async_fetch() do + def async_fetch do pid = GenServer.whereis(__MODULE__) if pid && Process.alive?(pid) do diff --git a/apps/indexer/lib/indexer/supervisor.ex b/apps/indexer/lib/indexer/supervisor.ex index c84a1b4460..c6b8c4fff8 100644 --- a/apps/indexer/lib/indexer/supervisor.ex +++ b/apps/indexer/lib/indexer/supervisor.ex @@ -16,11 +16,11 @@ defmodule Indexer.Supervisor do InternalTransaction, PendingTransaction, ReplacedTransaction, + StakingPools, Token, TokenBalance, TokenUpdater, - UncleBlock, - StakingPools + UncleBlock } alias Indexer.Temporary.{ From 657958b0c99dc50718719952705ec112f182d73a Mon Sep 17 00:00:00 2001 From: saneery Date: Mon, 22 Apr 2019 16:22:47 +0300 Subject: [PATCH 10/13] fix sobelow --- apps/explorer/lib/explorer/staking/pools_reader.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/explorer/lib/explorer/staking/pools_reader.ex b/apps/explorer/lib/explorer/staking/pools_reader.ex index 198730d183..ed13408caa 100644 --- a/apps/explorer/lib/explorer/staking/pools_reader.ex +++ b/apps/explorer/lib/explorer/staking/pools_reader.ex @@ -76,6 +76,7 @@ defmodule Explorer.Staking.PoolsReader do Application.get_env(:explorer, __MODULE__, [])[key] end + # sobelow_skip ["Traversal"] defp abi(file_name) do :explorer |> Application.app_dir("priv/contracts_abi/pos/#{file_name}") From d18116f00ef029921b534755cd688fabe0a76b4c Mon Sep 17 00:00:00 2001 From: saneery Date: Mon, 22 Apr 2019 18:41:10 +0300 Subject: [PATCH 11/13] update readme and changelog --- CHANGELOG.md | 1 + apps/indexer/README.md | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aab08669f8..28c57149e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - [#1777](https://github.com/poanetwork/blockscout/pull/1777) - show ERC-20 token transfer info on transaction page - [#1770](https://github.com/poanetwork/blockscout/pull/1770) - set a websocket keepalive from config - [#1789](https://github.com/poanetwork/blockscout/pull/1789) - add ERC-721 info to transaction overview page +- [#1801](https://github.com/poanetwork/blockscout/pull/1801) - Staking pools fetching ### Fixes diff --git a/apps/indexer/README.md b/apps/indexer/README.md index ceb4ce9d0f..1c8507843b 100644 --- a/apps/indexer/README.md +++ b/apps/indexer/README.md @@ -57,6 +57,7 @@ The following async fetchers are launched for importing missing data: - `token_balance` - `token` - `contract_code` +- `staking_pools` ### Async fetchers @@ -78,6 +79,7 @@ Most of them are based off `BufferedTask`, and the basic algorithm goes like thi - `token_balance`: for `address_token_balances` with null `value_fetched_at`. Also upserts `address_current_token_balances` - `token`: for `tokens` with `cataloged == false` - `contract_code`: for `transactions` with non-null `created_contract_address_hash` and null `created_contract_code_indexed_at` +- `staking_pools`: for fetching staking pools Additionally: - `token_updater` is run every 2 days to update token metadata From 32498b5b298a7bfc0f1d29b43fb8760eb26a9ad4 Mon Sep 17 00:00:00 2001 From: saneery Date: Wed, 24 Apr 2019 10:22:07 +0300 Subject: [PATCH 12/13] fix typo --- apps/explorer/lib/explorer/staking/pools_reader.ex | 4 ++-- apps/explorer/test/explorer/staking/pools_reader_test.exs | 2 +- apps/indexer/lib/indexer/fetcher/staking_pools.ex | 7 ++++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/apps/explorer/lib/explorer/staking/pools_reader.ex b/apps/explorer/lib/explorer/staking/pools_reader.ex index ed13408caa..d0fd8ee679 100644 --- a/apps/explorer/lib/explorer/staking/pools_reader.ex +++ b/apps/explorer/lib/explorer/staking/pools_reader.ex @@ -31,7 +31,7 @@ defmodule Explorer.Staking.PoolsReader do {:ok, [is_validator]} <- call_validators_method("isValidator", [mining_address]), {:ok, [was_validator_count]} <- call_validators_method("validatorCounter", [mining_address]), {:ok, [is_banned]} <- call_validators_method("isValidatorBanned", [mining_address]), - {:ok, [banned_unitil]} <- call_validators_method("bannedUntil", [mining_address]), + {:ok, [banned_until]} <- call_validators_method("bannedUntil", [mining_address]), {:ok, [was_banned_count]} <- call_validators_method("banCounter", [mining_address]) do { :ok, @@ -44,7 +44,7 @@ defmodule Explorer.Staking.PoolsReader do is_validator: is_validator, was_validator_count: was_validator_count, is_banned: is_banned, - banned_unitil: banned_unitil, + banned_until: banned_until, was_banned_count: was_banned_count } } diff --git a/apps/explorer/test/explorer/staking/pools_reader_test.exs b/apps/explorer/test/explorer/staking/pools_reader_test.exs index 7783b34648..f1d5164c3c 100644 --- a/apps/explorer/test/explorer/staking/pools_reader_test.exs +++ b/apps/explorer/test/explorer/staking/pools_reader_test.exs @@ -36,7 +36,7 @@ defmodule Explorer.Token.PoolsReaderTest do response = { :ok, %{ - banned_unitil: 0, + banned_until: 0, delegators_count: 0, is_active: true, is_banned: false, diff --git a/apps/indexer/lib/indexer/fetcher/staking_pools.ex b/apps/indexer/lib/indexer/fetcher/staking_pools.ex index e5affe0177..68794d9ee4 100644 --- a/apps/indexer/lib/indexer/fetcher/staking_pools.ex +++ b/apps/indexer/lib/indexer/fetcher/staking_pools.ex @@ -11,6 +11,7 @@ defmodule Indexer.Fetcher.StakingPools do alias Explorer.Chain alias Explorer.Staking.PoolsReader alias Indexer.BufferedTask + alias Indexer.Fetcher.StakingPools.Supervisor, as: StakingPoolsSupervisor @behaviour BufferedTask @@ -25,9 +26,9 @@ defmodule Indexer.Fetcher.StakingPools do @spec async_fetch() :: :ok def async_fetch do - pid = GenServer.whereis(__MODULE__) - - if pid && Process.alive?(pid) do + if StakingPoolsSupervisor.disabled?() do + :ok + else pools = PoolsReader.get_pools() |> Enum.map(&entry/1) From eb4154f14e93357b7034214c08141aedc6084f93 Mon Sep 17 00:00:00 2001 From: saneery Date: Thu, 25 Apr 2019 11:52:53 +0300 Subject: [PATCH 13/13] batching pool data requests --- .../lib/explorer/staking/pools_reader.ex | 51 ++++++++++++++++--- .../explorer/staking/pools_reader_test.exs | 2 +- .../indexer/fetcher/staking_pools_test.exs | 2 +- 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/apps/explorer/lib/explorer/staking/pools_reader.ex b/apps/explorer/lib/explorer/staking/pools_reader.ex index d0fd8ee679..de03ff10b5 100644 --- a/apps/explorer/lib/explorer/staking/pools_reader.ex +++ b/apps/explorer/lib/explorer/staking/pools_reader.ex @@ -24,15 +24,16 @@ defmodule Explorer.Staking.PoolsReader do @spec pool_data(String.t()) :: {:ok, map()} | :error def pool_data(staking_address) do with {:ok, [mining_address]} <- call_validators_method("miningByStakingAddress", [staking_address]), - {:ok, [is_active]} <- call_staking_method("isPoolActive", [staking_address]), - {:ok, [delegator_addresses]} <- call_staking_method("poolDelegators", [staking_address]), + data = fetch_data(staking_address, mining_address), + {:ok, [is_active]} <- data["isPoolActive"], + {:ok, [delegator_addresses]} <- data["poolDelegators"], delegators_count = Enum.count(delegator_addresses), - {:ok, [staked_amount]} <- call_staking_method("stakeAmountTotalMinusOrderedWithdraw", [staking_address]), - {:ok, [is_validator]} <- call_validators_method("isValidator", [mining_address]), - {:ok, [was_validator_count]} <- call_validators_method("validatorCounter", [mining_address]), - {:ok, [is_banned]} <- call_validators_method("isValidatorBanned", [mining_address]), - {:ok, [banned_until]} <- call_validators_method("bannedUntil", [mining_address]), - {:ok, [was_banned_count]} <- call_validators_method("banCounter", [mining_address]) do + {:ok, [staked_amount]} <- data["stakeAmountTotalMinusOrderedWithdraw"], + {:ok, [is_validator]} <- data["isValidator"], + {:ok, [was_validator_count]} <- data["validatorCounter"], + {:ok, [is_banned]} <- data["isValidatorBanned"], + {:ok, [banned_until]} <- data["bannedUntil"], + {:ok, [was_banned_count]} <- data["banCounter"] do { :ok, %{ @@ -72,6 +73,40 @@ defmodule Explorer.Staking.PoolsReader do resp end + defp fetch_data(staking_address, mining_address) do + contract_abi = abi("staking.json") ++ abi("validators.json") + + methods = [ + {:staking, "isPoolActive", staking_address}, + {:staking, "poolDelegators", staking_address}, + {:staking, "stakeAmountTotalMinusOrderedWithdraw", staking_address}, + {:validators, "isValidator", mining_address}, + {:validators, "validatorCounter", mining_address}, + {:validators, "isValidatorBanned", mining_address}, + {:validators, "bannedUntil", mining_address}, + {:validators, "banCounter", mining_address} + ] + + methods + |> Enum.map(&format_request/1) + |> Reader.query_contracts(contract_abi) + |> Enum.zip(methods) + |> Enum.into(%{}, fn {response, {_, function_name, _}} -> + {function_name, response} + end) + end + + defp format_request({contract_name, function_name, param}) do + %{ + contract_address: contract(contract_name), + function_name: function_name, + args: [param] + } + end + + defp contract(:staking), do: config(:staking_contract_address) + defp contract(:validators), do: config(:validators_contract_address) + defp config(key) do Application.get_env(:explorer, __MODULE__, [])[key] end diff --git a/apps/explorer/test/explorer/staking/pools_reader_test.exs b/apps/explorer/test/explorer/staking/pools_reader_test.exs index f1d5164c3c..ac6a600722 100644 --- a/apps/explorer/test/explorer/staking/pools_reader_test.exs +++ b/apps/explorer/test/explorer/staking/pools_reader_test.exs @@ -101,7 +101,7 @@ defmodule Explorer.Token.PoolsReaderTest do expect( EthereumJSONRPC.Mox, :json_rpc, - 9, + 2, fn requests, _opts -> {:ok, Enum.map(requests, fn diff --git a/apps/indexer/test/indexer/fetcher/staking_pools_test.exs b/apps/indexer/test/indexer/fetcher/staking_pools_test.exs index 2172fca188..8f985537bf 100644 --- a/apps/indexer/test/indexer/fetcher/staking_pools_test.exs +++ b/apps/indexer/test/indexer/fetcher/staking_pools_test.exs @@ -68,7 +68,7 @@ defmodule Indexer.Fetcher.StakingPoolsTest do expect( EthereumJSONRPC.Mox, :json_rpc, - 11, + 4, fn requests, _opts -> {:ok, Enum.map(requests, fn