-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1801 from poanetwork/pools_fetching
Staking pools fetching
- Loading branch information
Showing
19 changed files
with
2,320 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
88 changes: 88 additions & 0 deletions
88
apps/explorer/lib/explorer/chain/import/runner/staking_pools.ex
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
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]), | ||
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]} <- 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, | ||
%{ | ||
staking_address: staking_address, | ||
mining_address: mining_address, | ||
is_active: is_active, | ||
delegators_count: delegators_count, | ||
staked_amount: staked_amount, | ||
is_validator: is_validator, | ||
was_validator_count: was_validator_count, | ||
is_banned: is_banned, | ||
banned_until: banned_until, | ||
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 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 | ||
|
||
# sobelow_skip ["Traversal"] | ||
defp abi(file_name) do | ||
:explorer | ||
|> Application.app_dir("priv/contracts_abi/pos/#{file_name}") | ||
|> File.read!() | ||
|> Jason.decode!() | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
Oops, something went wrong.