commit
8da365b462
@ -0,0 +1,7 @@ |
|||||||
|
import $ from 'jquery' |
||||||
|
import hljs from 'highlight.js' |
||||||
|
|
||||||
|
// only activate highlighting on pages with this selector
|
||||||
|
if ($('[data-activate-highlight]').length > 0) { |
||||||
|
hljs.initHighlightingOnLoad() |
||||||
|
} |
@ -0,0 +1,55 @@ |
|||||||
|
defmodule BlockScoutWeb.TransactionRawTraceController do |
||||||
|
use BlockScoutWeb, :controller |
||||||
|
|
||||||
|
alias BlockScoutWeb.TransactionView |
||||||
|
alias Explorer.{Chain, Market} |
||||||
|
alias Explorer.ExchangeRates.Token |
||||||
|
|
||||||
|
def index(conn, %{"transaction_id" => hash_string}) do |
||||||
|
with {:ok, hash} <- Chain.string_to_transaction_hash(hash_string), |
||||||
|
{:ok, transaction} <- |
||||||
|
Chain.hash_to_transaction( |
||||||
|
hash, |
||||||
|
necessity_by_association: %{ |
||||||
|
:block => :optional, |
||||||
|
[created_contract_address: :names] => :optional, |
||||||
|
[from_address: :names] => :optional, |
||||||
|
[to_address: :names] => :optional, |
||||||
|
[to_address: :smart_contract] => :optional, |
||||||
|
:token_transfers => :optional |
||||||
|
} |
||||||
|
) do |
||||||
|
options = [ |
||||||
|
necessity_by_association: %{ |
||||||
|
[created_contract_address: :names] => :optional, |
||||||
|
[from_address: :names] => :optional, |
||||||
|
[to_address: :names] => :optional |
||||||
|
} |
||||||
|
] |
||||||
|
|
||||||
|
internal_transactions = Chain.transaction_to_internal_transactions(transaction, options) |
||||||
|
|
||||||
|
render( |
||||||
|
conn, |
||||||
|
"index.html", |
||||||
|
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), |
||||||
|
internal_transactions: internal_transactions, |
||||||
|
block_height: Chain.block_height(), |
||||||
|
show_token_transfers: Chain.transaction_has_token_transfers?(hash), |
||||||
|
transaction: transaction |
||||||
|
) |
||||||
|
else |
||||||
|
:error -> |
||||||
|
conn |
||||||
|
|> put_status(422) |
||||||
|
|> put_view(TransactionView) |
||||||
|
|> render("invalid.html", transaction_hash: hash_string) |
||||||
|
|
||||||
|
{:error, :not_found} -> |
||||||
|
conn |
||||||
|
|> put_status(404) |
||||||
|
|> put_view(TransactionView) |
||||||
|
|> render("not_found.html", transaction_hash: hash_string) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1 @@ |
|||||||
|
<%= render BlockScoutWeb.TransactionView, "_metatags.html", conn: @conn, transaction: @transaction %> |
@ -0,0 +1,18 @@ |
|||||||
|
<section class="container"> |
||||||
|
<%= render BlockScoutWeb.TransactionView, "overview.html", assigns %> |
||||||
|
|
||||||
|
<div class="card"> |
||||||
|
<div class="card-header"> |
||||||
|
<%= render BlockScoutWeb.TransactionView, "_tabs.html", assigns %> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="card-body"> |
||||||
|
<h2 class="card-title"><%= gettext "Raw Trace" %></h2> |
||||||
|
<%= if Enum.count(@internal_transactions) > 0 do %> |
||||||
|
<pre class="pre-scrollable line-numbers" data-activate-highlight><code class="json "><%= for {line, number} <- raw_traces_with_lines(@internal_transactions) do %><div data-line-number="<%= number %>"><%= line %></div><% end %></code></pre> |
||||||
|
<% else %> |
||||||
|
No trace entries found. |
||||||
|
<% end %> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</section> |
@ -0,0 +1,18 @@ |
|||||||
|
defmodule BlockScoutWeb.TransactionRawTraceView do |
||||||
|
use BlockScoutWeb, :view |
||||||
|
@dialyzer :no_match |
||||||
|
|
||||||
|
alias Explorer.Chain.InternalTransaction |
||||||
|
|
||||||
|
def render("scripts.html", %{conn: conn}) do |
||||||
|
render_scripts(conn, "raw_trace/code_highlighting.js") |
||||||
|
end |
||||||
|
|
||||||
|
def raw_traces_with_lines(internal_transactions) do |
||||||
|
internal_transactions |
||||||
|
|> InternalTransaction.internal_transactions_to_raw() |
||||||
|
|> Jason.encode!(pretty: true) |
||||||
|
|> String.split("\n") |
||||||
|
|> Enum.with_index(1) |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,282 @@ |
|||||||
|
defmodule EthereumJSONRPC.Geth.Tracer do |
||||||
|
@moduledoc """ |
||||||
|
Elixir implementation of a custom tracer (`priv/js/ethereum_jsonrpc/geth/debug_traceTransaction/tracer.js`) |
||||||
|
for variants that don't support specifying tracer in [debug_traceTransaction](https://github.com/ethereum/go-ethereum/wiki/Management-APIs#debug_tracetransaction) calls. |
||||||
|
""" |
||||||
|
|
||||||
|
import EthereumJSONRPC, only: [integer_to_quantity: 1, quantity_to_integer: 1] |
||||||
|
|
||||||
|
def replay(%{"structLogs" => logs} = result, receipt, tx) when is_list(logs) do |
||||||
|
%{"contractAddress" => contract_address} = receipt |
||||||
|
%{"from" => from, "to" => to, "value" => value, "input" => input} = tx |
||||||
|
|
||||||
|
top = |
||||||
|
to |
||||||
|
|> if do |
||||||
|
%{ |
||||||
|
"type" => "call", |
||||||
|
"callType" => "call", |
||||||
|
"to" => to, |
||||||
|
"input" => input, |
||||||
|
"output" => Map.get(result, "return", "0x" <> Map.get(result, "returnValue", "")) |
||||||
|
} |
||||||
|
else |
||||||
|
%{ |
||||||
|
"type" => "create", |
||||||
|
"init" => input, |
||||||
|
"createdContractAddressHash" => contract_address, |
||||||
|
"createdContractCode" => "0x" |
||||||
|
} |
||||||
|
end |
||||||
|
|> Map.merge(%{ |
||||||
|
"from" => from, |
||||||
|
"traceAddress" => [], |
||||||
|
"value" => value, |
||||||
|
"gas" => 0, |
||||||
|
"gasUsed" => 0 |
||||||
|
}) |
||||||
|
|
||||||
|
ctx = %{ |
||||||
|
depth: 1, |
||||||
|
stack: [top], |
||||||
|
trace_address: [0], |
||||||
|
calls: [[]] |
||||||
|
} |
||||||
|
|
||||||
|
logs |
||||||
|
|> Enum.reduce(ctx, &step/2) |
||||||
|
|> finalize() |
||||||
|
end |
||||||
|
|
||||||
|
defp step(%{"error" => _}, %{stack: [%{"error" => _} | _]} = ctx), do: ctx |
||||||
|
|
||||||
|
defp step( |
||||||
|
%{"error" => _} = log, |
||||||
|
%{ |
||||||
|
depth: stack_depth, |
||||||
|
stack: [call | stack], |
||||||
|
trace_address: [_, trace_index | trace_address], |
||||||
|
calls: [subsubcalls, subcalls | calls] |
||||||
|
} = ctx |
||||||
|
) do |
||||||
|
call = process_return(log, Map.put(call, "error", "error")) |
||||||
|
|
||||||
|
subsubcalls = |
||||||
|
subsubcalls |
||||||
|
|> Enum.reverse() |
||||||
|
|> Enum.map(fn |
||||||
|
subcalls when is_list(subcalls) -> subcalls |
||||||
|
subcall when is_map(subcall) -> %{subcall | "from" => call["createdContractAddressHash"] || call["to"]} |
||||||
|
end) |
||||||
|
|
||||||
|
%{ |
||||||
|
ctx |
||||||
|
| depth: stack_depth - 1, |
||||||
|
stack: stack, |
||||||
|
trace_address: [trace_index + 1 | trace_address], |
||||||
|
calls: [[subsubcalls, call | subcalls] | calls] |
||||||
|
} |
||||||
|
end |
||||||
|
|
||||||
|
defp step( |
||||||
|
%{"depth" => log_depth} = log, |
||||||
|
%{ |
||||||
|
depth: stack_depth, |
||||||
|
stack: [call | stack], |
||||||
|
trace_address: [_, trace_index | trace_address], |
||||||
|
calls: [subsubcalls, subcalls | calls] |
||||||
|
} = ctx |
||||||
|
) |
||||||
|
when log_depth == stack_depth - 1 do |
||||||
|
call = process_return(log, call) |
||||||
|
|
||||||
|
subsubcalls = |
||||||
|
subsubcalls |
||||||
|
|> Enum.reverse() |
||||||
|
|> Enum.map(fn |
||||||
|
subcalls when is_list(subcalls) -> subcalls |
||||||
|
subcall when is_map(subcall) -> %{subcall | "from" => call["createdContractAddressHash"] || call["to"]} |
||||||
|
end) |
||||||
|
|
||||||
|
step(log, %{ |
||||||
|
ctx |
||||||
|
| depth: stack_depth - 1, |
||||||
|
stack: stack, |
||||||
|
trace_address: [trace_index + 1 | trace_address], |
||||||
|
calls: [[subsubcalls, call | subcalls] | calls] |
||||||
|
}) |
||||||
|
end |
||||||
|
|
||||||
|
defp step(%{"gas" => log_gas, "gasCost" => log_gas_cost} = log, %{stack: [%{"gas" => call_gas} = call | stack]} = ctx) do |
||||||
|
gas = max(call_gas, log_gas) |
||||||
|
op(log, %{ctx | stack: [%{call | "gas" => gas, "gasUsed" => gas - log_gas - log_gas_cost} | stack]}) |
||||||
|
end |
||||||
|
|
||||||
|
defp op(%{"op" => "CREATE"} = log, ctx), do: create_op(log, ctx) |
||||||
|
defp op(%{"op" => "SELFDESTRUCT"} = log, ctx), do: self_destruct_op(log, ctx) |
||||||
|
defp op(%{"op" => "CALL"} = log, ctx), do: call_op(log, "call", ctx) |
||||||
|
defp op(%{"op" => "CALLCODE"} = log, ctx), do: call_op(log, "callcode", ctx) |
||||||
|
defp op(%{"op" => "DELEGATECALL"} = log, ctx), do: call_op(log, "delegatecall", ctx) |
||||||
|
defp op(%{"op" => "STATICCALL"} = log, ctx), do: call_op(log, "staticcall", ctx) |
||||||
|
defp op(%{"op" => "REVERT"}, ctx), do: revert_op(ctx) |
||||||
|
defp op(_, ctx), do: ctx |
||||||
|
|
||||||
|
defp process_return(%{"stack" => log_stack}, %{"type" => "create"} = call) do |
||||||
|
[ret | _] = Enum.reverse(log_stack) |
||||||
|
|
||||||
|
case quantity_to_integer(ret) do |
||||||
|
0 -> Map.put(call, "error", call["error"] || "internal failure") |
||||||
|
_ -> %{call | "createdContractAddressHash" => "0x" <> String.slice(ret, 24, 40)} |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
defp process_return( |
||||||
|
%{"stack" => log_stack, "memory" => log_memory}, |
||||||
|
%{"outputOffset" => out_off, "outputLength" => out_len} = call |
||||||
|
) do |
||||||
|
[ret | _] = Enum.reverse(log_stack) |
||||||
|
|
||||||
|
ret |
||||||
|
|> quantity_to_integer() |
||||||
|
|> case do |
||||||
|
0 -> |
||||||
|
Map.put(call, "error", call["error"] || "internal failure") |
||||||
|
|
||||||
|
_ -> |
||||||
|
output = |
||||||
|
log_memory |
||||||
|
|> IO.iodata_to_binary() |
||||||
|
|> String.slice(out_off, out_len) |
||||||
|
|
||||||
|
%{call | "output" => "0x" <> output} |
||||||
|
end |
||||||
|
|> Map.drop(["outputOffset", "outputLength"]) |
||||||
|
end |
||||||
|
|
||||||
|
defp create_op( |
||||||
|
%{"stack" => log_stack, "memory" => log_memory}, |
||||||
|
%{depth: stack_depth, stack: stack, trace_address: trace_address, calls: calls} = ctx |
||||||
|
) do |
||||||
|
[value, input_offset, input_length | _] = Enum.reverse(log_stack) |
||||||
|
|
||||||
|
init = |
||||||
|
log_memory |
||||||
|
|> IO.iodata_to_binary() |
||||||
|
|> String.slice(quantity_to_integer("0x" <> input_offset) * 2, quantity_to_integer("0x" <> input_length) * 2) |
||||||
|
|
||||||
|
call = %{ |
||||||
|
"type" => "create", |
||||||
|
"from" => nil, |
||||||
|
"traceAddress" => Enum.reverse(trace_address), |
||||||
|
"init" => "0x" <> init, |
||||||
|
"gas" => 0, |
||||||
|
"gasUsed" => 0, |
||||||
|
"value" => "0x" <> value, |
||||||
|
"createdContractAddressHash" => nil, |
||||||
|
"createdContractCode" => "0x" |
||||||
|
} |
||||||
|
|
||||||
|
%{ |
||||||
|
ctx |
||||||
|
| depth: stack_depth + 1, |
||||||
|
stack: [call | stack], |
||||||
|
trace_address: [0 | trace_address], |
||||||
|
calls: [[] | calls] |
||||||
|
} |
||||||
|
end |
||||||
|
|
||||||
|
defp self_destruct_op( |
||||||
|
%{"stack" => log_stack, "gas" => log_gas, "gasCost" => log_gas_cost}, |
||||||
|
%{trace_address: [trace_index | trace_address], calls: [subcalls | calls]} = ctx |
||||||
|
) do |
||||||
|
[to | _] = Enum.reverse(log_stack) |
||||||
|
|
||||||
|
if quantity_to_integer(to) in 1..8 do |
||||||
|
ctx |
||||||
|
else |
||||||
|
call = %{ |
||||||
|
"type" => "selfdestruct", |
||||||
|
"from" => nil, |
||||||
|
"to" => "0x" <> String.slice(to, 24, 40), |
||||||
|
"traceAddress" => Enum.reverse([trace_index | trace_address]), |
||||||
|
"gas" => log_gas, |
||||||
|
"gasUsed" => log_gas_cost, |
||||||
|
"value" => "0x0" |
||||||
|
} |
||||||
|
|
||||||
|
%{ctx | trace_address: [trace_index + 1 | trace_address], calls: [[call | subcalls] | calls]} |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
defp call_op( |
||||||
|
%{"stack" => log_stack, "memory" => log_memory}, |
||||||
|
call_type, |
||||||
|
%{ |
||||||
|
depth: stack_depth, |
||||||
|
stack: [%{"value" => parent_value} = parent | stack], |
||||||
|
trace_address: trace_address, |
||||||
|
calls: calls |
||||||
|
} = ctx |
||||||
|
) do |
||||||
|
[_, to | log_stack] = Enum.reverse(log_stack) |
||||||
|
|
||||||
|
{value, [input_offset, input_length, output_offset, output_length | _]} = |
||||||
|
case call_type do |
||||||
|
"delegatecall" -> |
||||||
|
{parent_value, log_stack} |
||||||
|
|
||||||
|
"staticcall" -> |
||||||
|
{"0x0", log_stack} |
||||||
|
|
||||||
|
_ -> |
||||||
|
[value | rest] = log_stack |
||||||
|
{"0x" <> value, rest} |
||||||
|
end |
||||||
|
|
||||||
|
input = |
||||||
|
log_memory |
||||||
|
|> IO.iodata_to_binary() |
||||||
|
|> String.slice(quantity_to_integer("0x" <> input_offset) * 2, quantity_to_integer("0x" <> input_length) * 2) |
||||||
|
|
||||||
|
call = %{ |
||||||
|
"type" => "call", |
||||||
|
"callType" => call_type, |
||||||
|
"from" => nil, |
||||||
|
"to" => "0x" <> String.slice(to, 24, 40), |
||||||
|
"traceAddress" => Enum.reverse(trace_address), |
||||||
|
"input" => "0x" <> input, |
||||||
|
"output" => "0x", |
||||||
|
"outputOffset" => quantity_to_integer("0x" <> output_offset) * 2, |
||||||
|
"outputLength" => quantity_to_integer("0x" <> output_length) * 2, |
||||||
|
"gas" => 0, |
||||||
|
"gasUsed" => 0, |
||||||
|
"value" => value |
||||||
|
} |
||||||
|
|
||||||
|
%{ |
||||||
|
ctx |
||||||
|
| depth: stack_depth + 1, |
||||||
|
stack: [call, parent | stack], |
||||||
|
trace_address: [0 | trace_address], |
||||||
|
calls: [[] | calls] |
||||||
|
} |
||||||
|
end |
||||||
|
|
||||||
|
defp revert_op(%{stack: [last | stack]} = ctx) do |
||||||
|
%{ctx | stack: [Map.put(last, "error", "execution reverted") | stack]} |
||||||
|
end |
||||||
|
|
||||||
|
defp finalize(%{stack: [top], calls: [calls]}) do |
||||||
|
calls = |
||||||
|
Enum.map(calls, fn |
||||||
|
subcalls when is_list(subcalls) -> subcalls |
||||||
|
subcall when is_map(subcall) -> %{subcall | "from" => top["createdContractAddressHash"] || top["to"]} |
||||||
|
end) |
||||||
|
|
||||||
|
[top | Enum.reverse(calls)] |
||||||
|
|> List.flatten() |
||||||
|
|> Enum.map(fn %{"gas" => gas, "gasUsed" => gas_used} = call -> |
||||||
|
%{call | "gas" => integer_to_quantity(gas), "gasUsed" => integer_to_quantity(gas_used)} |
||||||
|
end) |
||||||
|
end |
||||||
|
end |
@ -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 |
@ -0,0 +1,42 @@ |
|||||||
|
defmodule Explorer.Chain.InternalTransaction.Action do |
||||||
|
@moduledoc """ |
||||||
|
The action that was performed in a `t:EthereumJSONRPC.Parity.Trace.t/0` |
||||||
|
""" |
||||||
|
|
||||||
|
import EthereumJSONRPC, only: [integer_to_quantity: 1] |
||||||
|
alias Explorer.Chain.{Data, Hash, Wei} |
||||||
|
|
||||||
|
def to_raw(action) when is_map(action) do |
||||||
|
Enum.into(action, %{}, &entry_to_raw/1) |
||||||
|
end |
||||||
|
|
||||||
|
defp entry_to_raw({key, %Data{} = data}) when key in ~w(init input) do |
||||||
|
{key, Data.to_string(data)} |
||||||
|
end |
||||||
|
|
||||||
|
defp entry_to_raw({key, %Hash{} = address}) when key in ~w(address from refundAddress to) do |
||||||
|
{key, to_string(address)} |
||||||
|
end |
||||||
|
|
||||||
|
defp entry_to_raw({"callType", type}) do |
||||||
|
{"callType", Atom.to_string(type)} |
||||||
|
end |
||||||
|
|
||||||
|
defp entry_to_raw({"gas" = key, %Decimal{} = decimal}) do |
||||||
|
value = |
||||||
|
decimal |
||||||
|
|> Decimal.round() |
||||||
|
|> Decimal.to_integer() |
||||||
|
|
||||||
|
{key, integer_to_quantity(value)} |
||||||
|
end |
||||||
|
|
||||||
|
defp entry_to_raw({key, %Wei{value: value}}) when key in ~w(balance value) do |
||||||
|
rounded = |
||||||
|
value |
||||||
|
|> Decimal.round() |
||||||
|
|> Decimal.to_integer() |
||||||
|
|
||||||
|
{key, integer_to_quantity(rounded)} |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,32 @@ |
|||||||
|
defmodule Explorer.Chain.InternalTransaction.Result do |
||||||
|
@moduledoc """ |
||||||
|
The result of performing the `t:EthereumJSONRPC.Parity.Action.t/0` in a `t:EthereumJSONRPC.Parity.Trace.t/0`. |
||||||
|
""" |
||||||
|
|
||||||
|
import EthereumJSONRPC, only: [integer_to_quantity: 1] |
||||||
|
|
||||||
|
alias Explorer.Chain.{Data, Hash} |
||||||
|
|
||||||
|
def to_raw(result) when is_map(result) do |
||||||
|
Enum.into(result, %{}, &entry_to_raw/1) |
||||||
|
end |
||||||
|
|
||||||
|
defp entry_to_raw({"output" = key, %Data{} = data}) do |
||||||
|
{key, Data.to_string(data)} |
||||||
|
end |
||||||
|
|
||||||
|
defp entry_to_raw({"address" = key, %Hash{} = hash}) do |
||||||
|
{key, to_string(hash)} |
||||||
|
end |
||||||
|
|
||||||
|
defp entry_to_raw({"code", _} = entry), do: entry |
||||||
|
|
||||||
|
defp entry_to_raw({key, decimal}) when key in ~w(gasUsed) do |
||||||
|
integer = |
||||||
|
decimal |
||||||
|
|> Decimal.round() |
||||||
|
|> Decimal.to_integer() |
||||||
|
|
||||||
|
{key, integer_to_quantity(integer)} |
||||||
|
end |
||||||
|
end |
@ -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 |
@ -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" |
||||||
|
} |
||||||
|
] |
@ -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" |
||||||
|
} |
||||||
|
] |
@ -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 |
@ -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_until: 0, |
||||||
|
delegators_count: 0, |
||||||
|
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 PoolsReader.pool_data(address) == response |
||||||
|
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, |
||||||
|
2, |
||||||
|
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 |
@ -0,0 +1,136 @@ |
|||||||
|
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 Explorer.Staking.PoolsReader |
||||||
|
alias Indexer.BufferedTask |
||||||
|
alias Indexer.Fetcher.StakingPools.Supervisor, as: StakingPoolsSupervisor |
||||||
|
|
||||||
|
@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 |
||||||
|
if StakingPoolsSupervisor.disabled?() do |
||||||
|
:ok |
||||||
|
else |
||||||
|
pools = |
||||||
|
PoolsReader.get_pools() |
||||||
|
|> Enum.map(&entry/1) |
||||||
|
|
||||||
|
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 |
||||||
|
|
||||||
|
def 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 |
@ -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, |
||||||
|
4, |
||||||
|
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 |
Loading…
Reference in new issue