From a3c5acd351771a2f78d93b209dceaacd9d85ed97 Mon Sep 17 00:00:00 2001 From: Vadim Date: Thu, 28 Nov 2019 11:39:06 +0300 Subject: [PATCH] Add retrieving of validatorMinRewardPercent --- .../channels/stakes_channel.ex | 4 +- .../_stakes_modal_delegators_list.html.eex | 2 +- .../stakes/_stakes_modal_pool_info.html.eex | 2 +- .../chain/import/runner/staking_pools.ex | 2 +- .../lib/explorer/chain/staking_pool.ex | 6 +- .../lib/explorer/staking/contract_reader.ex | 8 +- .../lib/explorer/staking/contract_state.ex | 80 +++++++++++-------- .../explorer/staking/stake_snapshotting.ex | 48 ++++++----- .../contracts_abi/posdao/BlockRewardAuRa.json | 19 +++++ ...190718175620_add_block_reward_to_pools.exs | 9 --- ..._add_validator_reward_percent_to_pools.exs | 9 +++ 11 files changed, 116 insertions(+), 73 deletions(-) delete mode 100644 apps/explorer/priv/repo/migrations/20190718175620_add_block_reward_to_pools.exs create mode 100644 apps/explorer/priv/repo/migrations/20190718175620_add_validator_reward_percent_to_pools.exs diff --git a/apps/block_scout_web/lib/block_scout_web/channels/stakes_channel.ex b/apps/block_scout_web/lib/block_scout_web/channels/stakes_channel.ex index 7c11511d6d..501070492f 100644 --- a/apps/block_scout_web/lib/block_scout_web/channels/stakes_channel.ex +++ b/apps/block_scout_web/lib/block_scout_web/channels/stakes_channel.ex @@ -54,6 +54,7 @@ defmodule BlockScoutWeb.StakesChannel do def handle_in("render_delegators_list", %{"address" => pool_staking_address}, socket) do pool = Chain.staking_pool(pool_staking_address) token = ContractState.get(:token) + validator_min_reward_percent = ContractState.get(:validator_min_reward_percent) show_snapshotted_data = pool.is_validator && ContractState.get(:validator_set_apply_block) > 0 && ContractState.get(:is_snapshotted) @@ -78,7 +79,8 @@ defmodule BlockScoutWeb.StakesChannel do conn: socket, stakers: stakers, token: token, - show_snapshotted_data: show_snapshotted_data + show_snapshotted_data: show_snapshotted_data, + validator_min_reward_percent: validator_min_reward_percent ) {:reply, {:ok, %{html: html}}, socket} diff --git a/apps/block_scout_web/lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex index bb9824bf7b..da30ad3f04 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex @@ -50,7 +50,7 @@ render BlockScoutWeb.StakesView, "_stakes_th.html", title: reward_col_title, - tooltip: gettext("Reward distribution is based on stake amount. Validator receives a minimum of 30%.") + tooltip: gettext("Reward distribution is based on stake amount. Validator receives a minimum of %{min}%.", min: @validator_min_reward_percent) %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/stakes/_stakes_modal_pool_info.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/stakes/_stakes_modal_pool_info.html.eex index ece74027c6..674a443ee6 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/stakes/_stakes_modal_pool_info.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/stakes/_stakes_modal_pool_info.html.eex @@ -47,7 +47,7 @@ render BlockScoutWeb.StakesView, "_stakes_validator_info_item.html", title: gettext("Share of Pool’s Reward"), - value: "#{@validator.block_reward_ratio}%" + value: "#{@validator.validator_reward_percent}%" %> <%= render BlockScoutWeb.StakesView, diff --git a/apps/explorer/lib/explorer/chain/import/runner/staking_pools.ex b/apps/explorer/lib/explorer/chain/import/runner/staking_pools.ex index 6caf7d3849..60e11e50ad 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/staking_pools.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/staking_pools.ex @@ -130,7 +130,7 @@ defmodule Explorer.Chain.Import.Runner.StakingPools do is_unremovable: fragment("EXCLUDED.is_unremovable"), are_delegators_banned: fragment("EXCLUDED.are_delegators_banned"), likelihood: fragment("EXCLUDED.likelihood"), - block_reward_ratio: fragment("EXCLUDED.block_reward_ratio"), + validator_reward_percent: fragment("EXCLUDED.validator_reward_percent"), stakes_ratio: fragment("EXCLUDED.stakes_ratio"), validator_reward_ratio: fragment("EXCLUDED.validator_reward_ratio"), self_staked_amount: fragment("EXCLUDED.self_staked_amount"), diff --git a/apps/explorer/lib/explorer/chain/staking_pool.ex b/apps/explorer/lib/explorer/chain/staking_pool.ex index 92b97bde5f..c7ee0ecaeb 100644 --- a/apps/explorer/lib/explorer/chain/staking_pool.ex +++ b/apps/explorer/lib/explorer/chain/staking_pool.ex @@ -24,7 +24,7 @@ defmodule Explorer.Chain.StakingPool do is_unremovable: boolean, are_delegators_banned: boolean, likelihood: Decimal.t(), - block_reward_ratio: Decimal.t(), + validator_reward_percent: Decimal.t(), stakes_ratio: Decimal.t(), validator_reward_ratio: Decimal.t(), snapshotted_validator_reward_ratio: Decimal.t(), @@ -41,7 +41,7 @@ defmodule Explorer.Chain.StakingPool do @attrs ~w( is_active delegators_count total_staked_amount self_staked_amount snapshotted_total_staked_amount snapshotted_self_staked_amount is_validator was_validator_count is_banned are_delegators_banned ban_reason was_banned_count banned_until banned_delegators_until likelihood - stakes_ratio validator_reward_ratio snapshotted_validator_reward_ratio staking_address_hash mining_address_hash block_reward_ratio + stakes_ratio validator_reward_ratio snapshotted_validator_reward_ratio staking_address_hash mining_address_hash validator_reward_percent is_unremovable )a @req_attrs ~w( @@ -60,7 +60,7 @@ defmodule Explorer.Chain.StakingPool do field(:is_unremovable, :boolean, default: false) field(:are_delegators_banned, :boolean, default: false) field(:likelihood, :decimal) - field(:block_reward_ratio, :decimal) + field(:validator_reward_percent, :decimal) field(:stakes_ratio, :decimal) field(:validator_reward_ratio, :decimal) field(:snapshotted_validator_reward_ratio, :decimal) diff --git a/apps/explorer/lib/explorer/staking/contract_reader.ex b/apps/explorer/lib/explorer/staking/contract_reader.ex index d41460abc2..d93128f999 100644 --- a/apps/explorer/lib/explorer/staking/contract_reader.ex +++ b/apps/explorer/lib/explorer/staking/contract_reader.ex @@ -28,7 +28,7 @@ defmodule Explorer.Staking.ContractReader do def pool_staking_requests(staking_address) do [ active_delegators: {:staking, "poolDelegators", [staking_address]}, - block_reward: {:block_reward, "validatorRewardPercent", [staking_address]}, + validator_reward_percent: {:block_reward, "validatorRewardPercent", [staking_address]}, inactive_delegators: {:staking, "poolDelegatorsInactive", [staking_address]}, is_active: {:staking, "isPoolActive", [staking_address]}, mining_address_hash: {:validator_set, "miningByStakingAddress", [staking_address]}, @@ -65,6 +65,12 @@ defmodule Explorer.Staking.ContractReader do ] end + def validator_min_reward_percent_request(epoch_number) do + [ + value: {:block_reward, "validatorMinRewardPercent", [epoch_number]} + ] + end + # args = [staking_epoch, validator_staked, total_staked, pool_reward \\ 10_00000] def validator_reward_requests(args) do [ diff --git a/apps/explorer/lib/explorer/staking/contract_state.ex b/apps/explorer/lib/explorer/staking/contract_state.ex index 4de7e6ac52..3676cb6c82 100644 --- a/apps/explorer/lib/explorer/staking/contract_state.ex +++ b/apps/explorer/lib/explorer/staking/contract_state.ex @@ -28,6 +28,7 @@ defmodule Explorer.Staking.ContractState do :validator_set_contract, :block_reward_contract, :validator_set_apply_block, + :validator_min_reward_percent, :is_snapshotted ] @@ -123,6 +124,9 @@ defmodule Explorer.Staking.ContractState do # read general info from the contracts (including pool list and validator list) global_responses = ContractReader.perform_requests(ContractReader.global_requests(), contracts, abi) token = get_token(global_responses.token_contract_address) + validator_min_reward_percent = ContractReader.perform_requests( + ContractReader.validator_min_reward_percent_request(global_responses.epoch_number), contracts, abi + ).value start_snapshotting = (global_responses.epoch_start_block == block_number + 1) is_validator = Enum.into(global_responses.validators, %{}, &{hash_to_string(&1), true}) @@ -143,6 +147,7 @@ defmodule Explorer.Staking.ContractState do ]) |> Map.to_list() |> Enum.concat(token: token) + |> Enum.concat(validator_min_reward_percent: validator_min_reward_percent) :ets.insert(@table_name, settings) @@ -199,75 +204,79 @@ defmodule Explorer.Staking.ContractState do # get amounts for each of the stakers staker_responses = stakers - |> Enum.map(fn {pool_staking_address, staker_address, _} -> + |> Enum.map(fn {pool_staking_address, staker_address, _is_active} -> ContractReader.staker_requests(pool_staking_address, staker_address) end) |> ContractReader.perform_grouped_requests(stakers, contracts, abi) - # calculate total amount staked into all active pools - staked_total = Enum.sum(for {_, pool} <- pool_staking_responses, pool.is_active, do: pool.total_staked_amount) - - [likelihood_values, total_likelihood] = global_responses.pools_likelihood - - likelihood = - global_responses.pools_to_be_elected # array of pool addresses (staking addresses) - |> Enum.zip(likelihood_values) - |> Enum.into(%{}) - + # to keep sort order pool_staking_keys = Enum.map(pool_staking_responses, fn {key, _} -> key end) candidate_reward_responses = pool_staking_responses - |> Enum.map(fn {_address, response} -> + |> Enum.map(fn {_pool_staking_address, resp} -> ContractReader.validator_reward_requests([ global_responses.epoch_number, - response.self_staked_amount, - response.total_staked_amount, + resp.self_staked_amount, + resp.total_staked_amount, 1000_000 ]) end) |> ContractReader.perform_grouped_requests(pool_staking_keys, contracts, abi) + # to keep sort order delegator_keys = Enum.map(staker_responses, fn {key, _} -> key end) delegator_reward_responses = staker_responses - |> Enum.map(fn {{pool_address, _, _}, response} -> - staking_response = pool_staking_responses[pool_address] + |> Enum.map(fn {{pool_staking_address, _staker_address, _is_active}, resp} -> + staking_resp = pool_staking_responses[pool_staking_address] ContractReader.delegator_reward_requests([ global_responses.epoch_number, - response.stake_amount, - staking_response.self_staked_amount, - staking_response.total_staked_amount, + resp.stake_amount, + staking_resp.self_staked_amount, + staking_resp.total_staked_amount, 1000_000 ]) end) |> ContractReader.perform_grouped_requests(delegator_keys, contracts, abi) + # calculate total amount staked into all active pools + staked_total = Enum.sum(for {_, pool} <- pool_staking_responses, pool.is_active, do: pool.total_staked_amount) + + # calculate likelihood of becoming a validator on the next epoch + [likelihood_values, total_likelihood] = global_responses.pools_likelihood + + likelihood = + global_responses.pools_to_be_elected # array of pool addresses (staking addresses) + |> Enum.zip(likelihood_values) + |> Enum.into(%{}) + + # form entries for writing to the `staking_pools` table in DB pool_entries = - Enum.map(pools, fn staking_address -> - staking_response = pool_staking_responses[staking_address] - mining_response = pool_mining_responses[staking_address] - candidate_reward_response = candidate_reward_responses[staking_address] + Enum.map(pools, fn pool_staking_address -> + staking_resp = pool_staking_responses[pool_staking_address] + mining_resp = pool_mining_responses[pool_staking_address] + candidate_reward_resp = candidate_reward_responses[pool_staking_address] %{ - staking_address_hash: staking_address, - delegators_count: length(staking_response.active_delegators), + staking_address_hash: pool_staking_address, + delegators_count: length(staking_resp.active_delegators), stakes_ratio: - if staking_response.is_active do - ratio(staking_response.total_staked_amount, staked_total) + if staking_resp.is_active do + ratio(staking_resp.total_staked_amount, staked_total) end, - validator_reward_ratio: Float.floor(candidate_reward_response.validator_share / 10_000, 2), - likelihood: ratio(likelihood[staking_address] || 0, total_likelihood), - block_reward_ratio: staking_response.block_reward / 10_000, + validator_reward_ratio: Float.floor(candidate_reward_resp.validator_share / 10_000, 2), + likelihood: ratio(likelihood[pool_staking_address] || 0, total_likelihood), + validator_reward_percent: staking_resp.validator_reward_percent / 10_000, is_deleted: false, - is_validator: is_validator[staking_response.mining_address_hash] || false, - is_unremovable: hash_to_string(staking_address) == unremovable_validator, - ban_reason: binary_to_string(mining_response.ban_reason) + is_validator: is_validator[staking_resp.mining_address_hash] || false, + is_unremovable: hash_to_string(pool_staking_address) == unremovable_validator, + ban_reason: binary_to_string(mining_resp.ban_reason) } |> Map.merge( - Map.take(staking_response, [ + Map.take(staking_resp, [ :is_active, :mining_address_hash, :self_staked_amount, @@ -275,7 +284,7 @@ defmodule Explorer.Staking.ContractState do ]) ) |> Map.merge( - Map.take(mining_response, [ + Map.take(mining_resp, [ :are_delegators_banned, :banned_delegators_until, :banned_until, @@ -286,6 +295,7 @@ defmodule Explorer.Staking.ContractState do ) end) + # form entries for writing to the `staking_pools_delegators` table in DB delegator_entries = Enum.map(staker_responses, fn {{pool_address, delegator_address, is_active}, response} -> delegator_reward_response = delegator_reward_responses[{pool_address, delegator_address, is_active}] diff --git a/apps/explorer/lib/explorer/staking/stake_snapshotting.ex b/apps/explorer/lib/explorer/staking/stake_snapshotting.ex index 13f2ef7ffe..5f126ec30d 100644 --- a/apps/explorer/lib/explorer/staking/stake_snapshotting.ex +++ b/apps/explorer/lib/explorer/staking/stake_snapshotting.ex @@ -97,9 +97,12 @@ defmodule Explorer.Staking.StakeSnapshotting do |> Enum.zip(stakers) |> Map.new(fn {key, val} -> {val, key} end) + # to keep sort order + pool_staking_keys = Enum.map(pool_staking_responses, fn {key, _} -> key end) + validator_reward_responses = pool_staking_responses - |> Enum.map(fn {_address, resp} -> + |> Enum.map(fn {_pool_staking_address, resp} -> ContractReader.validator_reward_requests([ epoch_number, resp.snapshotted_self_staked_amount, @@ -107,36 +110,39 @@ defmodule Explorer.Staking.StakeSnapshotting do 1000_000 ]) end) - |> ContractReader.perform_grouped_requests(pool_staking_addresses, contracts, abi) + |> ContractReader.perform_grouped_requests(pool_staking_keys, contracts, abi) + + # to keep sort order + delegator_keys = Enum.map(staker_responses, fn {key, _} -> key end) delegator_reward_responses = staker_responses - |> Enum.map(fn {{pool_address, _delegator_address}, response} -> - staking_response = pool_staking_responses[pool_address] + |> Enum.map(fn {{pool_staking_address, _staker_address}, resp} -> + staking_resp = pool_staking_responses[pool_staking_address] ContractReader.delegator_reward_requests([ epoch_number, - response.snapshotted_stake_amount, - staking_response.snapshotted_self_staked_amount, - staking_response.snapshotted_total_staked_amount, + resp.snapshotted_stake_amount, + staking_resp.snapshotted_self_staked_amount, + staking_resp.snapshotted_total_staked_amount, 1000_000 ]) end) - |> ContractReader.perform_grouped_requests(stakers, contracts, abi) + |> ContractReader.perform_grouped_requests(delegator_keys, contracts, abi) pool_entries = - Enum.map(pool_staking_addresses, fn staking_address -> - staking_response = pool_staking_responses[staking_address] - mining_response = pool_mining_responses[staking_address] - validator_reward_response = validator_reward_responses[staking_address] + Enum.map(pool_staking_addresses, fn pool_staking_address -> + staking_resp = pool_staking_responses[pool_staking_address] + mining_resp = pool_mining_responses[pool_staking_address] + validator_reward_resp = validator_reward_responses[pool_staking_address] %{ - staking_address_hash: staking_address, - delegators_count: length(staking_response.active_delegators), - snapshotted_validator_reward_ratio: Float.floor(validator_reward_response.validator_share / 10_000, 2) + staking_address_hash: pool_staking_address, + delegators_count: length(staking_resp.active_delegators), + snapshotted_validator_reward_ratio: Float.floor(validator_reward_resp.validator_share / 10_000, 2) } |> Map.merge( - Map.take(staking_response, [ + Map.take(staking_resp, [ :mining_address_hash, :self_staked_amount, :snapshotted_self_staked_amount, @@ -145,7 +151,7 @@ defmodule Explorer.Staking.StakeSnapshotting do ]) ) |> Map.merge( - Map.take(mining_response, [ + Map.take(mining_resp, [ :banned_until, :was_banned_count, :was_validator_count @@ -154,13 +160,13 @@ defmodule Explorer.Staking.StakeSnapshotting do end) delegator_entries = - Enum.map(staker_responses, fn {{pool_staking_address, staker_address}, response} -> - delegator_reward_response = delegator_reward_responses[{pool_staking_address, staker_address}] + Enum.map(staker_responses, fn {{pool_staking_address, staker_address}, resp} -> + delegator_reward_resp = delegator_reward_responses[{pool_staking_address, staker_address}] - Map.merge(response, %{ + Map.merge(resp, %{ address_hash: staker_address, staking_address_hash: pool_staking_address, - snapshotted_reward_ratio: Float.floor(delegator_reward_response.delegator_share / 10_000, 2) + snapshotted_reward_ratio: Float.floor(delegator_reward_resp.delegator_share / 10_000, 2) }) end) diff --git a/apps/explorer/priv/contracts_abi/posdao/BlockRewardAuRa.json b/apps/explorer/priv/contracts_abi/posdao/BlockRewardAuRa.json index 6cabc8bcb2..234873d325 100644 --- a/apps/explorer/priv/contracts_abi/posdao/BlockRewardAuRa.json +++ b/apps/explorer/priv/contracts_abi/posdao/BlockRewardAuRa.json @@ -181,6 +181,25 @@ "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": [], diff --git a/apps/explorer/priv/repo/migrations/20190718175620_add_block_reward_to_pools.exs b/apps/explorer/priv/repo/migrations/20190718175620_add_block_reward_to_pools.exs deleted file mode 100644 index b84376ab6e..0000000000 --- a/apps/explorer/priv/repo/migrations/20190718175620_add_block_reward_to_pools.exs +++ /dev/null @@ -1,9 +0,0 @@ -defmodule Explorer.Repo.Migrations.AddBlockRewardToPools do - use Ecto.Migration - - def change do - alter table(:staking_pools) do - add(:block_reward_ratio, :decimal, precision: 5, scale: 2) - end - end -end diff --git a/apps/explorer/priv/repo/migrations/20190718175620_add_validator_reward_percent_to_pools.exs b/apps/explorer/priv/repo/migrations/20190718175620_add_validator_reward_percent_to_pools.exs new file mode 100644 index 0000000000..6a8e698e23 --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20190718175620_add_validator_reward_percent_to_pools.exs @@ -0,0 +1,9 @@ +defmodule Explorer.Repo.Migrations.AddValidatorRewardPercentToPools do + use Ecto.Migration + + def change do + alter table(:staking_pools) do + add(:validator_reward_percent, :decimal, precision: 5, scale: 2) + end + end +end