Add retry fetching while fetching Token's fields

pull/1078/head
Felipe Renan 6 years ago
parent 5f9c700e73
commit 0fbda6c72f
  1. 3
      apps/explorer/config/config.exs
  2. 55
      apps/explorer/lib/explorer/token/functions_reader.ex
  3. 131
      apps/explorer/test/explorer/token/functions_reader_test.exs

@ -10,7 +10,8 @@ config :ecto, json_library: Jason
# General application configuration
config :explorer,
ecto_repos: [Explorer.Repo],
coin: System.get_env("COIN") || "POA"
coin: System.get_env("COIN") || "POA",
token_functions_reader_retry: 3
config :explorer, Explorer.Integrations.EctoLogger, query_time_ms_threshold: 2_000

@ -80,17 +80,20 @@ defmodule Explorer.Token.FunctionsReader do
* Given that all functions were read:
%{
"totalSupply" => [],
"decimals" => [],
"name" => [],
"symbol" => []
name: "BNT",
decimals: 18,
total_supply: 1_000_000_000_000_000_000,
symbol: nil
}
* Given that some of them were read:
%{
"name" => [],
"symbol" => []
name: "BNT",
decimals: 18
}
It will retry to fetch each function in the Smart Contract according to :token_fetcher_retry
configured in the application env case one of them raised error.
"""
@spec get_functions_of(Hash.t()) :: Map.t()
def get_functions_of(%Hash{byte_count: unquote(Hash.Address.byte_count())} = address) do
@ -101,9 +104,45 @@ defmodule Explorer.Token.FunctionsReader do
@spec get_functions_of(String.t()) :: Map.t()
def get_functions_of(contract_address_hash) do
contract_functions_result = Reader.query_contract(contract_address_hash, @contract_abi, @contract_functions)
contract_address_hash
|> fetch_functions_from_contract(@contract_functions)
|> format_contract_functions_result(contract_address_hash)
end
defp fetch_functions_from_contract(contract_address_hash, contract_functions) do
retry = Application.get_env(:explorer, :token_functions_reader_retry)
format_contract_functions_result(contract_functions_result, contract_address_hash)
fetch_functions_from_contract(contract_address_hash, contract_functions, retry)
end
defp fetch_functions_from_contract(contract_address_hash, contract_functions, retry, result \\ %{}) do
contract_functions_result = Reader.query_contract(contract_address_hash, @contract_abi, contract_functions)
contract_functions_with_errors = contract_functions_with_errors(contract_functions_result)
if Enum.any?(contract_functions_with_errors) && retry > 0 do
fetch_functions_from_contract(
contract_address_hash,
contract_functions_with_errors,
retry - 1,
Map.merge(result, contract_functions_result)
)
else
Map.merge(result, contract_functions_result)
end
end
defp contract_functions_with_errors(contract_functions) do
contract_functions
|> Enum.filter(fn function ->
case function do
{_, {:error, _}} -> true
{_, {:ok, _}} -> false
end
end)
|> Enum.reduce(%{}, fn {name, _error}, acc ->
Map.put(acc, name, [])
end)
end
defp format_contract_functions_result(contract_functions, contract_address_hash) do

@ -85,9 +85,30 @@ defmodule Explorer.Token.FunctionsReaderTest do
end
)
expect(
EthereumJSONRPC.Mox,
:json_rpc,
2,
fn [%{id: "decimals"}, %{id: "symbol"}], _opts ->
{:ok,
[
%{
error: %{code: -32015, data: "something", message: "some error"},
id: "decimals",
jsonrpc: "2.0"
},
%{
error: %{code: -32015, data: "something", message: "some error"},
id: "symbol",
jsonrpc: "2.0"
}
]}
end
)
expected = %{
name: "Bancor",
total_supply: 1_000_000_000_000_000_000,
total_supply: 1_000_000_000_000_000_000
}
assert FunctionsReader.get_functions_of(token.contract_address_hash) == expected
@ -203,5 +224,113 @@ defmodule Explorer.Token.FunctionsReaderTest do
assert FunctionsReader.get_functions_of(token.contract_address_hash) == %{name: long_token_name_shortened}
end
test "retries when some function gave error" do
token = insert(:token, contract_address: build(:contract_address))
expect(
EthereumJSONRPC.Mox,
:json_rpc,
1,
fn [%{id: "decimals"}, %{id: "name"}, %{id: "symbol"}, %{id: "totalSupply"}], _opts ->
{:ok,
[
%{
error: %{code: -32015, data: "something", message: "some error"},
id: "symbol",
jsonrpc: "2.0"
},
%{
id: "decimals",
result: "0x0000000000000000000000000000000000000000000000000000000000000012"
}
]}
end
)
expect(
EthereumJSONRPC.Mox,
:json_rpc,
1,
fn [%{id: "symbol"}], _opts ->
{:ok,
[
%{
id: "symbol",
result:
"0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003424e540000000000000000000000000000000000000000000000000000000000"
}
]}
end
)
assert FunctionsReader.get_functions_of(token.contract_address_hash) == %{decimals: 18, symbol: "BNT"}
end
test "retries according to the configured number" do
original = Application.get_env(:explorer, :token_functions_reader_max_retries)
Application.put_env(:explorer, :token_functions_reader_max_retries, 2)
token = insert(:token, contract_address: build(:contract_address))
expect(
EthereumJSONRPC.Mox,
:json_rpc,
1,
fn [%{id: "decimals"}, %{id: "name"}, %{id: "symbol"}, %{id: "totalSupply"}], _opts ->
{:ok,
[
%{
error: %{code: -32015, data: "something", message: "some error"},
id: "decimals",
jsonrpc: "2.0"
},
%{
id: "name",
result:
"0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000642616e636f720000000000000000000000000000000000000000000000000000"
},
%{
error: %{code: -32015, data: "something", message: "some error"},
id: "symbol",
jsonrpc: "2.0"
},
%{
id: "totalSupply",
result: "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000"
}
]}
end
)
expect(
EthereumJSONRPC.Mox,
:json_rpc,
2,
fn [%{id: "decimals"}, %{id: "symbol"}], _opts ->
{:ok,
[
%{
error: %{code: -32015, data: "something", message: "some error"},
id: "decimals",
jsonrpc: "2.0"
},
%{
error: %{code: -32015, data: "something", message: "some error"},
id: "symbol",
jsonrpc: "2.0"
}
]}
end
)
assert FunctionsReader.get_functions_of(token.contract_address_hash) == %{
name: "Bancor",
total_supply: 1_000_000_000_000_000_000
}
on_exit(fn -> Application.put_env(:explorer, :token_functions_reader_max_retries, original) end)
end
end
end

Loading…
Cancel
Save