|
|
@ -3,6 +3,8 @@ defmodule Explorer.Token.FunctionsReader do |
|
|
|
Reads Token's fields using Smart Contract functions from the blockchain. |
|
|
|
Reads Token's fields using Smart Contract functions from the blockchain. |
|
|
|
""" |
|
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
require Logger |
|
|
|
|
|
|
|
|
|
|
|
alias Explorer.Chain.Hash |
|
|
|
alias Explorer.Chain.Hash |
|
|
|
alias Explorer.SmartContract.Reader |
|
|
|
alias Explorer.SmartContract.Reader |
|
|
|
|
|
|
|
|
|
|
@ -92,7 +94,7 @@ defmodule Explorer.Token.FunctionsReader do |
|
|
|
decimals: 18 |
|
|
|
decimals: 18 |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
It will retry to fetch each function in the Smart Contract according to :token_fetcher_retry |
|
|
|
It will retry to fetch each function in the Smart Contract according to :token_functions_reader_max_retries |
|
|
|
configured in the application env case one of them raised error. |
|
|
|
configured in the application env case one of them raised error. |
|
|
|
""" |
|
|
|
""" |
|
|
|
@spec get_functions_of(Hash.t()) :: Map.t() |
|
|
|
@spec get_functions_of(Hash.t()) :: Map.t() |
|
|
@ -110,39 +112,65 @@ defmodule Explorer.Token.FunctionsReader do |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
defp fetch_functions_from_contract(contract_address_hash, contract_functions) do |
|
|
|
defp fetch_functions_from_contract(contract_address_hash, contract_functions) do |
|
|
|
retry = Application.get_env(:explorer, :token_functions_reader_retry) |
|
|
|
max_retries = Application.get_env(:explorer, :token_functions_reader_max_retries) |
|
|
|
|
|
|
|
functions_fetched = %{} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fetch_functions_from_contract(contract_address_hash, contract_functions, max_retries, functions_fetched) |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
defp fetch_functions_from_contract(contract_address_hash, contract_functions, retries_left, functions_fetched) |
|
|
|
|
|
|
|
when retries_left == 0 do |
|
|
|
|
|
|
|
contract_functions_result = Reader.query_contract(contract_address_hash, @contract_abi, contract_functions) |
|
|
|
|
|
|
|
|
|
|
|
fetch_functions_from_contract(contract_address_hash, contract_functions, retry) |
|
|
|
Map.merge(functions_fetched, contract_functions_result) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
defp fetch_functions_from_contract(contract_address_hash, contract_functions, retry, result \\ %{}) do |
|
|
|
defp fetch_functions_from_contract(contract_address_hash, contract_functions, retries_left, functions_fetched) |
|
|
|
|
|
|
|
when retries_left > 0 do |
|
|
|
contract_functions_result = Reader.query_contract(contract_address_hash, @contract_abi, contract_functions) |
|
|
|
contract_functions_result = Reader.query_contract(contract_address_hash, @contract_abi, contract_functions) |
|
|
|
|
|
|
|
|
|
|
|
contract_functions_with_errors = contract_functions_with_errors(contract_functions_result) |
|
|
|
functions_with_errors = |
|
|
|
|
|
|
|
Enum.filter(contract_functions_result, fn function -> |
|
|
|
|
|
|
|
case function do |
|
|
|
|
|
|
|
{_, {:error, _}} -> true |
|
|
|
|
|
|
|
{_, {:ok, _}} -> false |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
end) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if Enum.any?(functions_with_errors) do |
|
|
|
|
|
|
|
log_functions_with_errors(contract_address_hash, functions_with_errors, retries_left) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
contract_functions_with_errors = |
|
|
|
|
|
|
|
Map.take( |
|
|
|
|
|
|
|
contract_functions, |
|
|
|
|
|
|
|
Enum.map(functions_with_errors, fn {function, _} -> function end) |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
if Enum.any?(contract_functions_with_errors) && retry > 0 do |
|
|
|
|
|
|
|
fetch_functions_from_contract( |
|
|
|
fetch_functions_from_contract( |
|
|
|
contract_address_hash, |
|
|
|
contract_address_hash, |
|
|
|
contract_functions_with_errors, |
|
|
|
contract_functions_with_errors, |
|
|
|
retry - 1, |
|
|
|
retries_left - 1, |
|
|
|
Map.merge(result, contract_functions_result) |
|
|
|
Map.merge(functions_fetched, contract_functions_result) |
|
|
|
) |
|
|
|
) |
|
|
|
else |
|
|
|
else |
|
|
|
Map.merge(result, contract_functions_result) |
|
|
|
Map.merge(functions_fetched, contract_functions_result) |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
defp contract_functions_with_errors(contract_functions) do |
|
|
|
defp log_functions_with_errors(contract_address_hash, functions_with_errors, retries_left) do |
|
|
|
contract_functions |
|
|
|
error_messages = |
|
|
|
|> Enum.filter(fn function -> |
|
|
|
Enum.map(functions_with_errors, fn {function, {:error, error_message}} -> |
|
|
|
case function do |
|
|
|
"function: #{function} - error: #{error_message} \n" |
|
|
|
{_, {:error, _}} -> true |
|
|
|
end) |
|
|
|
{_, {:ok, _}} -> false |
|
|
|
|
|
|
|
end |
|
|
|
Logger.debug( |
|
|
|
end) |
|
|
|
[ |
|
|
|
|> Enum.reduce(%{}, fn {name, _error}, acc -> |
|
|
|
"<Token contract hash: #{contract_address_hash}> error while fetching metadata: \n", |
|
|
|
Map.put(acc, name, []) |
|
|
|
error_messages, |
|
|
|
end) |
|
|
|
"Retries left: #{retries_left}" |
|
|
|
|
|
|
|
], |
|
|
|
|
|
|
|
fetcher: :token_functions |
|
|
|
|
|
|
|
) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
defp format_contract_functions_result(contract_functions, contract_address_hash) do |
|
|
|
defp format_contract_functions_result(contract_functions, contract_address_hash) do |
|
|
|