|
|
@ -28,6 +28,7 @@ defmodule BlockScoutWeb.StakesChannel do |
|
|
|
# apps/block_scout_web/lib/block_scout_web/endpoint.ex |
|
|
|
# apps/block_scout_web/lib/block_scout_web/endpoint.ex |
|
|
|
def terminate(_reason, socket) do |
|
|
|
def terminate(_reason, socket) do |
|
|
|
s = socket.assigns[@claim_reward_long_op] |
|
|
|
s = socket.assigns[@claim_reward_long_op] |
|
|
|
|
|
|
|
|
|
|
|
if s != nil do |
|
|
|
if s != nil do |
|
|
|
:ets.delete(ContractState, claim_reward_long_op_key(s.staker)) |
|
|
|
:ets.delete(ContractState, claim_reward_long_op_key(s.staker)) |
|
|
|
end |
|
|
|
end |
|
|
@ -251,55 +252,60 @@ defmodule BlockScoutWeb.StakesChannel do |
|
|
|
|
|
|
|
|
|
|
|
def handle_in("render_claim_reward", data, socket) do |
|
|
|
def handle_in("render_claim_reward", data, socket) do |
|
|
|
staker = socket.assigns[:account] |
|
|
|
staker = socket.assigns[:account] |
|
|
|
staking_contract_address = try do ContractState.get(:staking_contract).address after end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cond do |
|
|
|
staking_contract_address = |
|
|
|
claim_reward_long_op_active(socket) == true -> |
|
|
|
try do |
|
|
|
{:reply, {:error, %{reason: gettext("Pools searching is already in progress for this address")}}, socket} |
|
|
|
ContractState.get(:staking_contract).address |
|
|
|
staker == nil || staker == "" || staker == "0x0000000000000000000000000000000000000000" -> |
|
|
|
after |
|
|
|
{:reply, {:error, %{reason: gettext("Unknown staker address. Please, choose your account in MetaMask")}}, socket} |
|
|
|
|
|
|
|
staking_contract_address == nil || staking_contract_address == "" || staking_contract_address == "0x0000000000000000000000000000000000000000" -> |
|
|
|
|
|
|
|
{:reply, {:error, %{reason: gettext("Unknown address of Staking contract. Please, contact support")}}, socket} |
|
|
|
|
|
|
|
true -> |
|
|
|
|
|
|
|
result = if data["preload"] do |
|
|
|
|
|
|
|
%{ |
|
|
|
|
|
|
|
html: View.render_to_string(StakesView, "_stakes_modal_claim_reward.html", %{}), |
|
|
|
|
|
|
|
socket: socket |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
task = Task.async(__MODULE__, :find_claim_reward_pools, [socket, staker, staking_contract_address]) |
|
|
|
|
|
|
|
%{ |
|
|
|
|
|
|
|
html: "OK", |
|
|
|
|
|
|
|
socket: assign(socket, @claim_reward_long_op, %{task: task, staker: staker}) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
{:reply, {:ok, %{html: result.html}}, result.socket} |
|
|
|
empty_staker = staker == nil || staker == "" || staker == "0x0000000000000000000000000000000000000000" |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
empty_staking_contract_address = |
|
|
|
|
|
|
|
staking_contract_address == nil || staking_contract_address == "" || |
|
|
|
|
|
|
|
staking_contract_address == "0x0000000000000000000000000000000000000000" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
handle_in_render_claim_reward_result( |
|
|
|
|
|
|
|
socket, |
|
|
|
|
|
|
|
data, |
|
|
|
|
|
|
|
staker, |
|
|
|
|
|
|
|
staking_contract_address, |
|
|
|
|
|
|
|
empty_staker, |
|
|
|
|
|
|
|
empty_staking_contract_address |
|
|
|
|
|
|
|
) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
def handle_in("recalc_claim_reward", data, socket) do |
|
|
|
def handle_in("recalc_claim_reward", data, socket) do |
|
|
|
epochs = data["epochs"] |
|
|
|
epochs = data["epochs"] |
|
|
|
pool_staking_address = data["pool_staking_address"] |
|
|
|
pool_staking_address = data["pool_staking_address"] |
|
|
|
staker = socket.assigns[:account] |
|
|
|
staker = socket.assigns[:account] |
|
|
|
staking_contract_address = try do ContractState.get(:staking_contract).address after end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cond do |
|
|
|
staking_contract_address = |
|
|
|
claim_reward_long_op_active(socket) == true -> |
|
|
|
try do |
|
|
|
{:reply, {:error, %{reason: gettext("Reward calculating is already in progress for this address")}}, socket} |
|
|
|
ContractState.get(:staking_contract).address |
|
|
|
Enum.count(epochs) == 0 -> |
|
|
|
after |
|
|
|
{:reply, {:error, %{reason: gettext("Staking epochs are not specified or not in the allowed range")}}, socket} |
|
|
|
|
|
|
|
pool_staking_address == nil || pool_staking_address == "" || pool_staking_address == "0x0000000000000000000000000000000000000000" -> |
|
|
|
|
|
|
|
{:reply, {:error, %{reason: gettext("Unknown pool staking address. Please, contact support")}}, socket} |
|
|
|
|
|
|
|
staker == nil || staker == "" || staker == "0x0000000000000000000000000000000000000000" -> |
|
|
|
|
|
|
|
{:reply, {:error, %{reason: gettext("Unknown staker address. Please, choose your account in MetaMask")}}, socket} |
|
|
|
|
|
|
|
staking_contract_address == nil || staking_contract_address == "" || staking_contract_address == "0x0000000000000000000000000000000000000000" -> |
|
|
|
|
|
|
|
{:reply, {:error, %{reason: gettext("Unknown address of Staking contract. Please, contact support")}}, socket} |
|
|
|
|
|
|
|
true -> |
|
|
|
|
|
|
|
task = Task.async(__MODULE__, :recalc_claim_reward, [socket, staking_contract_address, epochs, pool_staking_address, staker]) |
|
|
|
|
|
|
|
socket = assign(socket, @claim_reward_long_op, %{task: task, staker: staker}) |
|
|
|
|
|
|
|
{:reply, {:ok, %{html: "OK"}}, socket} |
|
|
|
|
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
empty_pool_staking_address = |
|
|
|
|
|
|
|
pool_staking_address == nil || pool_staking_address == "" || |
|
|
|
|
|
|
|
pool_staking_address == "0x0000000000000000000000000000000000000000" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
empty_staker = staker == nil || staker == "" || staker == "0x0000000000000000000000000000000000000000" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
empty_staking_contract_address = |
|
|
|
|
|
|
|
staking_contract_address == nil || staking_contract_address == "" || |
|
|
|
|
|
|
|
staking_contract_address == "0x0000000000000000000000000000000000000000" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
handle_in_recalc_claim_reward_result( |
|
|
|
|
|
|
|
socket, |
|
|
|
|
|
|
|
epochs, |
|
|
|
|
|
|
|
staking_contract_address, |
|
|
|
|
|
|
|
pool_staking_address, |
|
|
|
|
|
|
|
staker, |
|
|
|
|
|
|
|
empty_pool_staking_address, |
|
|
|
|
|
|
|
empty_staking_contract_address, |
|
|
|
|
|
|
|
empty_staker |
|
|
|
|
|
|
|
) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
def handle_in("render_claim_withdrawal", %{"address" => staking_address}, socket) do |
|
|
|
def handle_in("render_claim_withdrawal", %{"address" => staking_address}, socket) do |
|
|
@ -325,12 +331,15 @@ defmodule BlockScoutWeb.StakesChannel do |
|
|
|
|
|
|
|
|
|
|
|
def handle_info({:DOWN, ref, :process, pid, _reason}, socket) do |
|
|
|
def handle_info({:DOWN, ref, :process, pid, _reason}, socket) do |
|
|
|
s = socket.assigns[@claim_reward_long_op] |
|
|
|
s = socket.assigns[@claim_reward_long_op] |
|
|
|
socket = if s && s.task.ref == ref && s.task.pid == pid do |
|
|
|
|
|
|
|
|
|
|
|
socket = |
|
|
|
|
|
|
|
if s && s.task.ref == ref && s.task.pid == pid do |
|
|
|
:ets.delete(ContractState, claim_reward_long_op_key(s.staker)) |
|
|
|
:ets.delete(ContractState, claim_reward_long_op_key(s.staker)) |
|
|
|
assign(socket, @claim_reward_long_op, nil) |
|
|
|
assign(socket, @claim_reward_long_op, nil) |
|
|
|
else |
|
|
|
else |
|
|
|
socket |
|
|
|
socket |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
{:noreply, socket} |
|
|
|
{:noreply, socket} |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
@ -339,7 +348,8 @@ defmodule BlockScoutWeb.StakesChannel do |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
def handle_out("staking_update", data, socket) do |
|
|
|
def handle_out("staking_update", data, socket) do |
|
|
|
dont_refresh_page = case Map.fetch(data, :dont_refresh_page) do |
|
|
|
dont_refresh_page = |
|
|
|
|
|
|
|
case Map.fetch(data, :dont_refresh_page) do |
|
|
|
{:ok, value} -> value |
|
|
|
{:ok, value} -> value |
|
|
|
_ -> false |
|
|
|
_ -> false |
|
|
|
end |
|
|
|
end |
|
|
@ -360,23 +370,32 @@ defmodule BlockScoutWeb.StakesChannel do |
|
|
|
|
|
|
|
|
|
|
|
def find_claim_reward_pools(socket, staker, staking_contract_address) do |
|
|
|
def find_claim_reward_pools(socket, staker, staking_contract_address) do |
|
|
|
:ets.insert(ContractState, {claim_reward_long_op_key(staker), true}) |
|
|
|
:ets.insert(ContractState, {claim_reward_long_op_key(staker), true}) |
|
|
|
|
|
|
|
|
|
|
|
try do |
|
|
|
try do |
|
|
|
json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) |
|
|
|
json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) |
|
|
|
staking_contract = ContractState.get(:staking_contract) |
|
|
|
staking_contract = ContractState.get(:staking_contract) |
|
|
|
|
|
|
|
|
|
|
|
responses = |
|
|
|
responses = |
|
|
|
ContractReader.get_staker_pools_length_request(staker) |
|
|
|
staker |
|
|
|
|
|
|
|
|> ContractReader.get_staker_pools_length_request() |
|
|
|
|> ContractReader.perform_requests(%{staking: staking_contract.address}, staking_contract.abi) |
|
|
|
|> ContractReader.perform_requests(%{staking: staking_contract.address}, staking_contract.abi) |
|
|
|
|
|
|
|
|
|
|
|
staker_pools_length = responses[:length] |
|
|
|
staker_pools_length = responses[:length] |
|
|
|
|
|
|
|
|
|
|
|
chunk_size = 100 |
|
|
|
chunk_size = 100 |
|
|
|
pools = if staker_pools_length > 0 do |
|
|
|
|
|
|
|
|
|
|
|
pools = |
|
|
|
|
|
|
|
if staker_pools_length > 0 do |
|
|
|
chunks = 0..trunc(ceil(staker_pools_length / chunk_size) - 1) |
|
|
|
chunks = 0..trunc(ceil(staker_pools_length / chunk_size) - 1) |
|
|
|
|
|
|
|
|
|
|
|
Enum.reduce(chunks, [], fn i, acc -> |
|
|
|
Enum.reduce(chunks, [], fn i, acc -> |
|
|
|
responses = |
|
|
|
responses = |
|
|
|
ContractReader.get_staker_pools_request(staker, i * chunk_size, chunk_size) |
|
|
|
staker |
|
|
|
|
|
|
|
|> ContractReader.get_staker_pools_request(i * chunk_size, chunk_size) |
|
|
|
|> ContractReader.perform_requests(%{staking: staking_contract.address}, staking_contract.abi) |
|
|
|
|> ContractReader.perform_requests(%{staking: staking_contract.address}, staking_contract.abi) |
|
|
|
acc ++ Enum.map(responses[:pools], fn pool_staking_address -> |
|
|
|
|
|
|
|
|
|
|
|
acc ++ |
|
|
|
|
|
|
|
Enum.map(responses[:pools], fn pool_staking_address -> |
|
|
|
address_bytes_to_string(pool_staking_address) |
|
|
|
address_bytes_to_string(pool_staking_address) |
|
|
|
end) |
|
|
|
end) |
|
|
|
end) |
|
|
|
end) |
|
|
@ -384,7 +403,8 @@ defmodule BlockScoutWeb.StakesChannel do |
|
|
|
[] |
|
|
|
[] |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
pools_amounts = Enum.map(pools, fn pool_staking_address -> |
|
|
|
pools_amounts = |
|
|
|
|
|
|
|
Enum.map(pools, fn pool_staking_address -> |
|
|
|
ContractReader.call_get_reward_amount( |
|
|
|
ContractReader.call_get_reward_amount( |
|
|
|
staking_contract_address, |
|
|
|
staking_contract_address, |
|
|
|
[], |
|
|
|
[], |
|
|
@ -394,14 +414,37 @@ defmodule BlockScoutWeb.StakesChannel do |
|
|
|
) |
|
|
|
) |
|
|
|
end) |
|
|
|
end) |
|
|
|
|
|
|
|
|
|
|
|
error = Enum.find_value(pools_amounts, fn result -> |
|
|
|
error = |
|
|
|
|
|
|
|
Enum.find_value(pools_amounts, fn result -> |
|
|
|
case result do |
|
|
|
case result do |
|
|
|
{:error, reason} -> error_reason_to_string(reason) |
|
|
|
{:error, reason} -> error_reason_to_string(reason) |
|
|
|
_ -> nil |
|
|
|
_ -> nil |
|
|
|
end |
|
|
|
end |
|
|
|
end) |
|
|
|
end) |
|
|
|
|
|
|
|
|
|
|
|
{error, pools} = if error != nil do |
|
|
|
{error, pools} = |
|
|
|
|
|
|
|
get_pools(pools_amounts, pools, staking_contract_address, staker, json_rpc_named_arguments, error) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
html = |
|
|
|
|
|
|
|
View.render_to_string( |
|
|
|
|
|
|
|
StakesView, |
|
|
|
|
|
|
|
"_stakes_modal_claim_reward_content.html", |
|
|
|
|
|
|
|
coin: %Token{symbol: Explorer.coin(), decimals: Decimal.new(18)}, |
|
|
|
|
|
|
|
error: error, |
|
|
|
|
|
|
|
pools: pools, |
|
|
|
|
|
|
|
token: ContractState.get(:token) |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
push(socket, "claim_reward_pools", %{ |
|
|
|
|
|
|
|
html: html |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
after |
|
|
|
|
|
|
|
:ets.delete(ContractState, claim_reward_long_op_key(staker)) |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_pools(pools_amounts, pools, staking_contract_address, staker, json_rpc_named_arguments, error) do |
|
|
|
|
|
|
|
if error != nil do |
|
|
|
{error, %{}} |
|
|
|
{error, %{}} |
|
|
|
else |
|
|
|
else |
|
|
|
block_reward_contract = ContractState.get(:block_reward_contract) |
|
|
|
block_reward_contract = ContractState.get(:block_reward_contract) |
|
|
@ -413,40 +456,52 @@ defmodule BlockScoutWeb.StakesChannel do |
|
|
|
|> Enum.filter(fn {amounts, _} -> amounts.token_reward_sum > 0 || amounts.native_reward_sum > 0 end) |
|
|
|
|> Enum.filter(fn {amounts, _} -> amounts.token_reward_sum > 0 || amounts.native_reward_sum > 0 end) |
|
|
|
|> Enum.map(fn {amounts, pool_staking_address} -> |
|
|
|
|> Enum.map(fn {amounts, pool_staking_address} -> |
|
|
|
responses = |
|
|
|
responses = |
|
|
|
ContractReader.epochs_to_claim_reward_from_request(pool_staking_address, staker) |
|
|
|
pool_staking_address |
|
|
|
|> ContractReader.perform_requests(%{block_reward: block_reward_contract.address}, block_reward_contract.abi) |
|
|
|
|> ContractReader.epochs_to_claim_reward_from_request(staker) |
|
|
|
|
|
|
|
|> ContractReader.perform_requests( |
|
|
|
|
|
|
|
%{block_reward: block_reward_contract.address}, |
|
|
|
|
|
|
|
block_reward_contract.abi |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
epochs = |
|
|
|
epochs = |
|
|
|
array_to_ranges(responses[:epochs]) |
|
|
|
responses[:epochs] |
|
|
|
|
|
|
|
|> array_to_ranges() |
|
|
|
|> Enum.map(fn {first, last} -> |
|
|
|
|> Enum.map(fn {first, last} -> |
|
|
|
Integer.to_string(first) <> (if first != last, do: "-" <> Integer.to_string(last), else: "") |
|
|
|
Integer.to_string(first) <> if first != last, do: "-" <> Integer.to_string(last), else: "" |
|
|
|
end) |
|
|
|
end) |
|
|
|
|
|
|
|
|
|
|
|
data = Map.put(amounts, :epochs, Enum.join(epochs, ",")) |
|
|
|
data = Map.put(amounts, :epochs, Enum.join(epochs, ",")) |
|
|
|
|
|
|
|
|
|
|
|
{data, pool_staking_address} |
|
|
|
{data, pool_staking_address} |
|
|
|
end) |
|
|
|
end) |
|
|
|
|> Enum.filter(fn {data, _} -> data.epochs != "" end) |
|
|
|
|> Enum.filter(fn {data, _} -> data.epochs != "" end) |
|
|
|
|
|
|
|
|
|
|
|
pools_gas_estimates = Enum.map(pools, fn {_data, pool_staking_address} -> |
|
|
|
pools_gas_estimates = |
|
|
|
result = ContractReader.claim_reward_estimate_gas( |
|
|
|
Enum.map(pools, fn {_data, pool_staking_address} -> |
|
|
|
|
|
|
|
result = |
|
|
|
|
|
|
|
ContractReader.claim_reward_estimate_gas( |
|
|
|
staking_contract_address, |
|
|
|
staking_contract_address, |
|
|
|
[], |
|
|
|
[], |
|
|
|
pool_staking_address, |
|
|
|
pool_staking_address, |
|
|
|
staker, |
|
|
|
staker, |
|
|
|
json_rpc_named_arguments |
|
|
|
json_rpc_named_arguments |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
{pool_staking_address, result} |
|
|
|
{pool_staking_address, result} |
|
|
|
end) |
|
|
|
end) |
|
|
|
|
|
|
|
|
|
|
|
error = Enum.find_value(pools_gas_estimates, fn {_, result} -> |
|
|
|
error = |
|
|
|
|
|
|
|
Enum.find_value(pools_gas_estimates, fn {_, result} -> |
|
|
|
case result do |
|
|
|
case result do |
|
|
|
{:error, reason} -> error_reason_to_string(reason) |
|
|
|
{:error, reason} -> error_reason_to_string(reason) |
|
|
|
_ -> nil |
|
|
|
_ -> nil |
|
|
|
end |
|
|
|
end |
|
|
|
end) |
|
|
|
end) |
|
|
|
|
|
|
|
|
|
|
|
pools = if error == nil do |
|
|
|
pools = |
|
|
|
|
|
|
|
if error == nil do |
|
|
|
pools_gas_estimates = Map.new(pools_gas_estimates) |
|
|
|
pools_gas_estimates = Map.new(pools_gas_estimates) |
|
|
|
|
|
|
|
|
|
|
|
Map.new(pools, fn {data, pool_staking_address} -> |
|
|
|
Map.new(pools, fn {data, pool_staking_address} -> |
|
|
|
{:ok, estimate} = pools_gas_estimates[pool_staking_address] |
|
|
|
{:ok, estimate} = pools_gas_estimates[pool_staking_address] |
|
|
|
data = Map.put(data, :gas_estimate, estimate) |
|
|
|
data = Map.put(data, :gas_estimate, estimate) |
|
|
@ -458,30 +513,16 @@ defmodule BlockScoutWeb.StakesChannel do |
|
|
|
|
|
|
|
|
|
|
|
{error, pools} |
|
|
|
{error, pools} |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
html = View.render_to_string( |
|
|
|
|
|
|
|
StakesView, |
|
|
|
|
|
|
|
"_stakes_modal_claim_reward_content.html", |
|
|
|
|
|
|
|
coin: %Token{symbol: Explorer.coin(), decimals: Decimal.new(18)}, |
|
|
|
|
|
|
|
error: error, |
|
|
|
|
|
|
|
pools: pools, |
|
|
|
|
|
|
|
token: ContractState.get(:token) |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
push(socket, "claim_reward_pools", %{ |
|
|
|
|
|
|
|
html: html |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
after |
|
|
|
|
|
|
|
:ets.delete(ContractState, claim_reward_long_op_key(staker)) |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
def recalc_claim_reward(socket, staking_contract_address, epochs, pool_staking_address, staker) do |
|
|
|
def recalc_claim_reward(socket, staking_contract_address, epochs, pool_staking_address, staker) do |
|
|
|
:ets.insert(ContractState, {claim_reward_long_op_key(staker), true}) |
|
|
|
:ets.insert(ContractState, {claim_reward_long_op_key(staker), true}) |
|
|
|
|
|
|
|
|
|
|
|
try do |
|
|
|
try do |
|
|
|
json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) |
|
|
|
json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) |
|
|
|
|
|
|
|
|
|
|
|
amounts_result = ContractReader.call_get_reward_amount( |
|
|
|
amounts_result = |
|
|
|
|
|
|
|
ContractReader.call_get_reward_amount( |
|
|
|
staking_contract_address, |
|
|
|
staking_contract_address, |
|
|
|
epochs, |
|
|
|
epochs, |
|
|
|
pool_staking_address, |
|
|
|
pool_staking_address, |
|
|
@ -489,15 +530,19 @@ defmodule BlockScoutWeb.StakesChannel do |
|
|
|
json_rpc_named_arguments |
|
|
|
json_rpc_named_arguments |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
{error, amounts} = case amounts_result do |
|
|
|
{error, amounts} = |
|
|
|
|
|
|
|
case amounts_result do |
|
|
|
{:ok, amounts} -> |
|
|
|
{:ok, amounts} -> |
|
|
|
{nil, amounts} |
|
|
|
{nil, amounts} |
|
|
|
|
|
|
|
|
|
|
|
{:error, reason} -> |
|
|
|
{:error, reason} -> |
|
|
|
{error_reason_to_string(reason), %{token_reward_sum: 0, native_reward_sum: 0}} |
|
|
|
{error_reason_to_string(reason), %{token_reward_sum: 0, native_reward_sum: 0}} |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
{error, gas_limit} = if error == nil do |
|
|
|
{error, gas_limit} = |
|
|
|
estimate_gas_result = ContractReader.claim_reward_estimate_gas( |
|
|
|
if error == nil do |
|
|
|
|
|
|
|
estimate_gas_result = |
|
|
|
|
|
|
|
ContractReader.claim_reward_estimate_gas( |
|
|
|
staking_contract_address, |
|
|
|
staking_contract_address, |
|
|
|
epochs, |
|
|
|
epochs, |
|
|
|
pool_staking_address, |
|
|
|
pool_staking_address, |
|
|
@ -508,6 +553,7 @@ defmodule BlockScoutWeb.StakesChannel do |
|
|
|
case estimate_gas_result do |
|
|
|
case estimate_gas_result do |
|
|
|
{:ok, gas_limit} -> |
|
|
|
{:ok, gas_limit} -> |
|
|
|
{nil, gas_limit} |
|
|
|
{nil, gas_limit} |
|
|
|
|
|
|
|
|
|
|
|
{:error, reason} -> |
|
|
|
{:error, reason} -> |
|
|
|
{error_reason_to_string(reason), 0} |
|
|
|
{error_reason_to_string(reason), 0} |
|
|
|
end |
|
|
|
end |
|
|
@ -519,8 +565,18 @@ defmodule BlockScoutWeb.StakesChannel do |
|
|
|
coin = %Token{symbol: Explorer.coin(), decimals: Decimal.new(18)} |
|
|
|
coin = %Token{symbol: Explorer.coin(), decimals: Decimal.new(18)} |
|
|
|
|
|
|
|
|
|
|
|
push(socket, "claim_reward_recalculations", %{ |
|
|
|
push(socket, "claim_reward_recalculations", %{ |
|
|
|
token_reward_sum: StakesHelpers.format_token_amount(amounts.token_reward_sum, token, digits: token.decimals, ellipsize: false, symbol: false), |
|
|
|
token_reward_sum: |
|
|
|
native_reward_sum: StakesHelpers.format_token_amount(amounts.native_reward_sum, coin, digits: coin.decimals, ellipsize: false, symbol: false), |
|
|
|
StakesHelpers.format_token_amount(amounts.token_reward_sum, token, |
|
|
|
|
|
|
|
digits: token.decimals, |
|
|
|
|
|
|
|
ellipsize: false, |
|
|
|
|
|
|
|
symbol: false |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
native_reward_sum: |
|
|
|
|
|
|
|
StakesHelpers.format_token_amount(amounts.native_reward_sum, coin, |
|
|
|
|
|
|
|
digits: coin.decimals, |
|
|
|
|
|
|
|
ellipsize: false, |
|
|
|
|
|
|
|
symbol: false |
|
|
|
|
|
|
|
), |
|
|
|
gas_limit: gas_limit, |
|
|
|
gas_limit: gas_limit, |
|
|
|
error: error |
|
|
|
error: error |
|
|
|
}) |
|
|
|
}) |
|
|
@ -534,6 +590,7 @@ defmodule BlockScoutWeb.StakesChannel do |
|
|
|
true |
|
|
|
true |
|
|
|
else |
|
|
|
else |
|
|
|
staker = socket.assigns[:account] |
|
|
|
staker = socket.assigns[:account] |
|
|
|
|
|
|
|
|
|
|
|
with [{_, true}] <- :ets.lookup(ContractState, claim_reward_long_op_key(staker)) do |
|
|
|
with [{_, true}] <- :ets.lookup(ContractState, claim_reward_long_op_key(staker)) do |
|
|
|
true |
|
|
|
true |
|
|
|
end |
|
|
|
end |
|
|
@ -544,13 +601,20 @@ defmodule BlockScoutWeb.StakesChannel do |
|
|
|
|
|
|
|
|
|
|
|
defp array_to_ranges(numbers, prev_ranges \\ []) do |
|
|
|
defp array_to_ranges(numbers, prev_ranges \\ []) do |
|
|
|
length = Enum.count(numbers) |
|
|
|
length = Enum.count(numbers) |
|
|
|
|
|
|
|
|
|
|
|
if length > 0 do |
|
|
|
if length > 0 do |
|
|
|
{first, last, next_index} = get_range(numbers) |
|
|
|
{first, last, next_index} = get_range(numbers) |
|
|
|
ranges = prev_ranges ++ [{first, last}] |
|
|
|
prev_ranges_reversed = Enum.reverse(prev_ranges) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ranges = |
|
|
|
|
|
|
|
[{first, last} | prev_ranges_reversed] |
|
|
|
|
|
|
|
|> Enum.reverse() |
|
|
|
|
|
|
|
|
|
|
|
if next_index == 0 || next_index >= length do |
|
|
|
if next_index == 0 || next_index >= length do |
|
|
|
ranges |
|
|
|
ranges |
|
|
|
else |
|
|
|
else |
|
|
|
Enum.slice(numbers, next_index, length - next_index) |
|
|
|
numbers |
|
|
|
|
|
|
|
|> Enum.slice(next_index, length - next_index) |
|
|
|
|> array_to_ranges(ranges) |
|
|
|
|> array_to_ranges(ranges) |
|
|
|
end |
|
|
|
end |
|
|
|
else |
|
|
|
else |
|
|
@ -568,10 +632,12 @@ defmodule BlockScoutWeb.StakesChannel do |
|
|
|
|
|
|
|
|
|
|
|
defp get_range(numbers) do |
|
|
|
defp get_range(numbers) do |
|
|
|
last_index = |
|
|
|
last_index = |
|
|
|
Enum.with_index(numbers) |
|
|
|
numbers |
|
|
|
|
|
|
|
|> Enum.with_index() |
|
|
|
|> Enum.find_index(fn {n, i} -> |
|
|
|
|> Enum.find_index(fn {n, i} -> |
|
|
|
if i > 0, do: n != Enum.at(numbers, i - 1) + 1, else: false |
|
|
|
if i > 0, do: n != Enum.at(numbers, i - 1) + 1, else: false |
|
|
|
end) |
|
|
|
end) |
|
|
|
|
|
|
|
|
|
|
|
next_index = if last_index == nil, do: Enum.count(numbers), else: last_index |
|
|
|
next_index = if last_index == nil, do: Enum.count(numbers), else: last_index |
|
|
|
first = Enum.at(numbers, 0) |
|
|
|
first = Enum.at(numbers, 0) |
|
|
|
last = Enum.at(numbers, next_index - 1) |
|
|
|
last = Enum.at(numbers, next_index - 1) |
|
|
@ -599,4 +665,85 @@ defmodule BlockScoutWeb.StakesChannel do |
|
|
|
staker = if staker == nil, do: "", else: staker |
|
|
|
staker = if staker == nil, do: "", else: staker |
|
|
|
Atom.to_string(@claim_reward_long_op) <> "_" <> staker |
|
|
|
Atom.to_string(@claim_reward_long_op) <> "_" <> staker |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
defp handle_in_render_claim_reward_result( |
|
|
|
|
|
|
|
socket, |
|
|
|
|
|
|
|
data, |
|
|
|
|
|
|
|
staker, |
|
|
|
|
|
|
|
staking_contract_address, |
|
|
|
|
|
|
|
empty_staker, |
|
|
|
|
|
|
|
empty_staking_contract_address |
|
|
|
|
|
|
|
) do |
|
|
|
|
|
|
|
cond do |
|
|
|
|
|
|
|
claim_reward_long_op_active(socket) == true -> |
|
|
|
|
|
|
|
{:reply, {:error, %{reason: gettext("Pools searching is already in progress for this address")}}, socket} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
empty_staker -> |
|
|
|
|
|
|
|
{:reply, {:error, %{reason: gettext("Unknown staker address. Please, choose your account in MetaMask")}}, |
|
|
|
|
|
|
|
socket} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
empty_staking_contract_address -> |
|
|
|
|
|
|
|
{:reply, {:error, %{reason: gettext("Unknown address of Staking contract. Please, contact support")}}, socket} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
true -> |
|
|
|
|
|
|
|
result = |
|
|
|
|
|
|
|
if data["preload"] do |
|
|
|
|
|
|
|
%{ |
|
|
|
|
|
|
|
html: View.render_to_string(StakesView, "_stakes_modal_claim_reward.html", %{}), |
|
|
|
|
|
|
|
socket: socket |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
task = Task.async(__MODULE__, :find_claim_reward_pools, [socket, staker, staking_contract_address]) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
%{ |
|
|
|
|
|
|
|
html: "OK", |
|
|
|
|
|
|
|
socket: assign(socket, @claim_reward_long_op, %{task: task, staker: staker}) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{:reply, {:ok, %{html: result.html}}, result.socket} |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
defp handle_in_recalc_claim_reward_result( |
|
|
|
|
|
|
|
socket, |
|
|
|
|
|
|
|
epochs, |
|
|
|
|
|
|
|
staking_contract_address, |
|
|
|
|
|
|
|
pool_staking_address, |
|
|
|
|
|
|
|
staker, |
|
|
|
|
|
|
|
empty_pool_staking_address, |
|
|
|
|
|
|
|
empty_staking_contract_address, |
|
|
|
|
|
|
|
empty_staker |
|
|
|
|
|
|
|
) do |
|
|
|
|
|
|
|
cond do |
|
|
|
|
|
|
|
claim_reward_long_op_active(socket) == true -> |
|
|
|
|
|
|
|
{:reply, {:error, %{reason: gettext("Reward calculating is already in progress for this address")}}, socket} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Enum.empty?(epochs) -> |
|
|
|
|
|
|
|
{:reply, {:error, %{reason: gettext("Staking epochs are not specified or not in the allowed range")}}, socket} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
empty_pool_staking_address -> |
|
|
|
|
|
|
|
{:reply, {:error, %{reason: gettext("Unknown pool staking address. Please, contact support")}}, socket} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
empty_staker -> |
|
|
|
|
|
|
|
{:reply, {:error, %{reason: gettext("Unknown staker address. Please, choose your account in MetaMask")}}, |
|
|
|
|
|
|
|
socket} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
empty_staking_contract_address -> |
|
|
|
|
|
|
|
{:reply, {:error, %{reason: gettext("Unknown address of Staking contract. Please, contact support")}}, socket} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
true -> |
|
|
|
|
|
|
|
task = |
|
|
|
|
|
|
|
Task.async(__MODULE__, :recalc_claim_reward, [ |
|
|
|
|
|
|
|
socket, |
|
|
|
|
|
|
|
staking_contract_address, |
|
|
|
|
|
|
|
epochs, |
|
|
|
|
|
|
|
pool_staking_address, |
|
|
|
|
|
|
|
staker |
|
|
|
|
|
|
|
]) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
socket = assign(socket, @claim_reward_long_op, %{task: task, staker: staker}) |
|
|
|
|
|
|
|
{:reply, {:ok, %{html: "OK"}}, socket} |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|