diff --git a/apps/block_scout_web/assets/css/components/_stakes_table.scss b/apps/block_scout_web/assets/css/components/_stakes_table.scss
index 84322f9463..da5cdf1a86 100644
--- a/apps/block_scout_web/assets/css/components/_stakes_table.scss
+++ b/apps/block_scout_web/assets/css/components/_stakes_table.scss
@@ -114,6 +114,12 @@ $stakes-table-cell-separation: 25px !default;
background-color: $stakes-banned-background;
color: $stakes-banned-color;
}
+
+ .stakes-td-banned-info {
+ display: block;
+ float: left;
+ padding-right: 30px;
+ }
}
.stakes-td-order {
diff --git a/apps/block_scout_web/assets/css/components/stakes/_modal_validator_info.scss b/apps/block_scout_web/assets/css/components/stakes/_modal_validator_info.scss
index 3df5db9001..b0514b55bc 100644
--- a/apps/block_scout_web/assets/css/components/stakes/_modal_validator_info.scss
+++ b/apps/block_scout_web/assets/css/components/stakes/_modal_validator_info.scss
@@ -47,3 +47,13 @@
padding: 0;
}
}
+
+.modal-validator-alert {
+ padding: 10px 30px;
+ background-color: #fff3f7;
+ color: #ff7986;
+ border-top: 1px solid #fee6ef;
+ border-bottom: 1px solid #fee6ef;
+ line-height: 1.5;
+ font-size: 14px;
+}
diff --git a/apps/block_scout_web/assets/css/components/stakes/_stakes.scss b/apps/block_scout_web/assets/css/components/stakes/_stakes.scss
index 7ed7a7b3a7..86ccaa7b86 100644
--- a/apps/block_scout_web/assets/css/components/stakes/_stakes.scss
+++ b/apps/block_scout_web/assets/css/components/stakes/_stakes.scss
@@ -95,6 +95,7 @@ $stakes-stats-item-border-color: #fff !default;
display: flex;
justify-content: flex-end;
padding-right: 30px;
+ float: left;
}
.stakes-control {
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 54902c1764..bcbd3309a4 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
@@ -36,12 +36,14 @@ defmodule BlockScoutWeb.StakesChannel do
def handle_in("render_validator_info", %{"address" => staking_address}, socket) do
pool = Chain.staking_pool(staking_address)
+ delegator = socket.assigns[:account] && Chain.staking_pool_delegator(staking_address, socket.assigns.account)
average_block_time = AverageBlockTime.average_block_time()
token = ContractState.get(:token)
html =
View.render_to_string(StakesView, "_stakes_modal_validator_info.html",
validator: pool,
+ delegator: delegator,
average_block_time: average_block_time,
token: token
)
diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/stakes_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/stakes_controller.ex
index 749d2edc87..f28519102f 100644
--- a/apps/block_scout_web/lib/block_scout_web/controllers/stakes_controller.ex
+++ b/apps/block_scout_web/lib/block_scout_web/controllers/stakes_controller.ex
@@ -92,6 +92,7 @@ defmodule BlockScoutWeb.StakesController do
"_rows.html",
token: token,
pool: pool,
+ delegator: delegator,
index: index,
average_block_time: average_block_time,
pools_type: filter,
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/stakes/_rows.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/stakes/_rows.html.eex
index 866164cdcb..6eeaeb990f 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/stakes/_rows.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/stakes/_rows.html.eex
@@ -34,9 +34,20 @@
<%= if @pool.is_banned do %>
- Banned until block #<%= @pool.banned_until %> (<%= estimated_unban_day(@pool.banned_until, @average_block_time) %>)
+ Banned until block #<%= @pool.banned_until %> (<%= estimated_unban_day(@pool.banned_until, @average_block_time) %>).
+ <%= if @delegator &&
+ @delegator.delegator_address_hash != @pool.staking_address_hash &&
+ @pool.are_delegators_banned &&
+ @pool.banned_until != @pool.banned_delegators_until do %>
+ You will be able to withdraw after block #<%= @pool.banned_delegators_until %> (<%= estimated_unban_day(@pool.banned_delegators_until, @average_block_time) %>)
+ <% end %>
- <% else %>
+ <% end %>
+ <%= if !@pool.is_banned ||
+ (!@pool.are_delegators_banned &&
+ @delegator &&
+ @delegator.delegator_address_hash != @pool.staking_address_hash
+ ) do %>
<%= if @buttons.move do %>
<%= render BlockScoutWeb.StakesView, "_stakes_control_move.html", address: @pool.staking_address_hash %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/stakes/_stakes_modal_validator_info.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/stakes/_stakes_modal_validator_info.html.eex
index ee7d64c309..69691b472b 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/stakes/_stakes_modal_validator_info.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/stakes/_stakes_modal_validator_info.html.eex
@@ -10,6 +10,19 @@
+ <%= if @validator.is_banned do %>
+
+ This pool is banned until block <%= "##{@validator.banned_until} (#{estimated_unban_day(@validator.banned_until, @average_block_time)})" %>
+ Reason for Ban: <%= String.capitalize(@validator.ban_reason) %>
+ <%= if @delegator &&
+ @delegator.delegator_address_hash != @validator.staking_address_hash &&
+ @validator.are_delegators_banned &&
+ @validator.banned_until != @validator.banned_delegators_until do %>
+
+ You will be able to withdraw after block #<%= @validator.banned_delegators_until %> (<%= estimated_unban_day(@validator.banned_delegators_until, @average_block_time) %>)
+ <% end %>
+
+ <% end %>
<%= render BlockScoutWeb.CommonComponentsView, "_modal_close_button.html" %>
<%=
@@ -48,12 +61,6 @@
title: "How Many Times this Address has been Banned",
value: @validator.was_banned_count
%>
- <%=
- render BlockScoutWeb.StakesView,
- "_stakes_validator_info_item.html",
- title: "The Block Number and Approximate Date When the Address Will be Unbanned",
- value: if(@validator.is_banned, do: "##{@validator.banned_until} (#{estimated_unban_day(@validator.banned_until, @average_block_time)})")
- %>
<%=
render BlockScoutWeb.StakesView,
"_stakes_validator_info_item.html",
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 0666aab2fb..566fd58987 100644
--- a/apps/explorer/lib/explorer/chain/import/runner/staking_pools.ex
+++ b/apps/explorer/lib/explorer/chain/import/runner/staking_pools.ex
@@ -128,15 +128,18 @@ defmodule Explorer.Chain.Import.Runner.StakingPools do
is_banned: fragment("EXCLUDED.is_banned"),
is_validator: fragment("EXCLUDED.is_validator"),
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"),
staked_ratio: fragment("EXCLUDED.staked_ratio"),
self_staked_amount: fragment("EXCLUDED.self_staked_amount"),
staked_amount: fragment("EXCLUDED.staked_amount"),
+ ban_reason: fragment("EXCLUDED.ban_reason"),
was_banned_count: fragment("EXCLUDED.was_banned_count"),
was_validator_count: fragment("EXCLUDED.was_validator_count"),
is_deleted: fragment("EXCLUDED.is_deleted"),
banned_until: fragment("EXCLUDED.banned_until"),
+ banned_delegators_until: fragment("EXCLUDED.banned_delegators_until"),
inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", pool.inserted_at),
updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", pool.updated_at)
]
diff --git a/apps/explorer/lib/explorer/chain/staking_pool.ex b/apps/explorer/lib/explorer/chain/staking_pool.ex
index f3ceefe424..f7e10ba144 100644
--- a/apps/explorer/lib/explorer/chain/staking_pool.ex
+++ b/apps/explorer/lib/explorer/chain/staking_pool.ex
@@ -15,17 +15,20 @@ defmodule Explorer.Chain.StakingPool do
@type t :: %__MODULE__{
staking_address_hash: Hash.Address.t(),
mining_address_hash: Hash.Address.t(),
- banned_until: boolean,
+ banned_until: integer,
+ banned_delegators_until: integer,
delegators_count: integer,
is_active: boolean,
is_banned: boolean,
is_validator: boolean,
is_unremovable: boolean,
+ are_delegators_banned: boolean,
likelihood: Decimal.t(),
block_reward_ratio: Decimal.t(),
staked_ratio: Decimal.t(),
self_staked_amount: Decimal.t(),
staked_amount: Decimal.t(),
+ ban_reason: String.t(),
was_banned_count: integer,
was_validator_count: integer,
is_deleted: boolean
@@ -33,7 +36,7 @@ defmodule Explorer.Chain.StakingPool do
@attrs ~w(
is_active delegators_count staked_amount self_staked_amount is_validator
- was_validator_count is_banned was_banned_count banned_until likelihood
+ was_validator_count is_banned are_delegators_banned ban_reason was_banned_count banned_until banned_delegators_until likelihood
staked_ratio staking_address_hash mining_address_hash block_reward_ratio
is_unremovable
)a
@@ -45,16 +48,19 @@ defmodule Explorer.Chain.StakingPool do
schema "staking_pools" do
field(:banned_until, :integer)
+ field(:banned_delegators_until, :integer)
field(:delegators_count, :integer)
field(:is_active, :boolean, default: false)
field(:is_banned, :boolean, default: false)
field(:is_validator, :boolean, default: false)
field(:is_unremovable, :boolean, default: false)
+ field(:are_delegators_banned, :boolean, default: false)
field(:likelihood, :decimal)
field(:block_reward_ratio, :decimal)
field(:staked_ratio, :decimal)
field(:self_staked_amount, :decimal)
field(:staked_amount, :decimal)
+ field(:ban_reason, :string)
field(:was_banned_count, :integer)
field(:was_validator_count, :integer)
field(:is_deleted, :boolean, default: false)
diff --git a/apps/explorer/lib/explorer/staking/contract_reader.ex b/apps/explorer/lib/explorer/staking/contract_reader.ex
index a579a07dc4..8c8271eab6 100644
--- a/apps/explorer/lib/explorer/staking/contract_reader.ex
+++ b/apps/explorer/lib/explorer/staking/contract_reader.ex
@@ -41,7 +41,10 @@ defmodule Explorer.Staking.ContractReader do
[
was_validator_count: {:validator_set, "validatorCounter", [mining_address]},
is_banned: {:validator_set, "isValidatorBanned", [mining_address]},
+ are_delegators_banned: {:validator_set, "areDelegatorsBanned", [mining_address]},
+ ban_reason: {:validator_set, "banReason", [mining_address]},
banned_until: {:validator_set, "bannedUntil", [mining_address]},
+ banned_delegators_until: {:validator_set, "bannedDelegatorsUntil", [mining_address]},
was_banned_count: {:validator_set, "banCounter", [mining_address]}
]
end
diff --git a/apps/explorer/lib/explorer/staking/contract_state.ex b/apps/explorer/lib/explorer/staking/contract_state.ex
index 41f0fd08d1..1941c82374 100644
--- a/apps/explorer/lib/explorer/staking/contract_state.ex
+++ b/apps/explorer/lib/explorer/staking/contract_state.ex
@@ -186,7 +186,8 @@ defmodule Explorer.Staking.ContractState do
block_reward_ratio: staking_response.block_reward / 10_000,
is_deleted: false,
is_validator: is_validator[staking_response.mining_address_hash] || false,
- is_unremovable: hash_to_string(staking_address) == unremovable_validator
+ is_unremovable: hash_to_string(staking_address) == unremovable_validator,
+ ban_reason: binary_to_string(mining_response.ban_reason)
}
|> Map.merge(
Map.take(staking_response, [
@@ -200,7 +201,9 @@ defmodule Explorer.Staking.ContractState do
Map.take(mining_response, [
:was_validator_count,
:is_banned,
+ :are_delegators_banned,
:banned_until,
+ :banned_delegators_until,
:was_banned_count
])
)
@@ -318,4 +321,11 @@ defmodule Explorer.Staking.ContractState do
|> File.read!()
|> Jason.decode!()
end
+
+ defp binary_to_string(binary) do
+ binary
+ |> :binary.bin_to_list()
+ |> Enum.filter(fn x -> x != 0 end)
+ |> List.to_string()
+ end
end
diff --git a/apps/explorer/priv/contracts_abi/posdao/ValidatorSetAuRa.json b/apps/explorer/priv/contracts_abi/posdao/ValidatorSetAuRa.json
index df164b450c..3269bd5c9f 100644
--- a/apps/explorer/priv/contracts_abi/posdao/ValidatorSetAuRa.json
+++ b/apps/explorer/priv/contracts_abi/posdao/ValidatorSetAuRa.json
@@ -126,6 +126,25 @@
"stateMutability": "view",
"type": "function"
},
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "name": "banReason",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bytes32"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
{
"constant": true,
"inputs": [
@@ -145,6 +164,25 @@
"stateMutability": "view",
"type": "function"
},
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "name": "bannedDelegatorsUntil",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
{
"constant": true,
"inputs": [],
@@ -630,6 +668,25 @@
"stateMutability": "view",
"type": "function"
},
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "_miningAddress",
+ "type": "address"
+ }
+ ],
+ "name": "areDelegatorsBanned",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
{
"constant": true,
"inputs": [
diff --git a/apps/explorer/priv/repo/migrations/20190904170014_add_additional_ban_fields_to_staking_pools.exs b/apps/explorer/priv/repo/migrations/20190904170014_add_additional_ban_fields_to_staking_pools.exs
new file mode 100644
index 0000000000..b0a6d1b929
--- /dev/null
+++ b/apps/explorer/priv/repo/migrations/20190904170014_add_additional_ban_fields_to_staking_pools.exs
@@ -0,0 +1,11 @@
+defmodule Explorer.Repo.Migrations.AddAdditionalBanFieldsToStakingPools do
+ use Ecto.Migration
+
+ def change do
+ alter table(:staking_pools) do
+ add(:are_delegators_banned, :boolean, default: false)
+ add(:ban_reason, :string)
+ add(:banned_delegators_until, :bigint)
+ end
+ end
+end
diff --git a/apps/explorer/test/explorer/staking/contract_state_test.exs b/apps/explorer/test/explorer/staking/contract_state_test.exs
index 2d3065b91a..96bd39b051 100644
--- a/apps/explorer/test/explorer/staking/contract_state_test.exs
+++ b/apps/explorer/test/explorer/staking/contract_state_test.exs
@@ -149,7 +149,7 @@ defmodule Explorer.Staking.ContractStateTest do
EthereumJSONRPC.Mox,
:json_rpc,
fn requests, _opts ->
- assert length(requests) == 16
+ assert length(requests) == 28
{:ok,
format_responses([
@@ -157,17 +157,29 @@ defmodule Explorer.Staking.ContractStateTest do
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000002",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000000",
"0x000000000000000000000000000000000000000000000000000000000000004a",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000000",
"0x000000000000000000000000000000000000000000000000000000000000004a",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000"
])}
end
|