Blockchain explorer for Ethereum based network and a tool for inspecting and analyzing EVM based blockchains.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
blockscout/apps/block_scout_web/lib/block_scout_web/chain.ex

263 lines
8.4 KiB

defmodule BlockScoutWeb.Chain do
@moduledoc """
Converts the `param` to the corresponding resource that uses that format of param.
"""
import Explorer.Chain,
only: [
find_or_insert_address_from_hash: 1,
hash_to_block: 1,
hash_to_transaction: 1,
number_to_block: 1,
string_to_address_hash: 1,
string_to_block_hash: 1,
string_to_transaction_hash: 1,
token_contract_address_from_token_name: 1
]
alias Explorer.Chain.Block.Reward
alias Explorer.Chain.{
Address,
Address.CoinBalance,
Address.CurrentTokenBalance,
Block,
InternalTransaction,
Log,
TokenTransfer,
Transaction,
Wei
}
alias Explorer.PagingOptions
@page_size 50
@default_paging_options %PagingOptions{page_size: @page_size + 1}
@address_hash_len 40
@tx_block_hash_len 64
def default_paging_options do
@default_paging_options
end
def current_filter(%{paging_options: paging_options} = params) do
params
|> Map.get("filter")
|> case do
"to" -> [direction: :to, paging_options: paging_options]
"from" -> [direction: :from, paging_options: paging_options]
_ -> [paging_options: paging_options]
end
end
def current_filter(params) do
params
|> Map.get("filter")
|> case do
"to" -> [direction: :to]
"from" -> [direction: :from]
_ -> []
end
end
@spec from_param(String.t()) :: {:ok, Address.t() | Block.t() | Transaction.t()} | {:error, :not_found}
def from_param(param)
def from_param("0x" <> number_string = param) when byte_size(number_string) == @address_hash_len,
do: address_from_param(param)
def from_param("0x" <> number_string = param) when byte_size(number_string) == @tx_block_hash_len,
do: block_or_transaction_from_param(param)
def from_param(param) when byte_size(param) == @address_hash_len,
do: address_from_param("0x" <> param)
def from_param(param) when byte_size(param) == @tx_block_hash_len,
do: block_or_transaction_from_param("0x" <> param)
def from_param(string) when is_binary(string) do
case param_to_block_number(string) do
{:ok, number} -> number_to_block(number)
_ -> token_address_from_name(string)
end
end
def next_page_params([], _list, _params), do: nil
def next_page_params(_, list, params) do
Map.merge(params, paging_params(List.last(list)))
end
def paging_options(%{"hash" => hash, "fetched_coin_balance" => fetched_coin_balance}) do
with {coin_balance, ""} <- Integer.parse(fetched_coin_balance),
{:ok, address_hash} <- string_to_address_hash(hash) do
[paging_options: %{@default_paging_options | key: {%Wei{value: Decimal.new(coin_balance)}, address_hash}}]
else
_ ->
[paging_options: @default_paging_options]
end
end
def paging_options(%{
"block_number" => block_number_string,
"transaction_index" => transaction_index_string,
"index" => index_string
}) do
with {block_number, ""} <- Integer.parse(block_number_string),
{transaction_index, ""} <- Integer.parse(transaction_index_string),
{index, ""} <- Integer.parse(index_string) do
[paging_options: %{@default_paging_options | key: {block_number, transaction_index, index}}]
else
_ ->
[paging_options: @default_paging_options]
end
end
def paging_options(%{"block_number" => block_number_string, "index" => index_string}) do
with {block_number, ""} <- Integer.parse(block_number_string),
{index, ""} <- Integer.parse(index_string) do
[paging_options: %{@default_paging_options | key: {block_number, index}}]
else
_ ->
[paging_options: @default_paging_options]
end
end
def paging_options(%{"block_number" => block_number_string}) do
with {block_number, ""} <- Integer.parse(block_number_string) do
[paging_options: %{@default_paging_options | key: {block_number}}]
else
_ ->
[paging_options: @default_paging_options]
end
end
def paging_options(%{"index" => index_string}) when is_binary(index_string) do
with {index, ""} <- Integer.parse(index_string) do
[paging_options: %{@default_paging_options | key: {index}}]
else
_ ->
[paging_options: @default_paging_options]
end
end
def paging_options(%{"index" => index}) when is_integer(index) do
[paging_options: %{@default_paging_options | key: {index}}]
end
def paging_options(%{"inserted_at" => inserted_at_string, "hash" => hash_string}) do
with {:ok, inserted_at, _} <- DateTime.from_iso8601(inserted_at_string),
{:ok, hash} <- string_to_transaction_hash(hash_string) do
[paging_options: %{@default_paging_options | key: {inserted_at, hash}}]
else
_ ->
[paging_options: @default_paging_options]
end
end
def paging_options(%{"token_name" => name, "token_type" => type, "token_inserted_at" => inserted_at}),
do: [paging_options: %{@default_paging_options | key: {name, type, inserted_at}}]
def paging_options(%{"value" => value, "address_hash" => address_hash}) do
[paging_options: %{@default_paging_options | key: {value, address_hash}}]
end
def paging_options(_params), do: [paging_options: @default_paging_options]
def param_to_block_number(formatted_number) when is_binary(formatted_number) do
case Integer.parse(formatted_number) do
{number, ""} -> {:ok, number}
_ -> {:error, :invalid}
end
end
def split_list_by_page(list_plus_one), do: Enum.split(list_plus_one, @page_size)
defp address_from_param(param) do
with {:ok, hash} <- string_to_address_hash(param) do
find_or_insert_address_from_hash(hash)
else
:error -> {:error, :not_found}
end
end
defp token_address_from_name(name) do
case token_contract_address_from_token_name(name) do
{:ok, hash} -> find_or_insert_address_from_hash(hash)
_ -> {:error, :not_found}
end
end
defp paging_params({%Address{hash: hash, fetched_coin_balance: fetched_coin_balance}, _}) do
%{"hash" => hash, "fetched_coin_balance" => Decimal.to_string(fetched_coin_balance.value)}
end
defp paging_params({%Reward{block: %{number: number}}, _}) do
%{"block_number" => number, "index" => 0}
end
defp paging_params(%Block{number: number}) do
%{"block_number" => number}
end
defp paging_params(%InternalTransaction{index: index, transaction_hash: transaction_hash}) do
{:ok, %Transaction{block_number: block_number, index: transaction_index}} = hash_to_transaction(transaction_hash)
%{"block_number" => block_number, "transaction_index" => transaction_index, "index" => index}
end
defp paging_params(%Log{index: index} = log) do
if Ecto.assoc_loaded?(log.transaction) do
%{"block_number" => log.transaction.block_number, "transaction_index" => log.transaction.index, "index" => index}
else
%{"index" => index}
end
end
defp paging_params(%Transaction{block_number: nil, inserted_at: inserted_at, hash: hash}) do
%{"inserted_at" => DateTime.to_iso8601(inserted_at), "hash" => hash}
end
defp paging_params(%Transaction{block_number: block_number, index: index}) do
%{"block_number" => block_number, "index" => index}
end
defp paging_params(%TokenTransfer{block_number: block_number, log_index: index}) do
%{"block_number" => block_number, "index" => index}
end
defp paging_params(%Address.Token{name: name, type: type, inserted_at: inserted_at}) do
inserted_at_datetime = DateTime.to_iso8601(inserted_at)
%{"token_name" => name, "token_type" => type, "token_inserted_at" => inserted_at_datetime}
end
defp paging_params(%CurrentTokenBalance{address_hash: address_hash, value: value}) do
%{"address_hash" => to_string(address_hash), "value" => Decimal.to_integer(value)}
end
defp paging_params(%CoinBalance{block_number: block_number}) do
%{"block_number" => block_number}
end
defp block_or_transaction_from_param(param) do
with {:error, :not_found} <- transaction_from_param(param) do
hash_string_to_block(param)
end
end
defp transaction_from_param(param) do
with {:ok, hash} <- string_to_transaction_hash(param) do
hash_to_transaction(hash)
else
:error -> {:error, :not_found}
end
end
defp hash_string_to_block(hash_string) do
with {:ok, hash} <- string_to_block_hash(hash_string) do
hash_to_block(hash)
else
:error -> {:error, :not_found}
end
end
end