\ No newline at end of file
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_function_response.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_function_response.html.eex
index 0af9292fc708..e361e2c17eca 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_function_response.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_function_response.html.eex
@@ -1,10 +1,40 @@
-[ <%= @function_name %> method Response ]
+[ <%= @function_name %> <%= gettext("method Response") %> ]
+
+
<%= case @outputs do %>
- <% {:error, message} -> %>
-
<%=raw(values_with_type(message, :error, nil, 0))%>
+ <% {:error, %{code: code, message: message, data: data}} -> %>
+ <% revert_reason = Chain.format_revert_reason_message(data) %>
+ <%= case decode_revert_reason(@smart_contract_address, revert_reason) do %>
+ <% {:ok, _identifier, text, mapping} -> %>
+
<%= raw(values_with_type(text, :error, nil)) %>
+
+ <%= for {name, type, value} <- mapping do %>
+
<%= raw(values_with_type(value, type, name, 1)) %>
+ <% end %>
+
+ <% {:error, _contract_verified, []} -> %>
+ <% decoded_revert_reason = decode_hex_revert_reason(revert_reason) %>
+
<%= "(#{code}) #{message} (#{if String.valid?(decoded_revert_reason), do: decoded_revert_reason, else: revert_reason})" %>
+ <% {:error, _contract_verified, candidates} -> %>
+ <% {:ok, _identifier, text, mapping} = Enum.at(candidates, 0) %>
+
<%= raw(values_with_type(text, :error, nil)) %>
+
+ <%= for {name, type, value} <- mapping do %>
+
<%= raw(values_with_type(value, type, name, 1)) %>
+ <% end %>
+
+ <% _ -> %>
+ <% decoded_revert_reason = decode_hex_revert_reason(revert_reason) %>
+
<%= "(#{code}) #{message} (#{if String.valid?(decoded_revert_reason), do: decoded_revert_reason, else: revert_reason})" %>
+ <% end %>
+ <% {:error, %{code: code, message: message}} -> %>
+
(error) : <%= "(#{code}) #{message}" %>
+ <% {:error, error} -> %>
+
(error) : <%= error %>
<% _ -> %>
+
[<%= for {item, index} <- Enum.with_index(@outputs) do %>
<%= if named_argument?(item) do %><%= item["name"] %> <% end %>
<%= raw(values_with_type(item["value"], item["type"], fetch_name(@names, index), 0)) %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_pending_contract_write.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_pending_contract_write.html.eex
index 327c5e332eb1..316f57180ac5 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_pending_contract_write.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_pending_contract_write.html.eex
@@ -1,14 +1,11 @@
-
+
<%= render BlockScoutWeb.CommonComponentsView, "_modal_close_button.html" %>
-
+
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/stakes/_stakes_stats_item_account.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/stakes/_stakes_stats_item_account.html.eex
index fe19d078a818..3adf4c627abe 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/stakes/_stakes_stats_item_account.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/stakes/_stakes_stats_item_account.html.eex
@@ -12,7 +12,7 @@
<% else %>
-
Login with MetaMask
+
Login
<% end %>
@@ -51,4 +51,7 @@
<% end %>
+ <%= if @account do %>
+
Disconnect wallet
+ <% end %>
\ No newline at end of file
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/_tile.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/_tile.html.eex
index fc81cf9bcf75..a8659fa797d9 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/tokens/_tile.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/_tile.html.eex
@@ -10,10 +10,12 @@
<% chain_id_for_token_icon = System.get_env("CHAIN_ID") %>
<% foreign_token_contract_address_hash = nil %>
<% token_hash_for_token_icon = if foreign_token_contract_address_hash, do: foreign_token_contract_address_hash, else: Address.checksum(@token.contract_address_hash) %>
- <% token_icon_url = Explorer.Chain.get_token_icon_url_by(chain_id_for_token_icon, token_hash_for_token_icon) %>
- <%= if token_icon_url do %>
-
- <% end %>
+ <%=
+ render BlockScoutWeb.TokensView,
+ "_token_icon.html",
+ chain_id: chain_id_for_token_icon,
+ address: token_hash_for_token_icon
+ %>
<% end %>
<% token = token_display_name(@token) %>
<%= link(token,
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/_token_icon.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/_token_icon.html.eex
new file mode 100644
index 000000000000..4cc2883e9ea9
--- /dev/null
+++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/_token_icon.html.eex
@@ -0,0 +1,2 @@
+<% token_icon_url = Explorer.Chain.get_token_icon_url_by(@chain_id, @address) %>
+
\ No newline at end of file
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/_token_balances.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/_token_balances.html.eex
index ea1aa55b5c61..f8fecf59a639 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/_token_balances.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/_token_balances.html.eex
@@ -7,7 +7,7 @@
- <%= format_token_balance_value(@token_balance.value, @token) %> <%= Gettext.gettext(BlockScoutWeb.Gettext, @token.symbol) %>
+ <%= format_token_balance_value(@token_balance.value, @token_balance.token_id, @token) %> <%= @token.symbol %>
<%= if show_total_supply_percentage?(@token.total_supply) do %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/holder/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/holder/index.html.eex
new file mode 100644
index 000000000000..7fe594a3db0d
--- /dev/null
+++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/holder/index.html.eex
@@ -0,0 +1,41 @@
+
+ <%= render(
+ OverviewView,
+ "_details.html",
+ token: @token,
+ total_token_transfers: @total_token_transfers,
+ token_id: @token_instance.token_id,
+ token_instance: @token_instance,
+ conn: @conn
+ ) %>
+
+
+
+ <%= render OverviewView, "_tabs.html", assigns %>
+
+
<%= gettext "Token Holders" %>
+
+
+
+
+ <%= gettext("Something went wrong, click to reload.") %>
+
+
+
+
+ <%= gettext "There are no transfers for this Token." %>
+
+
+
+
+ <%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %>
+
+
+ <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %>
+
+
+
+
+
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex
index fb1256bd6f41..0cad38ec8e27 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex
@@ -20,7 +20,7 @@
title='<%= gettext("View Contract") %>'
onclick='<%= "location='#{address_path(@conn, :show, Address.checksum(@token.contract_address_hash))}'" %>'
>
-
+
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex
index b046f97b8ee7..57badb7e4d41 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex
@@ -5,11 +5,18 @@
to: token_instance_path(@conn, :show, @token.contract_address_hash, to_string(@token_instance.token_id))
)
%>
- <%= if @token_instance.instance do %>
+ <%= if @token_instance.instance do %>
<%= link(
gettext("Metadata"),
to: token_instance_metadata_path(@conn, :index, Address.checksum(@token.contract_address_hash), to_string(@token_instance.token_id)),
class: "card-tab #{tab_status("metadata", @conn.request_path)}")
%>
<% end %>
+ <%= if !Chain.token_id_1155_is_unique?(@token.contract_address_hash, @token_instance.token_id) and @token.type == "ERC-1155" do %>
+ <%= link(
+ gettext("Token Holders"),
+ to: token_instance_holder_path(@conn, :index, Address.checksum(@token.contract_address_hash), to_string(@token_instance.token_id)),
+ class: "card-tab #{tab_status("token-holders", @conn.request_path)}")
+ %>
+ <% end %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/inventory/_token.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/inventory/_token.html.eex
index ae004ddeeec8..7db024cd0ae5 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/tokens/inventory/_token.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/inventory/_token.html.eex
@@ -1,30 +1,46 @@
+<% is_1155 = @token.type == "ERC-1155"%>
+<% is_unique = Chain.token_id_1155_is_unique?(@token.contract_address_hash, @token_transfer.token_id) or not is_1155%>
- <%= gettext "Unique Token" %>
+ <%= if is_unique do%>
+ <%= gettext "Unique Token" %>
+ <% else %>
+ <%= gettext "Not unique Token" %>
+ <% end %>
-
-
- <%= gettext "Token ID" %>:
-
- <%= link(@token_transfer.token_id, to: token_instance_path(@conn, :show, "#{@token.contract_address_hash}", "#{@token_transfer.token_id}")) %>
+ <%= if is_unique do %>
+
+
+ <%= gettext "Token ID" %>:
+
+ <%= link(@token_transfer.token_id, to: token_instance_path(@conn, :show, "#{@token.contract_address_hash}", "#{@token_transfer.token_id}")) %>
+
-
-
-
- <%= gettext "Owner Address" %>:
-
- <%= render BlockScoutWeb.AddressView,
- "_link.html",
- address: @token_transfer.to_address,
- contract: false,
- use_custom_tooltip: false %>
+
+ <%= gettext "Owner Address" %>:
+
+ <%= render BlockScoutWeb.AddressView,
+ "_link.html",
+ address: @token_transfer.to_address,
+ contract: false,
+ use_custom_tooltip: false %>
+
-
-
+
+ <% else %>
+
+
+ <%= gettext "Token ID" %>:
+
+ <%= link(@token_transfer.token_id, to: token_instance_path(@conn, :show, "#{@token.contract_address_hash}", "#{@token_transfer.token_id}")) %>
+
+
+
+ <% end %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/overview/_details.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/overview/_details.html.eex
index 1fb958e75118..79c3a77048b1 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/tokens/overview/_details.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/overview/_details.html.eex
@@ -106,7 +106,7 @@
<%= if @token.usd_value do %>
- <%= unless @token.custom_cap do %>
+ <%= unless Map.has_key?(@token, :custom_cap) && @token.custom_cap do %>
|
<% end %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/read_contract/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/read_contract/index.html.eex
index 811035aac2d0..da552eeeff89 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/tokens/read_contract/index.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/read_contract/index.html.eex
@@ -13,11 +13,7 @@
-
-
-
-
- <%= gettext("Loading...") %>
+ <%= render BlockScoutWeb.CommonComponentsView, "_loading_spinner.html", loading_text: gettext("Loading...") %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex
index 619fa0f58be3..c792e28a54e2 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex
@@ -3,14 +3,7 @@
- <%= cond do %>
- <% @token_transfer.to_address.hash == @burn_address_hash -> %>
- <%= gettext("Token Burning") %>
- <% @token_transfer.from_address.hash == @burn_address_hash -> %>
- <%= gettext("Token Minting") %>
- <% true -> %>
- <%= gettext("Token Transfer") %>
- <% end %>
+ <%= render(BlockScoutWeb.CommonComponentsView, "_token_transfer_type_display_name.html", type: Chain.get_token_transfer_type(@token_transfer)) %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex
index aad3bb75601d..c0c84d459fc5 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex
@@ -1,11 +1,13 @@
" class="table thead-light table-bordered">
+ <%= if !assigns[:error] do %>
+
+ <%= gettext "Method Id" %>
+ 0x<%= @method_id %>
+
+ <% end %>
- <%= gettext "Method Id" %>
- 0x<%= @method_id %>
-
-
- Call
+ <%= if assigns[:error], do: gettext("Error"), else: gettext("Call") %>
<%= @text %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_link_to_token_instance.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_link_to_token_instance.html.eex
new file mode 100644
index 000000000000..88273f4bc093
--- /dev/null
+++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_link_to_token_instance.html.eex
@@ -0,0 +1 @@
+<%= "[" %><%= link(short_token_id(@token_id, 30), to: token_instance_path(BlockScoutWeb.Endpoint, :show, @transfer.token.contract_address_hash, to_string(@token_id)), "data-test": "token_link") %><%= "]" %>
\ No newline at end of file
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_link_to_token_symbol.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_link_to_token_symbol.html.eex
new file mode 100644
index 000000000000..73cb4a2c2cd5
--- /dev/null
+++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_link_to_token_symbol.html.eex
@@ -0,0 +1 @@
+<%= link(token_symbol(@transfer.token), to: token_path(BlockScoutWeb.Endpoint, :show, @transfer.token.contract_address_hash), "data-test": "token_link") %>
\ No newline at end of file
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_total_transfers.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_total_transfers.html.eex
index c0b0d09119cb..fd9efa4435af 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_total_transfers.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_total_transfers.html.eex
@@ -1,8 +1,23 @@
<%= case token_transfer_amount(@transfer) do %>
<% {:ok, :erc721_instance} -> %>
- <%= "TokenID ["%><%= link(short_token_id(@transfer.token_id, 30), to: token_instance_path(BlockScoutWeb.Endpoint, :show, @transfer.token.contract_address_hash, to_string(@transfer.token_id)), "data-test": "token_link") %><%= "]" %>
+ <%= render BlockScoutWeb.TransactionView, "_transfer_token_with_id.html", transfer: @transfer, token_id: @transfer.token_id %>
+ <% {:ok, :erc1155_instance, value} -> %>
+ <% transfer_type = Chain.get_token_transfer_type(@transfer) %>
+ <%= if transfer_type == :token_spawning do %>
+ <%= render BlockScoutWeb.TransactionView, "_transfer_token_with_id.html", transfer: @transfer, token_id: @transfer.token_id %>
+ <% else %>
+ <%= "#{value} " %>
+ <%= render BlockScoutWeb.TransactionView, "_transfer_token_with_id.html", transfer: @transfer, token_id: @transfer.token_id %>
+ <% end %>
+ <% {:ok, :erc1155_instance, values, token_ids, _decimals} -> %>
+ <% values_ids = Enum.zip(values, token_ids) %>
+ <%= for {value, token_id} <- values_ids do %>
+
+ <%= "#{value} "%>
+ <%= render BlockScoutWeb.TransactionView, "_transfer_token_with_id.html", transfer: @transfer, token_id: token_id %>
+
+ <% end %>
<% {:ok, value} -> %>
<%= value %>
-<% end %>
-<%= " "%>
-<%= link(token_symbol(@transfer.token), to: token_path(BlockScoutWeb.Endpoint, :show, @transfer.token.contract_address_hash), "data-test": "token_link") %>
\ No newline at end of file
+ <%= " " %><%= render BlockScoutWeb.TransactionView, "_link_to_token_symbol.html", transfer: @transfer %>
+<% end %>
\ No newline at end of file
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_transfer_token_with_id.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_transfer_token_with_id.html.eex
new file mode 100644
index 000000000000..44a3444aa131
--- /dev/null
+++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_transfer_token_with_id.html.eex
@@ -0,0 +1,2 @@
+<%= "TokenID " %><%= render BlockScoutWeb.TransactionView, "_link_to_token_instance.html", transfer: @transfer, token_id: @token_id %>
+<%= " " %><%= render BlockScoutWeb.TransactionView, "_link_to_token_symbol.html", transfer: @transfer %>
\ No newline at end of file
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex
index 68c8f4df2d94..d4d45896eb25 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex
@@ -128,7 +128,19 @@
text: gettext("The revert reason of the transaction.") %>
<%= gettext "Revert reason" %>
- <%= BlockScoutWeb.TransactionView.transaction_revert_reason(@transaction) %>
+ <%= case BlockScoutWeb.TransactionView.transaction_revert_reason(@transaction) do %>
+ <% {:error, _contract_not_verified, candidates} when candidates != [] -> %>
+ <% {:ok, method_id, text, mapping} = Enum.at(candidates, 0) %>
+ <%= render(BlockScoutWeb.TransactionView, "_decoded_input_body.html", method_id: method_id, text: text, mapping: mapping, error: true) %>
+ <% {:ok, method_id, text, mapping} -> %>
+ <%= render(BlockScoutWeb.TransactionView, "_decoded_input_body.html", method_id: method_id, text: text, mapping: mapping, error: true) %>
+ <% _ -> %>
+ <% hex = BlockScoutWeb.TransactionView.get_pure_transaction_revert_reason(@transaction) %>
+ <% utf8 = BlockScoutWeb.TransactionView.decoded_revert_reason(@transaction) %>
+
+
Raw:<%= raw("\t") %><%= hex %><%= raw("\n") %>UTF-8:<%= raw("\t") %><%= utf8 %>
+
+ <% end %>
<% end %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex
index 3bbbe6a419b5..885990a377ae 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex
@@ -1,14 +1,7 @@
- <%= cond do %>
- <% @token_transfer.to_address.hash == @burn_address_hash -> %>
- <%= gettext("Token Burning") %>
- <% @token_transfer.from_address.hash == @burn_address_hash -> %>
- <%= gettext("Token Minting") %>
- <% true -> %>
- <%= gettext("Token Transfer") %>
- <% end %>
+ <%= render(BlockScoutWeb.CommonComponentsView, "_token_transfer_type_display_name.html", type: Chain.get_token_transfer_type(@token_transfer)) %>
diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/address_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/address_view.ex
index e9beb38bb033..fc27740dced3 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/address_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/address_view.ex
@@ -176,6 +176,12 @@ defmodule BlockScoutWeb.API.RPC.AddressView do
|> Map.put_new(:tokenID, token_transfer.token_id)
end
+ defp prepare_token_transfer(%{token_type: "ERC-1155"} = token_transfer) do
+ token_transfer
+ |> prepare_common_token_transfer()
+ |> Map.put_new(:tokenID, token_transfer.token_id)
+ end
+
defp prepare_token_transfer(%{token_type: "ERC-20"} = token_transfer) do
token_transfer
|> prepare_common_token_transfer()
diff --git a/apps/block_scout_web/lib/block_scout_web/views/currency_helpers.ex b/apps/block_scout_web/lib/block_scout_web/views/currency_helpers.ex
index 8d497b4c0b75..a54df9729817 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/currency_helpers.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/currency_helpers.ex
@@ -42,6 +42,15 @@ defmodule BlockScoutWeb.CurrencyHelpers do
iex> format_according_to_decimals(205000, Decimal.new(2))
"2,050"
+
+ iex> format_according_to_decimals(105000, Decimal.new(0))
+ "105,000"
+
+ iex> format_according_to_decimals(105000000000000000000, Decimal.new(100500))
+ "105"
+
+ iex> format_according_to_decimals(105000000000000000000, nil)
+ "105,000,000,000,000,000,000"
"""
@spec format_according_to_decimals(non_neg_integer() | nil, nil) :: String.t()
def format_according_to_decimals(nil, _) do
@@ -60,9 +69,13 @@ defmodule BlockScoutWeb.CurrencyHelpers do
@spec format_according_to_decimals(Decimal.t(), Decimal.t()) :: String.t()
def format_according_to_decimals(value, decimals) do
- value
- |> divide_decimals(decimals)
- |> thousands_separator()
+ if Decimal.cmp(decimals, 18) == :gt do
+ format_according_to_decimals(value, Decimal.new(18))
+ else
+ value
+ |> divide_decimals(decimals)
+ |> thousands_separator()
+ end
end
defp thousands_separator(value) do
diff --git a/apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex
index 770962cfb4b9..aaf42e5162be 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex
@@ -2,7 +2,7 @@ defmodule BlockScoutWeb.SmartContractView do
use BlockScoutWeb, :view
alias Explorer.Chain
- alias Explorer.Chain.Address
+ alias Explorer.Chain.{Address, Transaction}
alias Explorer.Chain.Hash.Address, as: HashAddress
alias Explorer.SmartContract.Helper
@@ -26,6 +26,15 @@ defmodule BlockScoutWeb.SmartContractView do
def outputs?(outputs) when is_nil(outputs), do: false
+ def error?(outputs) when not is_nil(outputs) do
+ case outputs do
+ {:error, _} -> true
+ _ -> false
+ end
+ end
+
+ def error?(outputs) when is_nil(outputs), do: false
+
def address?(type), do: type in ["address", "address payable"]
def int?(type), do: String.contains?(type, "int") && !String.contains?(type, "[")
@@ -113,7 +122,7 @@ defmodule BlockScoutWeb.SmartContractView do
def values_with_type(value, type, names, index, _components),
do: render_type_value(type, binary_to_utf_string(value), fetch_name(names, index))
- def values_with_type(value, :error, _components), do: render_type_value("error", value, nil)
+ def values_with_type(value, :error, _components), do: render_type_value("error", value, "error")
defp fetch_name(nil, _index), do: nil
@@ -337,4 +346,23 @@ defmodule BlockScoutWeb.SmartContractView do
type
end
end
+
+ def decode_revert_reason(to_address, revert_reason) do
+ smart_contract = Chain.address_hash_to_smart_contract(to_address)
+
+ Transaction.decoded_revert_reason(
+ %Transaction{to_address: %{smart_contract: smart_contract}, hash: to_address},
+ revert_reason
+ )
+ end
+
+ def decode_hex_revert_reason(hex_revert_reason) do
+ case Integer.parse(hex_revert_reason, 16) do
+ {number, ""} ->
+ :binary.encode_unsigned(number)
+
+ _ ->
+ hex_revert_reason
+ end
+ end
end
diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/helpers.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/helpers.ex
index 21a984bc6711..ebe5db74ba37 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/tokens/helpers.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/helpers.ex
@@ -16,27 +16,39 @@ defmodule BlockScoutWeb.Tokens.Helpers do
When the token's type is ERC-721, the function will return a string with the token_id that
represents the ERC-721 token since this kind of token doesn't have amount and decimals.
"""
+ def token_transfer_amount(%{token: token, amount: amount, amounts: amounts, token_id: token_id, token_ids: token_ids}) do
+ do_token_transfer_amount(token, amount, amounts, token_id, token_ids)
+ end
+
def token_transfer_amount(%{token: token, amount: amount, token_id: token_id}) do
- do_token_transfer_amount(token, amount, token_id)
+ do_token_transfer_amount(token, amount, nil, token_id, nil)
end
- defp do_token_transfer_amount(%Token{type: "ERC-20"}, nil, _token_id) do
+ defp do_token_transfer_amount(%Token{type: "ERC-20"}, nil, nil, _token_id, _token_ids) do
{:ok, "--"}
end
- defp do_token_transfer_amount(%Token{type: "ERC-20", decimals: nil}, amount, _token_id) do
+ defp do_token_transfer_amount(%Token{type: "ERC-20", decimals: nil}, amount, _amounts, _token_id, _token_ids) do
{:ok, CurrencyHelpers.format_according_to_decimals(amount, Decimal.new(0))}
end
- defp do_token_transfer_amount(%Token{type: "ERC-20", decimals: decimals}, amount, _token_id) do
+ defp do_token_transfer_amount(%Token{type: "ERC-20", decimals: decimals}, amount, _amounts, _token_id, _token_ids) do
{:ok, CurrencyHelpers.format_according_to_decimals(amount, decimals)}
end
- defp do_token_transfer_amount(%Token{type: "ERC-721"}, _amount, _token_id) do
+ defp do_token_transfer_amount(%Token{type: "ERC-721"}, _amount, _amounts, _token_id, _token_ids) do
{:ok, :erc721_instance}
end
- defp do_token_transfer_amount(_token, _amount, _token_id) do
+ defp do_token_transfer_amount(%Token{type: "ERC-1155", decimals: decimals}, amount, amounts, _token_id, token_ids) do
+ if amount do
+ {:ok, :erc1155_instance, CurrencyHelpers.format_according_to_decimals(amount, decimals)}
+ else
+ {:ok, :erc1155_instance, amounts, token_ids, decimals}
+ end
+ end
+
+ defp do_token_transfer_amount(_token, _amount, _amounts, _token_id, _token_ids) do
nil
end
diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/holder_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/holder_view.ex
index e185e3f9e8a3..2edfd9398119 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/tokens/holder_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/holder_view.ex
@@ -54,19 +54,23 @@ defmodule BlockScoutWeb.Tokens.HolderView do
## Examples
iex> token = build(:token, type: "ERC-20", decimals: Decimal.new(2))
- iex> BlockScoutWeb.Tokens.HolderView.format_token_balance_value(100000, token)
+ iex> BlockScoutWeb.Tokens.HolderView.format_token_balance_value(100000, nil, token)
"1,000"
iex> token = build(:token, type: "ERC-721")
- iex> BlockScoutWeb.Tokens.HolderView.format_token_balance_value(1, token)
+ iex> BlockScoutWeb.Tokens.HolderView.format_token_balance_value(1, nil, token)
1
"""
- def format_token_balance_value(value, %Token{type: "ERC-20", decimals: decimals}) do
+ def format_token_balance_value(value, _id, %Token{type: "ERC-20", decimals: decimals}) do
format_according_to_decimals(value, decimals)
end
- def format_token_balance_value(value, _token) do
+ def format_token_balance_value(value, id, %Token{type: "ERC-1155", decimals: decimals}) do
+ to_string(format_according_to_decimals(value, decimals)) <> " TokenID " <> to_string(id)
+ end
+
+ def format_token_balance_value(value, _id, _token) do
value
end
end
diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/holder_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/holder_view.ex
new file mode 100644
index 000000000000..38cf207bc435
--- /dev/null
+++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/holder_view.ex
@@ -0,0 +1,5 @@
+defmodule BlockScoutWeb.Tokens.Instance.HolderView do
+ use BlockScoutWeb, :view
+
+ alias BlockScoutWeb.Tokens.Instance.OverviewView
+end
diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex
index a539ef3d7f04..2e396b2b50e2 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex
@@ -2,6 +2,7 @@ defmodule BlockScoutWeb.Tokens.Instance.OverviewView do
use BlockScoutWeb, :view
alias BlockScoutWeb.CurrencyHelpers
+ alias Explorer.Chain
alias Explorer.Chain.{Address, SmartContract, Token}
alias Explorer.SmartContract.Helper
alias FileInfo
diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/inventory_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/inventory_view.ex
index e720e1b10524..547d6dd33a31 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/tokens/inventory_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/inventory_view.ex
@@ -4,4 +4,5 @@ defmodule BlockScoutWeb.Tokens.InventoryView do
import BlockScoutWeb.Tokens.Instance.OverviewView, only: [media_src: 1, media_type: 1]
alias BlockScoutWeb.Tokens.OverviewView
+ alias Explorer.Chain
end
diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/overview_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/overview_view.ex
index f18b68ee7434..d590d327d839 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/tokens/overview_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/overview_view.ex
@@ -44,6 +44,7 @@ defmodule BlockScoutWeb.Tokens.OverviewView do
defp tab_name(["inventory"]), do: gettext("Inventory")
def display_inventory?(%Token{type: "ERC-721"}), do: true
+ def display_inventory?(%Token{type: "ERC-1155"}), do: true
def display_inventory?(_), do: false
def smart_contract_with_read_only_functions?(
@@ -58,7 +59,7 @@ defmodule BlockScoutWeb.Tokens.OverviewView do
Get the total value of the token supply in USD.
"""
def total_supply_usd(token) do
- if token.custom_cap do
+ if Map.has_key?(token, :custom_cap) && token.custom_cap do
token.custom_cap
else
tokens = CurrencyHelpers.divide_decimals(token.total_supply, token.decimals)
diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/transfer_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/transfer_view.ex
index 96f29c5fe07d..3ea5d8484010 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/tokens/transfer_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/transfer_view.ex
@@ -2,5 +2,6 @@ defmodule BlockScoutWeb.Tokens.TransferView do
use BlockScoutWeb, :view
alias BlockScoutWeb.Tokens.OverviewView
+ alias Explorer.Chain
alias Explorer.Chain.Address
end
diff --git a/apps/block_scout_web/lib/block_scout_web/views/transaction_token_transfer_view.ex b/apps/block_scout_web/lib/block_scout_web/views/transaction_token_transfer_view.ex
index eb8fe810539e..66999ad804fc 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/transaction_token_transfer_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/transaction_token_transfer_view.ex
@@ -1,3 +1,5 @@
defmodule BlockScoutWeb.TransactionTokenTransferView do
use BlockScoutWeb, :view
+
+ alias Explorer.Chain
end
diff --git a/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex
index ab995282b5f8..ea74c005ec96 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex
@@ -184,33 +184,24 @@ defmodule BlockScoutWeb.TransactionView do
end)
new_acc1 =
- if token_transfer.token_id do
- [new_entry | acc1]
- else
- if existing_entry do
- acc1
- |> Enum.map(fn entry ->
- if entry.to_address_hash == token_transfer.to_address_hash &&
- entry.from_address_hash == token_transfer.from_address_hash &&
- entry.token == token_transfer.token do
- updated_entry =
- if new_entry.amount do
- %{
- entry
- | amount: Decimal.add(new_entry.amount, entry.amount)
- }
- else
- entry
- end
-
- updated_entry
- else
+ if existing_entry do
+ acc1
+ |> Enum.map(fn entry ->
+ if entry.to_address_hash == token_transfer.to_address_hash &&
+ entry.from_address_hash == token_transfer.from_address_hash &&
+ entry.token == token_transfer.token do
+ updated_entry = %{
entry
- end
- end)
- else
- [new_entry | acc1]
- end
+ | amount: Decimal.add(new_entry.amount, entry.amount)
+ }
+
+ updated_entry
+ else
+ entry
+ end
+ end)
+ else
+ [new_entry | acc1]
end
{new_acc1, acc2}
@@ -220,6 +211,7 @@ defmodule BlockScoutWeb.TransactionView do
case type do
:erc20 -> gettext("ERC-20 ")
:erc721 -> gettext("ERC-721 ")
+ :erc1155 -> gettext("ERC-1155 ")
_ -> ""
end
end
@@ -309,9 +301,6 @@ defmodule BlockScoutWeb.TransactionView do
def contract_creation?(_), do: false
- # def utf8_encode() do
- # end
-
def fee(%Transaction{} = transaction) do
{_, value} = Chain.fee(transaction, :wei)
value
@@ -336,9 +325,13 @@ defmodule BlockScoutWeb.TransactionView do
end
def transaction_revert_reason(transaction) do
- Chain.transaction_to_revert_reason(transaction)
+ transaction |> Chain.transaction_to_revert_reason() |> decoded_revert_reason(transaction)
end
+ def get_pure_transaction_revert_reason(nil), do: nil
+
+ def get_pure_transaction_revert_reason(transaction), do: Chain.transaction_to_revert_reason(transaction)
+
def empty_exchange_rate?(exchange_rate) do
Token.null?(exchange_rate)
end
@@ -379,6 +372,10 @@ defmodule BlockScoutWeb.TransactionView do
Transaction.decoded_input_data(transaction)
end
+ def decoded_revert_reason(revert_reason, transaction) do
+ Transaction.decoded_revert_reason(transaction, revert_reason)
+ end
+
@doc """
Converts a transaction's gas price to a displayable value.
"""
@@ -580,4 +577,33 @@ defmodule BlockScoutWeb.TransactionView do
defp template_to_string(template) when is_tuple(template) do
safe_to_string(template)
end
+
+ # Function decodes revert reason of the transaction
+ @spec decoded_revert_reason(%Transaction{} | nil) :: binary() | nil
+ def decoded_revert_reason(transaction) do
+ revert_reason = get_pure_transaction_revert_reason(transaction)
+
+ case revert_reason do
+ "0x" <> hex_part ->
+ proccess_hex_revert_reason(hex_part)
+
+ hex_part ->
+ proccess_hex_revert_reason(hex_part)
+ end
+ end
+
+ # Function converts hex revert reason to the binary
+ @spec proccess_hex_revert_reason(nil) :: nil
+ defp proccess_hex_revert_reason(nil), do: nil
+
+ @spec proccess_hex_revert_reason(binary()) :: binary()
+ defp proccess_hex_revert_reason(hex_revert_reason) do
+ case Integer.parse(hex_revert_reason, 16) do
+ {number, ""} ->
+ :binary.encode_unsigned(number)
+
+ _ ->
+ hex_revert_reason
+ end
+ end
end
diff --git a/apps/block_scout_web/lib/block_scout_web/web_router.ex b/apps/block_scout_web/lib/block_scout_web/web_router.ex
index 7d237421946c..fef036a0a1d9 100644
--- a/apps/block_scout_web/lib/block_scout_web/web_router.ex
+++ b/apps/block_scout_web/lib/block_scout_web/web_router.ex
@@ -295,6 +295,13 @@ defmodule BlockScoutWeb.WebRouter do
only: [:index],
as: :metadata
)
+
+ resources(
+ "/token-holders",
+ Tokens.Instance.HolderController,
+ only: [:index],
+ as: :holder
+ )
end
end
@@ -346,6 +353,13 @@ defmodule BlockScoutWeb.WebRouter do
only: [:index],
as: :metadata
)
+
+ resources(
+ "/token-holders",
+ Tokens.Instance.HolderController,
+ only: [:index],
+ as: :holder
+ )
end
end
diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot
index b3bed9cf5b1d..762e7de2c1c4 100644
--- a/apps/block_scout_web/priv/gettext/default.pot
+++ b/apps/block_scout_web/priv/gettext/default.pot
@@ -98,7 +98,7 @@ msgid "%{subnetwork} Staking DApp - BlockScout"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/views/transaction_view.ex:356
+#: lib/block_scout_web/views/transaction_view.ex:349
msgid "(Awaiting internal transactions for status)"
msgstr ""
@@ -116,7 +116,7 @@ msgid "(query)"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/app.html.eex:188
+#: lib/block_scout_web/templates/layout/app.html.eex:185
msgid "- We're indexing this chain right now. Some of the counts may be inaccurate."
msgstr ""
@@ -192,7 +192,7 @@ msgid "Action"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:414
+#: lib/block_scout_web/templates/transaction/overview.html.eex:426
msgid "Actual gas amount used by the transaction."
msgstr ""
@@ -208,12 +208,12 @@ msgid "Address"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:201
+#: lib/block_scout_web/templates/transaction/overview.html.eex:213
msgid "Address (external or contract) receiving the transaction."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:181
+#: lib/block_scout_web/templates/transaction/overview.html.eex:193
msgid "Address (external or contract) sending the transaction."
msgstr ""
@@ -273,7 +273,7 @@ msgid "Amount"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:400
+#: lib/block_scout_web/templates/transaction/overview.html.eex:412
msgid "Amount of"
msgstr ""
@@ -363,19 +363,19 @@ msgid "Become a Candidate"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:435
+#: lib/block_scout_web/templates/transaction/overview.html.eex:447
msgid "Binary data included with the transaction. See input / logs below for additional info."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_coin_balance/_coin_balances.html.eex:8
-#: lib/block_scout_web/templates/block/overview.html.eex:26 lib/block_scout_web/templates/transaction/overview.html.eex:140
+#: lib/block_scout_web/templates/block/overview.html.eex:26 lib/block_scout_web/templates/transaction/overview.html.eex:152
msgid "Block"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/_link.html.eex:2
-#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:28 lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:50
+#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:28 lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:43
msgid "Block #%{number}"
msgstr ""
@@ -421,12 +421,12 @@ msgid "Block number"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:139
+#: lib/block_scout_web/templates/transaction/overview.html.eex:151
msgid "Block number containing the transaction."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address/overview.html.eex:249
+#: lib/block_scout_web/templates/address/overview.html.eex:254
msgid "Block number in which the address was updated."
msgstr ""
@@ -448,7 +448,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:54
-#: lib/block_scout_web/templates/address/overview.html.eex:266 lib/block_scout_web/templates/address_validation/index.html.eex:15
+#: lib/block_scout_web/templates/address/overview.html.eex:271 lib/block_scout_web/templates/address_validation/index.html.eex:15
#: lib/block_scout_web/views/address_view.ex:415
msgid "Blocks Validated"
msgstr ""
@@ -496,8 +496,8 @@ msgstr ""
#: lib/block_scout_web/templates/layout/_topnav.html.eex:102 lib/block_scout_web/templates/layout/app.html.eex:45
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:89 lib/block_scout_web/templates/smart_contract/_functions.html.eex:89
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:125 lib/block_scout_web/templates/transaction/_pending_tile.html.eex:20
-#: lib/block_scout_web/templates/transaction/_tile.html.eex:37 lib/block_scout_web/templates/transaction/overview.html.eex:400
-#: lib/block_scout_web/views/transaction_view.ex:506 lib/block_scout_web/views/wei_helpers.ex:80
+#: lib/block_scout_web/templates/transaction/_tile.html.eex:37 lib/block_scout_web/templates/transaction/overview.html.eex:412
+#: lib/block_scout_web/views/transaction_view.ex:503 lib/block_scout_web/views/wei_helpers.ex:80
msgid "CELO"
msgstr ""
@@ -506,6 +506,11 @@ msgstr ""
msgid "CELO burned from transactions included in the block (Base fee (per unit of gas) * Gas Used)."
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/address_token/overview.html.eex:66
+msgid "CRC Worth"
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/common_components/_csv_export_button.html.eex:2
msgid "CSV"
@@ -517,6 +522,7 @@ msgid "CSV is being prepared, please wait..."
msgstr ""
#, elixir-format
+#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:10
#: lib/block_scout_web/views/internal_transaction_view.ex:21
msgid "Call"
msgstr ""
@@ -527,10 +533,10 @@ msgid "Call Code"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:108
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:305
-#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:56
-#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:93 lib/block_scout_web/templates/api_docs/_action_tile.html.eex:47
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:104
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:301
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:52
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:89 lib/block_scout_web/templates/api_docs/_action_tile.html.eex:47
#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:54
msgid "Cancel"
msgstr ""
@@ -619,7 +625,7 @@ msgid "Coin Balance History"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:53
+#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:55
msgid "Collapse"
msgstr ""
@@ -630,12 +636,12 @@ msgid "Compiler"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:69
+#: lib/block_scout_web/templates/address_contract/index.html.eex:57
msgid "Compiler version"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/views/transaction_view.ex:349
+#: lib/block_scout_web/views/transaction_view.ex:342
msgid "Confirmed"
msgstr ""
@@ -645,7 +651,7 @@ msgid "Confirmed by "
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:172
+#: lib/block_scout_web/templates/transaction/overview.html.eex:184
msgid "Confirmed within"
msgstr ""
@@ -680,17 +686,17 @@ msgid "Connection Lost, click to load newer validations"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:94
+#: lib/block_scout_web/templates/address_contract/index.html.eex:82
msgid "Constructor Arguments"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:211
+#: lib/block_scout_web/templates/transaction/overview.html.eex:223
msgid "Contract"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:132
+#: lib/block_scout_web/templates/address_contract/index.html.eex:120
msgid "Contract ABI"
msgstr ""
@@ -709,18 +715,18 @@ msgid "Contract Address Pending"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/views/transaction_view.ex:460
+#: lib/block_scout_web/views/transaction_view.ex:457
msgid "Contract Call"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/views/transaction_view.ex:457
+#: lib/block_scout_web/views/transaction_view.ex:454
msgid "Contract Creation"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:148
-#: lib/block_scout_web/templates/address_contract/index.html.eex:163
+#: lib/block_scout_web/templates/address_contract/index.html.eex:136
+#: lib/block_scout_web/templates/address_contract/index.html.eex:151
msgid "Contract Creation Code"
msgstr ""
@@ -748,12 +754,12 @@ msgid "Contract name:"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:104
+#: lib/block_scout_web/templates/address_contract/index.html.eex:92
msgid "Contract source code"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:154
+#: lib/block_scout_web/templates/address_contract/index.html.eex:142
msgid "Contracts that self destruct in their constructors have no contract code published and cannot be verified."
msgstr ""
@@ -763,7 +769,7 @@ msgid "Contribute"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:134
+#: lib/block_scout_web/templates/address_contract/index.html.eex:122
msgid "Copy ABI"
msgstr ""
@@ -776,8 +782,8 @@ msgid "Copy Address"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:150
-#: lib/block_scout_web/templates/address_contract/index.html.eex:166
+#: lib/block_scout_web/templates/address_contract/index.html.eex:138
+#: lib/block_scout_web/templates/address_contract/index.html.eex:154
msgid "Copy Contract Creation Code"
msgstr ""
@@ -787,15 +793,15 @@ msgid "Copy Decompiled Contract Code"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:199
-#: lib/block_scout_web/templates/address_contract/index.html.eex:209
+#: lib/block_scout_web/templates/address_contract/index.html.eex:181
+#: lib/block_scout_web/templates/address_contract/index.html.eex:191
msgid "Copy Deployed ByteCode"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex:14
-#: lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex:15 lib/block_scout_web/templates/transaction/overview.html.eex:191
-#: lib/block_scout_web/templates/transaction/overview.html.eex:192
+#: lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex:15 lib/block_scout_web/templates/transaction/overview.html.eex:203
+#: lib/block_scout_web/templates/transaction/overview.html.eex:204
msgid "Copy From Address"
msgstr ""
@@ -822,16 +828,16 @@ msgid "Copy Raw Trace"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:106
-#: lib/block_scout_web/templates/address_contract/index.html.eex:120
+#: lib/block_scout_web/templates/address_contract/index.html.eex:94
+#: lib/block_scout_web/templates/address_contract/index.html.eex:108
msgid "Copy Source Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex:31
-#: lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex:32 lib/block_scout_web/templates/transaction/overview.html.eex:220
-#: lib/block_scout_web/templates/transaction/overview.html.eex:221 lib/block_scout_web/templates/transaction/overview.html.eex:230
-#: lib/block_scout_web/templates/transaction/overview.html.eex:231
+#: lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex:32 lib/block_scout_web/templates/transaction/overview.html.eex:232
+#: lib/block_scout_web/templates/transaction/overview.html.eex:233 lib/block_scout_web/templates/transaction/overview.html.eex:242
+#: lib/block_scout_web/templates/transaction/overview.html.eex:243
msgid "Copy To Address"
msgstr ""
@@ -852,19 +858,19 @@ msgid "Copy Txn Hash"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:461
+#: lib/block_scout_web/templates/transaction/overview.html.eex:473
msgid "Copy Txn Hex Input"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:467
+#: lib/block_scout_web/templates/transaction/overview.html.eex:479
msgid "Copy Txn UTF-8 Input"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:20
-#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:39 lib/block_scout_web/templates/transaction/overview.html.eex:460
-#: lib/block_scout_web/templates/transaction/overview.html.eex:466
+#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:41 lib/block_scout_web/templates/transaction/overview.html.eex:472
+#: lib/block_scout_web/templates/transaction/overview.html.eex:478
msgid "Copy Value"
msgstr ""
@@ -922,7 +928,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:104
-#: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:7 lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:21
+#: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:7 lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:23
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:120
msgid "Data"
msgstr ""
@@ -933,7 +939,7 @@ msgid "Date & time at which block was produced."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:158
+#: lib/block_scout_web/templates/transaction/overview.html.eex:170
msgid "Date & time of transaction inclusion, including length of time for confirmation."
msgstr ""
@@ -998,8 +1004,8 @@ msgid "Delegators’ Staked Amount"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:197
-#: lib/block_scout_web/templates/address_contract/index.html.eex:205
+#: lib/block_scout_web/templates/address_contract/index.html.eex:179
+#: lib/block_scout_web/templates/address_contract/index.html.eex:187
msgid "Deployed ByteCode"
msgstr ""
@@ -1022,7 +1028,7 @@ msgid "Difficulty"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:155
+#: lib/block_scout_web/templates/address_contract/index.html.eex:143
msgid "Displaying the init data provided of the creating transaction."
msgstr ""
@@ -1058,12 +1064,17 @@ msgid "EIP-1167"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/views/transaction_view.ex:221
+#: lib/block_scout_web/views/transaction_view.ex:214
+msgid "ERC-1155 "
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/views/transaction_view.ex:212
msgid "ERC-20 "
msgstr ""
#, elixir-format
-#: lib/block_scout_web/views/transaction_view.ex:222
+#: lib/block_scout_web/views/transaction_view.ex:213
msgid "ERC-721 "
msgstr ""
@@ -1073,7 +1084,7 @@ msgid "ETH RPC API Documentation"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:80
+#: lib/block_scout_web/templates/address_contract/index.html.eex:68
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:82
msgid "EVM Version"
msgstr ""
@@ -1115,23 +1126,28 @@ msgid "Epochs range(s) or enum, e.g.: 5-9,23-27,47,50"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:31
+#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:10
+msgid "Error"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:33
msgid "Error rendering value"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address/_balance_card.html.eex:35
-#: lib/block_scout_web/templates/address/_balance_dropdown.html.eex:14
+#: lib/block_scout_web/templates/address/_balance_card.html.eex:31
+#: lib/block_scout_web/templates/address/_balance_card.html.eex:36 lib/block_scout_web/templates/address/_balance_dropdown.html.eex:10
msgid "Error trying to fetch balances."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/views/transaction_view.ex:360
+#: lib/block_scout_web/views/transaction_view.ex:353
msgid "Error: %{reason}"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/views/transaction_view.ex:358
+#: lib/block_scout_web/views/transaction_view.ex:351
msgid "Error: (Awaiting internal transactions for reason)"
msgstr ""
@@ -1163,7 +1179,7 @@ msgid "Execute"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:53
+#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:55
msgid "Expand"
msgstr ""
@@ -1173,7 +1189,7 @@ msgid "Export Data"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:246
+#: lib/block_scout_web/templates/address_contract/index.html.eex:222
msgid "External libraries"
msgstr ""
@@ -1194,11 +1210,28 @@ msgid "Favorites"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address/_balance_card.html.eex:32
-#: lib/block_scout_web/templates/address/_balance_dropdown.html.eex:11
+#: lib/block_scout_web/templates/address/overview.html.eex:245
+msgid "Fetching gas used..."
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address/_balance_card.html.eex:28
+#: lib/block_scout_web/templates/address/_balance_card.html.eex:33 lib/block_scout_web/templates/address/_balance_dropdown.html.eex:7
msgid "Fetching tokens..."
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/address/overview.html.eex:208
+#: lib/block_scout_web/templates/address/overview.html.eex:212
+msgid "Fetching transactions..."
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address/overview.html.eex:227
+#: lib/block_scout_web/templates/address/overview.html.eex:231
+msgid "Fetching transfers..."
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/admin/dashboard/index.html.eex:16
msgid "For any existing contracts in the database, insert all ABI entries into the contract_methods table. Use this in case you have verified smart contracts before early March 2019 and you want other contracts with the same functions to show those ABI's as candidate matches."
@@ -1212,7 +1245,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:42
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:40 lib/block_scout_web/templates/address_transaction/index.html.eex:38
-#: lib/block_scout_web/templates/transaction/overview.html.eex:182 lib/block_scout_web/views/address_internal_transaction_view.ex:10
+#: lib/block_scout_web/templates/transaction/overview.html.eex:194 lib/block_scout_web/views/address_internal_transaction_view.ex:10
#: lib/block_scout_web/views/address_token_transfer_view.ex:10 lib/block_scout_web/views/address_transaction_view.ex:10
msgid "From"
msgstr ""
@@ -1225,12 +1258,12 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/_tile.html.eex:68
#: lib/block_scout_web/templates/block/overview.html.eex:198 lib/block_scout_web/templates/block/overview.html.eex:290
-#: lib/block_scout_web/templates/transaction/overview.html.eex:362
+#: lib/block_scout_web/templates/transaction/overview.html.eex:374
msgid "Gas Limit"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:342
+#: lib/block_scout_web/templates/transaction/overview.html.eex:354
msgid "Gas Price"
msgstr ""
@@ -1240,20 +1273,20 @@ msgid "Gas Tracker"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address/overview.html.eex:237
+#: lib/block_scout_web/templates/address/overview.html.eex:241
#: lib/block_scout_web/templates/block/_tile.html.eex:74 lib/block_scout_web/templates/block/overview.html.eex:189
#: lib/block_scout_web/templates/block/overview.html.eex:283
msgid "Gas Used"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:415
+#: lib/block_scout_web/templates/transaction/overview.html.eex:427
msgid "Gas Used by Transaction"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address/overview.html.eex:236
-#: lib/block_scout_web/templates/address/overview.html.eex:265
+#: lib/block_scout_web/templates/address/overview.html.eex:240
+#: lib/block_scout_web/templates/address/overview.html.eex:270
msgid "Gas used by the address."
msgstr ""
@@ -1284,8 +1317,8 @@ msgid "Hash"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:443
-#: lib/block_scout_web/templates/transaction/overview.html.eex:447
+#: lib/block_scout_web/templates/transaction/overview.html.eex:455
+#: lib/block_scout_web/templates/transaction/overview.html.eex:459
msgid "Hex (Default)"
msgstr ""
@@ -1330,11 +1363,6 @@ msgstr ""
msgid "Implementation"
msgstr ""
-#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:63
-msgid "Implementation Name:"
-msgstr ""
-
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:148
msgid "Implementation address of the proxy contract."
@@ -1346,7 +1374,7 @@ msgid "Inactive Pool Addresses. Current validator pools are specified by a check
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:427
+#: lib/block_scout_web/templates/transaction/overview.html.eex:439
msgid "Index position of Transaction in the block."
msgstr ""
@@ -1371,7 +1399,7 @@ msgid "Input"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:203
+#: lib/block_scout_web/templates/transaction/overview.html.eex:215
msgid "Interacted With (To)"
msgstr ""
@@ -1384,7 +1412,7 @@ msgstr ""
#: lib/block_scout_web/templates/address/_tabs.html.eex:28
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:21 lib/block_scout_web/templates/transaction/_tabs.html.eex:11
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:6 lib/block_scout_web/views/address_view.ex:405
-#: lib/block_scout_web/views/transaction_view.ex:527
+#: lib/block_scout_web/views/transaction_view.ex:524
msgid "Internal Transactions"
msgstr ""
@@ -1399,11 +1427,6 @@ msgstr ""
msgid "Inventory"
msgstr ""
-#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:58
-msgid "Is a Proxy?"
-msgstr ""
-
#, elixir-format
#: lib/block_scout_web/templates/transaction/not_found.html.eex:16
msgid "It could still be in the TX Pool of a different node, waiting to be broadcasted."
@@ -1420,7 +1443,7 @@ msgid "JSON RPC error"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address/overview.html.eex:250
+#: lib/block_scout_web/templates/address/overview.html.eex:255
msgid "Last Balance Update"
msgstr ""
@@ -1468,45 +1491,40 @@ msgid "Likelihood of Becoming a Validator on the Next Epoch"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:293
+#: lib/block_scout_web/templates/transaction/overview.html.eex:305
msgid "List of ERC-1155 tokens created in the transaction."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:277
+#: lib/block_scout_web/templates/transaction/overview.html.eex:289
msgid "List of token burnt in the transaction."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:260
+#: lib/block_scout_web/templates/transaction/overview.html.eex:272
msgid "List of token minted in the transaction."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:244
+#: lib/block_scout_web/templates/transaction/overview.html.eex:256
msgid "List of token transferred in the transaction."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:26
-msgid "Loading chart"
+#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:22
+msgid "Loading chart..."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_read_contract/index.html.eex:16
-#: lib/block_scout_web/templates/address_read_proxy/index.html.eex:16 lib/block_scout_web/templates/address_write_contract/index.html.eex:16
-#: lib/block_scout_web/templates/address_write_proxy/index.html.eex:16 lib/block_scout_web/templates/tokens/read_contract/index.html.eex:20
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:77
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:295
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:46
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:83 lib/block_scout_web/templates/address_read_contract/index.html.eex:12
+#: lib/block_scout_web/templates/address_read_proxy/index.html.eex:12 lib/block_scout_web/templates/address_write_contract/index.html.eex:12
+#: lib/block_scout_web/templates/address_write_proxy/index.html.eex:12 lib/block_scout_web/templates/tokens/read_contract/index.html.eex:16
msgid "Loading..."
msgstr ""
-#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:81
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:299
-#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:50
-#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:87
-msgid "Loading...."
-msgstr ""
-
#, elixir-format
#: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:2
msgid "Log Data"
@@ -1521,7 +1539,7 @@ msgstr ""
#: lib/block_scout_web/templates/address/_tabs.html.eex:47
#: lib/block_scout_web/templates/address_logs/index.html.eex:10 lib/block_scout_web/templates/transaction/_tabs.html.eex:17
#: lib/block_scout_web/templates/transaction_log/index.html.eex:8 lib/block_scout_web/views/address_view.ex:416
-#: lib/block_scout_web/views/transaction_view.ex:528
+#: lib/block_scout_web/views/transaction_view.ex:525
msgid "Logs"
msgstr ""
@@ -1552,38 +1570,38 @@ msgid "Max Amount to Move:"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:371
+#: lib/block_scout_web/templates/transaction/overview.html.eex:383
msgid "Max Fee per Gas"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:381
+#: lib/block_scout_web/templates/transaction/overview.html.eex:393
msgid "Max Priority Fee per Gas"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/views/transaction_view.ex:330
+#: lib/block_scout_web/views/transaction_view.ex:319
msgid "Max of"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:361
+#: lib/block_scout_web/templates/transaction/overview.html.eex:373
msgid "Maximum gas amount approved for the transaction."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:370
+#: lib/block_scout_web/templates/transaction/overview.html.eex:382
msgid "Maximum total amount per unit of gas a user is willing to pay for a transaction, including base fee and priority fee."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/instance/metadata/index.html.eex:18
-#: lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex:10 lib/block_scout_web/views/tokens/instance/overview_view.ex:178
+#: lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex:10 lib/block_scout_web/views/tokens/instance/overview_view.ex:179
msgid "Metadata"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:4
+#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:5
msgid "Method Id"
msgstr ""
@@ -1662,10 +1680,15 @@ msgstr ""
msgid "N/A"
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/block/overview.html.eex:110
+msgid "N/A bytes"
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:52
#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:59 lib/block_scout_web/templates/log/_data_decoded_view.html.eex:4
-#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:19
+#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:21
msgid "Name"
msgstr ""
@@ -1691,8 +1714,8 @@ msgid "New Vyper Smart Contract Verification"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:84
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:91 lib/block_scout_web/templates/address_contract_verification/new.html.eex:99
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:80
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:87 lib/block_scout_web/templates/address_contract_verification/new.html.eex:95
msgid "Next"
msgstr ""
@@ -1715,17 +1738,22 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:208
-#: lib/block_scout_web/templates/transaction/overview.html.eex:425
+#: lib/block_scout_web/templates/transaction/overview.html.eex:437
msgid "Nonce"
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/tokens/inventory/_token.html.eex:11
+msgid "Not unique Token"
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:202
msgid "Number of transactions related to this address."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address/overview.html.eex:219
+#: lib/block_scout_web/templates/address/overview.html.eex:221
msgid "Number of transfers to/from this address."
msgstr ""
@@ -1751,7 +1779,7 @@ msgid "Optimization enabled"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:74
+#: lib/block_scout_web/templates/address_contract/index.html.eex:62
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:114
msgid "Optimization runs"
msgstr ""
@@ -1773,7 +1801,7 @@ msgid "Other Explorers"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/tokens/inventory/_token.html.eex:18
+#: lib/block_scout_web/templates/tokens/inventory/_token.html.eex:24
msgid "Owner Address"
msgstr ""
@@ -1801,8 +1829,8 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:47
-#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:184 lib/block_scout_web/views/transaction_view.ex:355
-#: lib/block_scout_web/views/transaction_view.ex:389
+#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:184 lib/block_scout_web/views/transaction_view.ex:348
+#: lib/block_scout_web/views/transaction_view.ex:386
msgid "Pending"
msgstr ""
@@ -1849,7 +1877,7 @@ msgid "Pools searching is already in progress for this address"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:427
+#: lib/block_scout_web/templates/transaction/overview.html.eex:439
msgid "Position"
msgstr ""
@@ -1882,13 +1910,13 @@ msgid "Price"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:341
+#: lib/block_scout_web/templates/transaction/overview.html.eex:353
msgid "Price per unit of gas specified by the sender. Higher gas prices can prioritize transaction inclusion during times of high usage."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:239
-#: lib/block_scout_web/templates/transaction/overview.html.eex:391
+#: lib/block_scout_web/templates/transaction/overview.html.eex:403
msgid "Priority Fee / Tip"
msgstr ""
@@ -1919,13 +1947,13 @@ msgid "RPC"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:436
+#: lib/block_scout_web/templates/transaction/overview.html.eex:448
msgid "Raw Input"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:24
-#: lib/block_scout_web/templates/transaction_raw_trace/index.html.eex:7 lib/block_scout_web/views/transaction_view.ex:529
+#: lib/block_scout_web/templates/transaction_raw_trace/index.html.eex:7 lib/block_scout_web/views/transaction_view.ex:526
msgid "Raw Trace"
msgstr ""
@@ -1973,9 +2001,9 @@ msgid "Request URL"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:302
-#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:53
-#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:90
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:298
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:49
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:86
msgid "Reset"
msgstr ""
@@ -2008,7 +2036,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/_tile.html.eex:53
-#: lib/block_scout_web/templates/chain/_block.html.eex:26 lib/block_scout_web/views/internal_transaction_view.ex:28
+#: lib/block_scout_web/templates/chain/_block.html.eex:27 lib/block_scout_web/views/internal_transaction_view.ex:28
msgid "Reward"
msgstr ""
@@ -2119,6 +2147,11 @@ msgstr ""
msgid "Shows the tokens held in the address (includes ERC-20, ERC-721 and ERC-1155)."
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/address_token/overview.html.eex:67
+msgid "Shows the total CRC balance in the address."
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/address_token/overview.html.eex:45
msgid "Shows total assets held in the address"
@@ -2141,15 +2174,16 @@ msgid "Size of the block in bytes."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:38
+#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:34
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:54 lib/block_scout_web/templates/address_logs/index.html.eex:23
#: lib/block_scout_web/templates/address_signed/index.html.eex:23 lib/block_scout_web/templates/address_token/index.html.eex:57
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:58 lib/block_scout_web/templates/address_transaction/index.html.eex:54
#: lib/block_scout_web/templates/address_validation/index.html.eex:24 lib/block_scout_web/templates/block_transaction/index.html.eex:29
#: lib/block_scout_web/templates/chain/show.html.eex:179 lib/block_scout_web/templates/pending_transaction/index.html.eex:22
#: lib/block_scout_web/templates/stakes/_table.html.eex:49 lib/block_scout_web/templates/tokens/holder/index.html.eex:27
-#: lib/block_scout_web/templates/tokens/instance/transfer/index.html.eex:23 lib/block_scout_web/templates/tokens/inventory/index.html.eex:22
-#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:21 lib/block_scout_web/templates/transaction/index.html.eex:29
+#: lib/block_scout_web/templates/tokens/instance/holder/index.html.eex:23 lib/block_scout_web/templates/tokens/instance/transfer/index.html.eex:23
+#: lib/block_scout_web/templates/tokens/inventory/index.html.eex:22 lib/block_scout_web/templates/tokens/transfer/index.html.eex:21
+#: lib/block_scout_web/templates/transaction/index.html.eex:29
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:13 lib/block_scout_web/templates/transaction_log/index.html.eex:15
#: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:14
msgid "Something went wrong, click to reload."
@@ -2249,7 +2283,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_emission_reward_tile.html.eex:8
-#: lib/block_scout_web/views/transaction_view.ex:357
+#: lib/block_scout_web/views/transaction_view.ex:350
msgid "Success"
msgstr ""
@@ -2465,13 +2499,13 @@ msgid "There are no transactions."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/tokens/instance/transfer/index.html.eex:28
-#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:26
+#: lib/block_scout_web/templates/tokens/instance/holder/index.html.eex:28
+#: lib/block_scout_web/templates/tokens/instance/transfer/index.html.eex:28 lib/block_scout_web/templates/tokens/transfer/index.html.eex:26
msgid "There are no transfers for this Token."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:43
+#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:39
msgid "There is no coin history for this address."
msgstr ""
@@ -2486,7 +2520,7 @@ msgid "There is no information currently available for this view. Deselect filte
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:29
+#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:25
#: lib/block_scout_web/templates/chain/show.html.eex:8
msgid "There was a problem loading the chart."
msgstr ""
@@ -2544,14 +2578,14 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:65
-#: lib/block_scout_web/templates/transaction/overview.html.eex:159
+#: lib/block_scout_web/templates/transaction/overview.html.eex:171
msgid "Timestamp"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:36
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:34 lib/block_scout_web/templates/address_transaction/index.html.eex:32
-#: lib/block_scout_web/templates/transaction/overview.html.eex:205 lib/block_scout_web/views/address_internal_transaction_view.ex:9
+#: lib/block_scout_web/templates/transaction/overview.html.eex:217 lib/block_scout_web/views/address_internal_transaction_view.ex:9
#: lib/block_scout_web/views/address_token_transfer_view.ex:9 lib/block_scout_web/views/address_transaction_view.ex:9
msgid "To"
msgstr ""
@@ -2578,13 +2612,12 @@ msgid "Token"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:8
-#: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:6 lib/block_scout_web/views/transaction_view.ex:451
+#: lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex:3 lib/block_scout_web/views/transaction_view.ex:448
msgid "Token Burning"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/views/transaction_view.ex:452
+#: lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex:7 lib/block_scout_web/views/transaction_view.ex:449
msgid "Token Creation"
msgstr ""
@@ -2596,25 +2629,25 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/holder/index.html.eex:20
+#: lib/block_scout_web/templates/tokens/instance/holder/index.html.eex:16 lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex:17
#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:9 lib/block_scout_web/views/tokens/overview_view.ex:42
msgid "Token Holders"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:38
-#: lib/block_scout_web/templates/tokens/inventory/_token.html.eex:11
+#: lib/block_scout_web/templates/tokens/inventory/_token.html.eex:18 lib/block_scout_web/templates/tokens/inventory/_token.html.eex:37
msgid "Token ID"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:10
-#: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:8 lib/block_scout_web/views/transaction_view.ex:450
+#: lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex:5 lib/block_scout_web/views/transaction_view.ex:447
msgid "Token Minting"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:12
-#: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:10 lib/block_scout_web/views/transaction_view.ex:453
+#: lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex:9
+#: lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex:11 lib/block_scout_web/views/transaction_view.ex:450
msgid "Token Transfer"
msgstr ""
@@ -2624,8 +2657,8 @@ msgstr ""
#: lib/block_scout_web/templates/tokens/instance/transfer/index.html.eex:16 lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:3
#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:14 lib/block_scout_web/templates/transaction/_tabs.html.eex:4
#: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:7 lib/block_scout_web/views/address_view.ex:407
-#: lib/block_scout_web/views/tokens/instance/overview_view.ex:177 lib/block_scout_web/views/tokens/overview_view.ex:41
-#: lib/block_scout_web/views/transaction_view.ex:526
+#: lib/block_scout_web/views/tokens/instance/overview_view.ex:178 lib/block_scout_web/views/tokens/overview_view.ex:41
+#: lib/block_scout_web/views/transaction_view.ex:523
msgid "Token Transfers"
msgstr ""
@@ -2644,22 +2677,22 @@ msgid "Tokens"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:278
+#: lib/block_scout_web/templates/transaction/overview.html.eex:290
msgid "Tokens Burnt"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:294
+#: lib/block_scout_web/templates/transaction/overview.html.eex:306
msgid "Tokens Created"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:261
+#: lib/block_scout_web/templates/transaction/overview.html.eex:273
msgid "Tokens Minted"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:245
+#: lib/block_scout_web/templates/transaction/overview.html.eex:257
msgid "Tokens Transferred"
msgstr ""
@@ -2705,7 +2738,7 @@ msgid "Total gas limit provided by all transactions in the block."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:325
+#: lib/block_scout_web/templates/transaction/overview.html.eex:337
msgid "Total transaction fee."
msgstr ""
@@ -2721,7 +2754,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:19
-#: lib/block_scout_web/views/transaction_view.ex:463
+#: lib/block_scout_web/views/transaction_view.ex:460
msgid "Transaction"
msgstr ""
@@ -2736,7 +2769,7 @@ msgid "Transaction %{transaction}, %{subnetwork} %{transaction}"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:401
+#: lib/block_scout_web/templates/transaction/overview.html.eex:413
msgid "Transaction Burnt Fee"
msgstr ""
@@ -2746,7 +2779,7 @@ msgid "Transaction Details"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:326
+#: lib/block_scout_web/templates/transaction/overview.html.eex:338
msgid "Transaction Fee"
msgstr ""
@@ -2757,22 +2790,22 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:2
-#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:17
+#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:19
msgid "Transaction Inputs"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:351
+#: lib/block_scout_web/templates/transaction/overview.html.eex:363
msgid "Transaction Type"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:424
+#: lib/block_scout_web/templates/transaction/overview.html.eex:436
msgid "Transaction number from the sending address. Each transaction sent from an address increments the nonce by 1."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:350
+#: lib/block_scout_web/templates/transaction/overview.html.eex:362
msgid "Transaction type, introduced in EIP-2718."
msgstr ""
@@ -2796,7 +2829,7 @@ msgid "Transactions sent"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address/overview.html.eex:220
+#: lib/block_scout_web/templates/address/overview.html.eex:222
#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:50
msgid "Transfers"
msgstr ""
@@ -2825,12 +2858,12 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:5
-#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:20
+#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:22
msgid "Type"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:450
+#: lib/block_scout_web/templates/transaction/overview.html.eex:462
msgid "UTF-8"
msgstr ""
@@ -2850,12 +2883,12 @@ msgid "Uncles"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/views/transaction_view.ex:348
+#: lib/block_scout_web/views/transaction_view.ex:341
msgid "Unconfirmed"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/tokens/inventory/_token.html.eex:6
+#: lib/block_scout_web/templates/tokens/inventory/_token.html.eex:9
msgid "Unique Token"
msgstr ""
@@ -2887,12 +2920,12 @@ msgid "Use the search box to find a hosted network, or select from the list of a
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:380
+#: lib/block_scout_web/templates/transaction/overview.html.eex:392
msgid "User defined maximum fee (tip) per unit of gas paid to validator for transaction prioritization."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:390
+#: lib/block_scout_web/templates/transaction/overview.html.eex:402
msgid "User-defined tip sent to validator for transaction priority/inclusion."
msgstr ""
@@ -2954,32 +2987,32 @@ msgid "Validator pools can be banned for misbehavior (such as not revealing secr
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:311
+#: lib/block_scout_web/templates/transaction/overview.html.eex:323
msgid "Value"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:310
+#: lib/block_scout_web/templates/transaction/overview.html.eex:322
msgid "Value sent in CELO (and USD) if applicable."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:86
+#: lib/block_scout_web/templates/address_contract/index.html.eex:74
msgid "Verified at"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:24
-#: lib/block_scout_web/templates/address_contract/index.html.eex:170 lib/block_scout_web/templates/address_contract/index.html.eex:182
-#: lib/block_scout_web/templates/address_contract/index.html.eex:213 lib/block_scout_web/templates/address_contract/index.html.eex:225
+#: lib/block_scout_web/templates/address_contract/index.html.eex:158 lib/block_scout_web/templates/address_contract/index.html.eex:164
+#: lib/block_scout_web/templates/address_contract/index.html.eex:195 lib/block_scout_web/templates/address_contract/index.html.eex:201
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:13
msgid "Verify & Publish"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:301
-#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:52
-#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:89
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:297
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:48
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:85
msgid "Verify & publish"
msgstr ""
@@ -3056,7 +3089,7 @@ msgid "WEI"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_pending_contract_write.html.eex:12
+#: lib/block_scout_web/templates/smart_contract/_pending_contract_write.html.eex:9
msgid "Waiting for transaction's confirmation..."
msgstr ""
@@ -3201,7 +3234,7 @@ msgid "balance of the address"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:400
+#: lib/block_scout_web/templates/transaction/overview.html.eex:412
msgid "burned for this transaction. Equals Block Base Fee per Gas * Gas Used."
msgstr ""
@@ -3216,7 +3249,7 @@ msgid "candidate"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:216
+#: lib/block_scout_web/templates/transaction/overview.html.eex:228
msgid "created"
msgstr ""
@@ -3256,6 +3289,11 @@ msgstr ""
msgid "is not a valid transaction hash"
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/smart_contract/_function_response.html.eex:3
+msgid "method Response"
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/common_components/_pagination_container.html.eex:41
msgid "of"
diff --git a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
index fbc307c004cb..29f1af2efec0 100644
--- a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
+++ b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
@@ -99,7 +99,7 @@ msgid "%{subnetwork} Staking DApp - BlockScout"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/views/transaction_view.ex:356
+#: lib/block_scout_web/views/transaction_view.ex:349
msgid "(Awaiting internal transactions for status)"
msgstr ""
@@ -117,7 +117,7 @@ msgid "(query)"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/app.html.eex:188
+#: lib/block_scout_web/templates/layout/app.html.eex:185
msgid "- We're indexing this chain right now. Some of the counts may be inaccurate."
msgstr ""
@@ -193,7 +193,7 @@ msgid "Action"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:414
+#: lib/block_scout_web/templates/transaction/overview.html.eex:426
msgid "Actual gas amount used by the transaction."
msgstr ""
@@ -209,12 +209,12 @@ msgid "Address"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:201
+#: lib/block_scout_web/templates/transaction/overview.html.eex:213
msgid "Address (external or contract) receiving the transaction."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:181
+#: lib/block_scout_web/templates/transaction/overview.html.eex:193
msgid "Address (external or contract) sending the transaction."
msgstr ""
@@ -274,7 +274,7 @@ msgid "Amount"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:400
+#: lib/block_scout_web/templates/transaction/overview.html.eex:412
msgid "Amount of"
msgstr ""
@@ -364,19 +364,19 @@ msgid "Become a Candidate"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:435
+#: lib/block_scout_web/templates/transaction/overview.html.eex:447
msgid "Binary data included with the transaction. See input / logs below for additional info."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_coin_balance/_coin_balances.html.eex:8
-#: lib/block_scout_web/templates/block/overview.html.eex:26 lib/block_scout_web/templates/transaction/overview.html.eex:140
+#: lib/block_scout_web/templates/block/overview.html.eex:26 lib/block_scout_web/templates/transaction/overview.html.eex:152
msgid "Block"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/_link.html.eex:2
-#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:28 lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:50
+#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:28 lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:43
msgid "Block #%{number}"
msgstr ""
@@ -422,12 +422,12 @@ msgid "Block number"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:139
+#: lib/block_scout_web/templates/transaction/overview.html.eex:151
msgid "Block number containing the transaction."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address/overview.html.eex:249
+#: lib/block_scout_web/templates/address/overview.html.eex:254
msgid "Block number in which the address was updated."
msgstr ""
@@ -449,7 +449,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:54
-#: lib/block_scout_web/templates/address/overview.html.eex:266 lib/block_scout_web/templates/address_validation/index.html.eex:15
+#: lib/block_scout_web/templates/address/overview.html.eex:271 lib/block_scout_web/templates/address_validation/index.html.eex:15
#: lib/block_scout_web/views/address_view.ex:415
msgid "Blocks Validated"
msgstr ""
@@ -497,8 +497,8 @@ msgstr ""
#: lib/block_scout_web/templates/layout/_topnav.html.eex:102 lib/block_scout_web/templates/layout/app.html.eex:45
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:89 lib/block_scout_web/templates/smart_contract/_functions.html.eex:89
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:125 lib/block_scout_web/templates/transaction/_pending_tile.html.eex:20
-#: lib/block_scout_web/templates/transaction/_tile.html.eex:37 lib/block_scout_web/templates/transaction/overview.html.eex:400
-#: lib/block_scout_web/views/transaction_view.ex:506 lib/block_scout_web/views/wei_helpers.ex:80
+#: lib/block_scout_web/templates/transaction/_tile.html.eex:37 lib/block_scout_web/templates/transaction/overview.html.eex:412
+#: lib/block_scout_web/views/transaction_view.ex:503 lib/block_scout_web/views/wei_helpers.ex:80
msgid "CELO"
msgstr ""
@@ -507,6 +507,11 @@ msgstr ""
msgid "CELO burned from transactions included in the block (Base fee (per unit of gas) * Gas Used)."
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/address_token/overview.html.eex:66
+msgid "CRC Worth"
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/common_components/_csv_export_button.html.eex:2
msgid "CSV"
@@ -518,6 +523,7 @@ msgid "CSV is being prepared, please wait..."
msgstr ""
#, elixir-format
+#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:10
#: lib/block_scout_web/views/internal_transaction_view.ex:21
msgid "Call"
msgstr ""
@@ -528,10 +534,10 @@ msgid "Call Code"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:108
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:305
-#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:56
-#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:93 lib/block_scout_web/templates/api_docs/_action_tile.html.eex:47
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:104
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:301
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:52
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:89 lib/block_scout_web/templates/api_docs/_action_tile.html.eex:47
#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:54
msgid "Cancel"
msgstr ""
@@ -620,7 +626,7 @@ msgid "Coin Balance History"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:53
+#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:55
msgid "Collapse"
msgstr ""
@@ -631,12 +637,12 @@ msgid "Compiler"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:69
+#: lib/block_scout_web/templates/address_contract/index.html.eex:57
msgid "Compiler version"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/views/transaction_view.ex:349
+#: lib/block_scout_web/views/transaction_view.ex:342
msgid "Confirmed"
msgstr ""
@@ -646,7 +652,7 @@ msgid "Confirmed by "
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:172
+#: lib/block_scout_web/templates/transaction/overview.html.eex:184
msgid "Confirmed within"
msgstr ""
@@ -681,17 +687,17 @@ msgid "Connection Lost, click to load newer validations"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:94
+#: lib/block_scout_web/templates/address_contract/index.html.eex:82
msgid "Constructor Arguments"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:211
+#: lib/block_scout_web/templates/transaction/overview.html.eex:223
msgid "Contract"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:132
+#: lib/block_scout_web/templates/address_contract/index.html.eex:120
msgid "Contract ABI"
msgstr ""
@@ -710,18 +716,18 @@ msgid "Contract Address Pending"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/views/transaction_view.ex:460
+#: lib/block_scout_web/views/transaction_view.ex:457
msgid "Contract Call"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/views/transaction_view.ex:457
+#: lib/block_scout_web/views/transaction_view.ex:454
msgid "Contract Creation"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:148
-#: lib/block_scout_web/templates/address_contract/index.html.eex:163
+#: lib/block_scout_web/templates/address_contract/index.html.eex:136
+#: lib/block_scout_web/templates/address_contract/index.html.eex:151
msgid "Contract Creation Code"
msgstr ""
@@ -749,12 +755,12 @@ msgid "Contract name:"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:104
+#: lib/block_scout_web/templates/address_contract/index.html.eex:92
msgid "Contract source code"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:154
+#: lib/block_scout_web/templates/address_contract/index.html.eex:142
msgid "Contracts that self destruct in their constructors have no contract code published and cannot be verified."
msgstr ""
@@ -764,7 +770,7 @@ msgid "Contribute"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:134
+#: lib/block_scout_web/templates/address_contract/index.html.eex:122
msgid "Copy ABI"
msgstr ""
@@ -777,8 +783,8 @@ msgid "Copy Address"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:150
-#: lib/block_scout_web/templates/address_contract/index.html.eex:166
+#: lib/block_scout_web/templates/address_contract/index.html.eex:138
+#: lib/block_scout_web/templates/address_contract/index.html.eex:154
msgid "Copy Contract Creation Code"
msgstr ""
@@ -788,15 +794,15 @@ msgid "Copy Decompiled Contract Code"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:199
-#: lib/block_scout_web/templates/address_contract/index.html.eex:209
+#: lib/block_scout_web/templates/address_contract/index.html.eex:181
+#: lib/block_scout_web/templates/address_contract/index.html.eex:191
msgid "Copy Deployed ByteCode"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex:14
-#: lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex:15 lib/block_scout_web/templates/transaction/overview.html.eex:191
-#: lib/block_scout_web/templates/transaction/overview.html.eex:192
+#: lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex:15 lib/block_scout_web/templates/transaction/overview.html.eex:203
+#: lib/block_scout_web/templates/transaction/overview.html.eex:204
msgid "Copy From Address"
msgstr ""
@@ -823,16 +829,16 @@ msgid "Copy Raw Trace"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:106
-#: lib/block_scout_web/templates/address_contract/index.html.eex:120
+#: lib/block_scout_web/templates/address_contract/index.html.eex:94
+#: lib/block_scout_web/templates/address_contract/index.html.eex:108
msgid "Copy Source Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex:31
-#: lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex:32 lib/block_scout_web/templates/transaction/overview.html.eex:220
-#: lib/block_scout_web/templates/transaction/overview.html.eex:221 lib/block_scout_web/templates/transaction/overview.html.eex:230
-#: lib/block_scout_web/templates/transaction/overview.html.eex:231
+#: lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex:32 lib/block_scout_web/templates/transaction/overview.html.eex:232
+#: lib/block_scout_web/templates/transaction/overview.html.eex:233 lib/block_scout_web/templates/transaction/overview.html.eex:242
+#: lib/block_scout_web/templates/transaction/overview.html.eex:243
msgid "Copy To Address"
msgstr ""
@@ -853,19 +859,19 @@ msgid "Copy Txn Hash"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:461
+#: lib/block_scout_web/templates/transaction/overview.html.eex:473
msgid "Copy Txn Hex Input"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:467
+#: lib/block_scout_web/templates/transaction/overview.html.eex:479
msgid "Copy Txn UTF-8 Input"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:20
-#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:39 lib/block_scout_web/templates/transaction/overview.html.eex:460
-#: lib/block_scout_web/templates/transaction/overview.html.eex:466
+#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:41 lib/block_scout_web/templates/transaction/overview.html.eex:472
+#: lib/block_scout_web/templates/transaction/overview.html.eex:478
msgid "Copy Value"
msgstr ""
@@ -923,7 +929,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:104
-#: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:7 lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:21
+#: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:7 lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:23
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:120
msgid "Data"
msgstr ""
@@ -934,7 +940,7 @@ msgid "Date & time at which block was produced."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:158
+#: lib/block_scout_web/templates/transaction/overview.html.eex:170
msgid "Date & time of transaction inclusion, including length of time for confirmation."
msgstr ""
@@ -999,8 +1005,8 @@ msgid "Delegators’ Staked Amount"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:197
-#: lib/block_scout_web/templates/address_contract/index.html.eex:205
+#: lib/block_scout_web/templates/address_contract/index.html.eex:179
+#: lib/block_scout_web/templates/address_contract/index.html.eex:187
msgid "Deployed ByteCode"
msgstr ""
@@ -1023,7 +1029,7 @@ msgid "Difficulty"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:155
+#: lib/block_scout_web/templates/address_contract/index.html.eex:143
msgid "Displaying the init data provided of the creating transaction."
msgstr ""
@@ -1059,12 +1065,17 @@ msgid "EIP-1167"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/views/transaction_view.ex:221
+#: lib/block_scout_web/views/transaction_view.ex:214
+msgid "ERC-1155 "
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/views/transaction_view.ex:212
msgid "ERC-20 "
msgstr ""
#, elixir-format
-#: lib/block_scout_web/views/transaction_view.ex:222
+#: lib/block_scout_web/views/transaction_view.ex:213
msgid "ERC-721 "
msgstr ""
@@ -1074,7 +1085,7 @@ msgid "ETH RPC API Documentation"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:80
+#: lib/block_scout_web/templates/address_contract/index.html.eex:68
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:82
msgid "EVM Version"
msgstr ""
@@ -1116,23 +1127,28 @@ msgid "Epochs range(s) or enum, e.g.: 5-9,23-27,47,50"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:31
+#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:10
+msgid "Error"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:33
msgid "Error rendering value"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address/_balance_card.html.eex:35
-#: lib/block_scout_web/templates/address/_balance_dropdown.html.eex:14
+#: lib/block_scout_web/templates/address/_balance_card.html.eex:31
+#: lib/block_scout_web/templates/address/_balance_card.html.eex:36 lib/block_scout_web/templates/address/_balance_dropdown.html.eex:10
msgid "Error trying to fetch balances."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/views/transaction_view.ex:360
+#: lib/block_scout_web/views/transaction_view.ex:353
msgid "Error: %{reason}"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/views/transaction_view.ex:358
+#: lib/block_scout_web/views/transaction_view.ex:351
msgid "Error: (Awaiting internal transactions for reason)"
msgstr ""
@@ -1164,7 +1180,7 @@ msgid "Execute"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:53
+#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:55
msgid "Expand"
msgstr ""
@@ -1174,7 +1190,7 @@ msgid "Export Data"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:246
+#: lib/block_scout_web/templates/address_contract/index.html.eex:222
msgid "External libraries"
msgstr ""
@@ -1195,11 +1211,28 @@ msgid "Favorites"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address/_balance_card.html.eex:32
-#: lib/block_scout_web/templates/address/_balance_dropdown.html.eex:11
+#: lib/block_scout_web/templates/address/overview.html.eex:245
+msgid "Fetching gas used..."
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address/_balance_card.html.eex:28
+#: lib/block_scout_web/templates/address/_balance_card.html.eex:33 lib/block_scout_web/templates/address/_balance_dropdown.html.eex:7
msgid "Fetching tokens..."
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/address/overview.html.eex:208
+#: lib/block_scout_web/templates/address/overview.html.eex:212
+msgid "Fetching transactions..."
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address/overview.html.eex:227
+#: lib/block_scout_web/templates/address/overview.html.eex:231
+msgid "Fetching transfers..."
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/admin/dashboard/index.html.eex:16
msgid "For any existing contracts in the database, insert all ABI entries into the contract_methods table. Use this in case you have verified smart contracts before early March 2019 and you want other contracts with the same functions to show those ABI's as candidate matches."
@@ -1213,7 +1246,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:42
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:40 lib/block_scout_web/templates/address_transaction/index.html.eex:38
-#: lib/block_scout_web/templates/transaction/overview.html.eex:182 lib/block_scout_web/views/address_internal_transaction_view.ex:10
+#: lib/block_scout_web/templates/transaction/overview.html.eex:194 lib/block_scout_web/views/address_internal_transaction_view.ex:10
#: lib/block_scout_web/views/address_token_transfer_view.ex:10 lib/block_scout_web/views/address_transaction_view.ex:10
msgid "From"
msgstr ""
@@ -1226,12 +1259,12 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/_tile.html.eex:68
#: lib/block_scout_web/templates/block/overview.html.eex:198 lib/block_scout_web/templates/block/overview.html.eex:290
-#: lib/block_scout_web/templates/transaction/overview.html.eex:362
+#: lib/block_scout_web/templates/transaction/overview.html.eex:374
msgid "Gas Limit"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:342
+#: lib/block_scout_web/templates/transaction/overview.html.eex:354
msgid "Gas Price"
msgstr ""
@@ -1241,20 +1274,20 @@ msgid "Gas Tracker"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address/overview.html.eex:237
+#: lib/block_scout_web/templates/address/overview.html.eex:241
#: lib/block_scout_web/templates/block/_tile.html.eex:74 lib/block_scout_web/templates/block/overview.html.eex:189
#: lib/block_scout_web/templates/block/overview.html.eex:283
msgid "Gas Used"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:415
+#: lib/block_scout_web/templates/transaction/overview.html.eex:427
msgid "Gas Used by Transaction"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address/overview.html.eex:236
-#: lib/block_scout_web/templates/address/overview.html.eex:265
+#: lib/block_scout_web/templates/address/overview.html.eex:240
+#: lib/block_scout_web/templates/address/overview.html.eex:270
msgid "Gas used by the address."
msgstr ""
@@ -1285,8 +1318,8 @@ msgid "Hash"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:443
-#: lib/block_scout_web/templates/transaction/overview.html.eex:447
+#: lib/block_scout_web/templates/transaction/overview.html.eex:455
+#: lib/block_scout_web/templates/transaction/overview.html.eex:459
msgid "Hex (Default)"
msgstr ""
@@ -1331,11 +1364,6 @@ msgstr ""
msgid "Implementation"
msgstr ""
-#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:63
-msgid "Implementation Name:"
-msgstr ""
-
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:148
msgid "Implementation address of the proxy contract."
@@ -1347,7 +1375,7 @@ msgid "Inactive Pool Addresses. Current validator pools are specified by a check
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:427
+#: lib/block_scout_web/templates/transaction/overview.html.eex:439
msgid "Index position of Transaction in the block."
msgstr ""
@@ -1372,7 +1400,7 @@ msgid "Input"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:203
+#: lib/block_scout_web/templates/transaction/overview.html.eex:215
msgid "Interacted With (To)"
msgstr ""
@@ -1385,7 +1413,7 @@ msgstr ""
#: lib/block_scout_web/templates/address/_tabs.html.eex:28
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:21 lib/block_scout_web/templates/transaction/_tabs.html.eex:11
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:6 lib/block_scout_web/views/address_view.ex:405
-#: lib/block_scout_web/views/transaction_view.ex:527
+#: lib/block_scout_web/views/transaction_view.ex:524
msgid "Internal Transactions"
msgstr ""
@@ -1400,11 +1428,6 @@ msgstr ""
msgid "Inventory"
msgstr ""
-#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:58
-msgid "Is a Proxy?"
-msgstr ""
-
#, elixir-format
#: lib/block_scout_web/templates/transaction/not_found.html.eex:16
msgid "It could still be in the TX Pool of a different node, waiting to be broadcasted."
@@ -1421,7 +1444,7 @@ msgid "JSON RPC error"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address/overview.html.eex:250
+#: lib/block_scout_web/templates/address/overview.html.eex:255
msgid "Last Balance Update"
msgstr ""
@@ -1469,45 +1492,40 @@ msgid "Likelihood of Becoming a Validator on the Next Epoch"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:293
+#: lib/block_scout_web/templates/transaction/overview.html.eex:305
msgid "List of ERC-1155 tokens created in the transaction."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:277
+#: lib/block_scout_web/templates/transaction/overview.html.eex:289
msgid "List of token burnt in the transaction."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:260
+#: lib/block_scout_web/templates/transaction/overview.html.eex:272
msgid "List of token minted in the transaction."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:244
+#: lib/block_scout_web/templates/transaction/overview.html.eex:256
msgid "List of token transferred in the transaction."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:26
-msgid "Loading chart"
+#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:22
+msgid "Loading chart..."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_read_contract/index.html.eex:16
-#: lib/block_scout_web/templates/address_read_proxy/index.html.eex:16 lib/block_scout_web/templates/address_write_contract/index.html.eex:16
-#: lib/block_scout_web/templates/address_write_proxy/index.html.eex:16 lib/block_scout_web/templates/tokens/read_contract/index.html.eex:20
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:77
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:295
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:46
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:83 lib/block_scout_web/templates/address_read_contract/index.html.eex:12
+#: lib/block_scout_web/templates/address_read_proxy/index.html.eex:12 lib/block_scout_web/templates/address_write_contract/index.html.eex:12
+#: lib/block_scout_web/templates/address_write_proxy/index.html.eex:12 lib/block_scout_web/templates/tokens/read_contract/index.html.eex:16
msgid "Loading..."
msgstr ""
-#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:81
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:299
-#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:50
-#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:87
-msgid "Loading...."
-msgstr ""
-
#, elixir-format
#: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:2
msgid "Log Data"
@@ -1522,7 +1540,7 @@ msgstr ""
#: lib/block_scout_web/templates/address/_tabs.html.eex:47
#: lib/block_scout_web/templates/address_logs/index.html.eex:10 lib/block_scout_web/templates/transaction/_tabs.html.eex:17
#: lib/block_scout_web/templates/transaction_log/index.html.eex:8 lib/block_scout_web/views/address_view.ex:416
-#: lib/block_scout_web/views/transaction_view.ex:528
+#: lib/block_scout_web/views/transaction_view.ex:525
msgid "Logs"
msgstr ""
@@ -1553,38 +1571,38 @@ msgid "Max Amount to Move:"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:371
+#: lib/block_scout_web/templates/transaction/overview.html.eex:383
msgid "Max Fee per Gas"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:381
+#: lib/block_scout_web/templates/transaction/overview.html.eex:393
msgid "Max Priority Fee per Gas"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/views/transaction_view.ex:330
+#: lib/block_scout_web/views/transaction_view.ex:319
msgid "Max of"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:361
+#: lib/block_scout_web/templates/transaction/overview.html.eex:373
msgid "Maximum gas amount approved for the transaction."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:370
+#: lib/block_scout_web/templates/transaction/overview.html.eex:382
msgid "Maximum total amount per unit of gas a user is willing to pay for a transaction, including base fee and priority fee."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/instance/metadata/index.html.eex:18
-#: lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex:10 lib/block_scout_web/views/tokens/instance/overview_view.ex:178
+#: lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex:10 lib/block_scout_web/views/tokens/instance/overview_view.ex:179
msgid "Metadata"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:4
+#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:5
msgid "Method Id"
msgstr ""
@@ -1663,10 +1681,15 @@ msgstr ""
msgid "N/A"
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/block/overview.html.eex:110
+msgid "N/A bytes"
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:52
#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:59 lib/block_scout_web/templates/log/_data_decoded_view.html.eex:4
-#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:19
+#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:21
msgid "Name"
msgstr ""
@@ -1692,8 +1715,8 @@ msgid "New Vyper Smart Contract Verification"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:84
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:91 lib/block_scout_web/templates/address_contract_verification/new.html.eex:99
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:80
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:87 lib/block_scout_web/templates/address_contract_verification/new.html.eex:95
msgid "Next"
msgstr ""
@@ -1716,17 +1739,22 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:208
-#: lib/block_scout_web/templates/transaction/overview.html.eex:425
+#: lib/block_scout_web/templates/transaction/overview.html.eex:437
msgid "Nonce"
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/tokens/inventory/_token.html.eex:11
+msgid "Not unique Token"
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:202
msgid "Number of transactions related to this address."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address/overview.html.eex:219
+#: lib/block_scout_web/templates/address/overview.html.eex:221
msgid "Number of transfers to/from this address."
msgstr ""
@@ -1752,7 +1780,7 @@ msgid "Optimization enabled"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:74
+#: lib/block_scout_web/templates/address_contract/index.html.eex:62
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:114
msgid "Optimization runs"
msgstr ""
@@ -1774,7 +1802,7 @@ msgid "Other Explorers"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/tokens/inventory/_token.html.eex:18
+#: lib/block_scout_web/templates/tokens/inventory/_token.html.eex:24
msgid "Owner Address"
msgstr ""
@@ -1802,8 +1830,8 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:47
-#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:184 lib/block_scout_web/views/transaction_view.ex:355
-#: lib/block_scout_web/views/transaction_view.ex:389
+#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:184 lib/block_scout_web/views/transaction_view.ex:348
+#: lib/block_scout_web/views/transaction_view.ex:386
msgid "Pending"
msgstr ""
@@ -1850,7 +1878,7 @@ msgid "Pools searching is already in progress for this address"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:427
+#: lib/block_scout_web/templates/transaction/overview.html.eex:439
msgid "Position"
msgstr ""
@@ -1883,13 +1911,13 @@ msgid "Price"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:341
+#: lib/block_scout_web/templates/transaction/overview.html.eex:353
msgid "Price per unit of gas specified by the sender. Higher gas prices can prioritize transaction inclusion during times of high usage."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:239
-#: lib/block_scout_web/templates/transaction/overview.html.eex:391
+#: lib/block_scout_web/templates/transaction/overview.html.eex:403
msgid "Priority Fee / Tip"
msgstr ""
@@ -1920,13 +1948,13 @@ msgid "RPC"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:436
+#: lib/block_scout_web/templates/transaction/overview.html.eex:448
msgid "Raw Input"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:24
-#: lib/block_scout_web/templates/transaction_raw_trace/index.html.eex:7 lib/block_scout_web/views/transaction_view.ex:529
+#: lib/block_scout_web/templates/transaction_raw_trace/index.html.eex:7 lib/block_scout_web/views/transaction_view.ex:526
msgid "Raw Trace"
msgstr ""
@@ -1974,9 +2002,9 @@ msgid "Request URL"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:302
-#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:53
-#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:90
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:298
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:49
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:86
msgid "Reset"
msgstr ""
@@ -2009,7 +2037,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/_tile.html.eex:53
-#: lib/block_scout_web/templates/chain/_block.html.eex:26 lib/block_scout_web/views/internal_transaction_view.ex:28
+#: lib/block_scout_web/templates/chain/_block.html.eex:27 lib/block_scout_web/views/internal_transaction_view.ex:28
msgid "Reward"
msgstr ""
@@ -2120,6 +2148,11 @@ msgstr ""
msgid "Shows the tokens held in the address (includes ERC-20, ERC-721 and ERC-1155)."
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/address_token/overview.html.eex:67
+msgid "Shows the total CRC balance in the address."
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/address_token/overview.html.eex:45
msgid "Shows total assets held in the address"
@@ -2142,15 +2175,16 @@ msgid "Size of the block in bytes."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:38
+#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:34
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:54 lib/block_scout_web/templates/address_logs/index.html.eex:23
#: lib/block_scout_web/templates/address_signed/index.html.eex:23 lib/block_scout_web/templates/address_token/index.html.eex:57
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:58 lib/block_scout_web/templates/address_transaction/index.html.eex:54
#: lib/block_scout_web/templates/address_validation/index.html.eex:24 lib/block_scout_web/templates/block_transaction/index.html.eex:29
#: lib/block_scout_web/templates/chain/show.html.eex:179 lib/block_scout_web/templates/pending_transaction/index.html.eex:22
#: lib/block_scout_web/templates/stakes/_table.html.eex:49 lib/block_scout_web/templates/tokens/holder/index.html.eex:27
-#: lib/block_scout_web/templates/tokens/instance/transfer/index.html.eex:23 lib/block_scout_web/templates/tokens/inventory/index.html.eex:22
-#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:21 lib/block_scout_web/templates/transaction/index.html.eex:29
+#: lib/block_scout_web/templates/tokens/instance/holder/index.html.eex:23 lib/block_scout_web/templates/tokens/instance/transfer/index.html.eex:23
+#: lib/block_scout_web/templates/tokens/inventory/index.html.eex:22 lib/block_scout_web/templates/tokens/transfer/index.html.eex:21
+#: lib/block_scout_web/templates/transaction/index.html.eex:29
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:13 lib/block_scout_web/templates/transaction_log/index.html.eex:15
#: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:14
msgid "Something went wrong, click to reload."
@@ -2250,7 +2284,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_emission_reward_tile.html.eex:8
-#: lib/block_scout_web/views/transaction_view.ex:357
+#: lib/block_scout_web/views/transaction_view.ex:350
msgid "Success"
msgstr ""
@@ -2466,13 +2500,13 @@ msgid "There are no transactions."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/tokens/instance/transfer/index.html.eex:28
-#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:26
+#: lib/block_scout_web/templates/tokens/instance/holder/index.html.eex:28
+#: lib/block_scout_web/templates/tokens/instance/transfer/index.html.eex:28 lib/block_scout_web/templates/tokens/transfer/index.html.eex:26
msgid "There are no transfers for this Token."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:43
+#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:39
msgid "There is no coin history for this address."
msgstr ""
@@ -2487,7 +2521,7 @@ msgid "There is no information currently available for this view. Deselect filte
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:29
+#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:25
#: lib/block_scout_web/templates/chain/show.html.eex:8
msgid "There was a problem loading the chart."
msgstr ""
@@ -2545,14 +2579,14 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:65
-#: lib/block_scout_web/templates/transaction/overview.html.eex:159
+#: lib/block_scout_web/templates/transaction/overview.html.eex:171
msgid "Timestamp"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:36
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:34 lib/block_scout_web/templates/address_transaction/index.html.eex:32
-#: lib/block_scout_web/templates/transaction/overview.html.eex:205 lib/block_scout_web/views/address_internal_transaction_view.ex:9
+#: lib/block_scout_web/templates/transaction/overview.html.eex:217 lib/block_scout_web/views/address_internal_transaction_view.ex:9
#: lib/block_scout_web/views/address_token_transfer_view.ex:9 lib/block_scout_web/views/address_transaction_view.ex:9
msgid "To"
msgstr ""
@@ -2579,13 +2613,12 @@ msgid "Token"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:8
-#: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:6 lib/block_scout_web/views/transaction_view.ex:451
+#: lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex:3 lib/block_scout_web/views/transaction_view.ex:448
msgid "Token Burning"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/views/transaction_view.ex:452
+#: lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex:7 lib/block_scout_web/views/transaction_view.ex:449
msgid "Token Creation"
msgstr ""
@@ -2597,25 +2630,25 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/holder/index.html.eex:20
+#: lib/block_scout_web/templates/tokens/instance/holder/index.html.eex:16 lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex:17
#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:9 lib/block_scout_web/views/tokens/overview_view.ex:42
msgid "Token Holders"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:38
-#: lib/block_scout_web/templates/tokens/inventory/_token.html.eex:11
+#: lib/block_scout_web/templates/tokens/inventory/_token.html.eex:18 lib/block_scout_web/templates/tokens/inventory/_token.html.eex:37
msgid "Token ID"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:10
-#: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:8 lib/block_scout_web/views/transaction_view.ex:450
+#: lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex:5 lib/block_scout_web/views/transaction_view.ex:447
msgid "Token Minting"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:12
-#: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:10 lib/block_scout_web/views/transaction_view.ex:453
+#: lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex:9
+#: lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex:11 lib/block_scout_web/views/transaction_view.ex:450
msgid "Token Transfer"
msgstr ""
@@ -2625,8 +2658,8 @@ msgstr ""
#: lib/block_scout_web/templates/tokens/instance/transfer/index.html.eex:16 lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:3
#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:14 lib/block_scout_web/templates/transaction/_tabs.html.eex:4
#: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:7 lib/block_scout_web/views/address_view.ex:407
-#: lib/block_scout_web/views/tokens/instance/overview_view.ex:177 lib/block_scout_web/views/tokens/overview_view.ex:41
-#: lib/block_scout_web/views/transaction_view.ex:526
+#: lib/block_scout_web/views/tokens/instance/overview_view.ex:178 lib/block_scout_web/views/tokens/overview_view.ex:41
+#: lib/block_scout_web/views/transaction_view.ex:523
msgid "Token Transfers"
msgstr ""
@@ -2645,22 +2678,22 @@ msgid "Tokens"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:278
+#: lib/block_scout_web/templates/transaction/overview.html.eex:290
msgid "Tokens Burnt"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:294
+#: lib/block_scout_web/templates/transaction/overview.html.eex:306
msgid "Tokens Created"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:261
+#: lib/block_scout_web/templates/transaction/overview.html.eex:273
msgid "Tokens Minted"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:245
+#: lib/block_scout_web/templates/transaction/overview.html.eex:257
msgid "Tokens Transferred"
msgstr ""
@@ -2706,7 +2739,7 @@ msgid "Total gas limit provided by all transactions in the block."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:325
+#: lib/block_scout_web/templates/transaction/overview.html.eex:337
msgid "Total transaction fee."
msgstr ""
@@ -2722,7 +2755,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:19
-#: lib/block_scout_web/views/transaction_view.ex:463
+#: lib/block_scout_web/views/transaction_view.ex:460
msgid "Transaction"
msgstr ""
@@ -2737,7 +2770,7 @@ msgid "Transaction %{transaction}, %{subnetwork} %{transaction}"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:401
+#: lib/block_scout_web/templates/transaction/overview.html.eex:413
msgid "Transaction Burnt Fee"
msgstr ""
@@ -2747,7 +2780,7 @@ msgid "Transaction Details"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:326
+#: lib/block_scout_web/templates/transaction/overview.html.eex:338
msgid "Transaction Fee"
msgstr ""
@@ -2758,22 +2791,22 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:2
-#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:17
+#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:19
msgid "Transaction Inputs"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:351
+#: lib/block_scout_web/templates/transaction/overview.html.eex:363
msgid "Transaction Type"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:424
+#: lib/block_scout_web/templates/transaction/overview.html.eex:436
msgid "Transaction number from the sending address. Each transaction sent from an address increments the nonce by 1."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:350
+#: lib/block_scout_web/templates/transaction/overview.html.eex:362
msgid "Transaction type, introduced in EIP-2718."
msgstr ""
@@ -2797,7 +2830,7 @@ msgid "Transactions sent"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address/overview.html.eex:220
+#: lib/block_scout_web/templates/address/overview.html.eex:222
#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:50
msgid "Transfers"
msgstr ""
@@ -2826,12 +2859,12 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:5
-#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:20
+#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:22
msgid "Type"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:450
+#: lib/block_scout_web/templates/transaction/overview.html.eex:462
msgid "UTF-8"
msgstr ""
@@ -2851,12 +2884,12 @@ msgid "Uncles"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/views/transaction_view.ex:348
+#: lib/block_scout_web/views/transaction_view.ex:341
msgid "Unconfirmed"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/tokens/inventory/_token.html.eex:6
+#: lib/block_scout_web/templates/tokens/inventory/_token.html.eex:9
msgid "Unique Token"
msgstr ""
@@ -2888,12 +2921,12 @@ msgid "Use the search box to find a hosted network, or select from the list of a
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:380
+#: lib/block_scout_web/templates/transaction/overview.html.eex:392
msgid "User defined maximum fee (tip) per unit of gas paid to validator for transaction prioritization."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:390
+#: lib/block_scout_web/templates/transaction/overview.html.eex:402
msgid "User-defined tip sent to validator for transaction priority/inclusion."
msgstr ""
@@ -2955,32 +2988,32 @@ msgid "Validator pools can be banned for misbehavior (such as not revealing secr
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:311
+#: lib/block_scout_web/templates/transaction/overview.html.eex:323
msgid "Value"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:310
+#: lib/block_scout_web/templates/transaction/overview.html.eex:322
msgid "Value sent in CELO (and USD) if applicable."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:86
+#: lib/block_scout_web/templates/address_contract/index.html.eex:74
msgid "Verified at"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:24
-#: lib/block_scout_web/templates/address_contract/index.html.eex:170 lib/block_scout_web/templates/address_contract/index.html.eex:182
-#: lib/block_scout_web/templates/address_contract/index.html.eex:213 lib/block_scout_web/templates/address_contract/index.html.eex:225
+#: lib/block_scout_web/templates/address_contract/index.html.eex:158 lib/block_scout_web/templates/address_contract/index.html.eex:164
+#: lib/block_scout_web/templates/address_contract/index.html.eex:195 lib/block_scout_web/templates/address_contract/index.html.eex:201
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:13
msgid "Verify & Publish"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:301
-#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:52
-#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:89
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:297
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:48
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:85
msgid "Verify & publish"
msgstr ""
@@ -3057,7 +3090,7 @@ msgid "WEI"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_pending_contract_write.html.eex:12
+#: lib/block_scout_web/templates/smart_contract/_pending_contract_write.html.eex:9
msgid "Waiting for transaction's confirmation..."
msgstr ""
@@ -3202,7 +3235,7 @@ msgid "balance of the address"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:400
+#: lib/block_scout_web/templates/transaction/overview.html.eex:412
msgid "burned for this transaction. Equals Block Base Fee per Gas * Gas Used."
msgstr ""
@@ -3217,7 +3250,7 @@ msgid "candidate"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:216
+#: lib/block_scout_web/templates/transaction/overview.html.eex:228
msgid "created"
msgstr ""
@@ -3257,6 +3290,11 @@ msgstr ""
msgid "is not a valid transaction hash"
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/smart_contract/_function_response.html.eex:3
+msgid "method Response"
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/common_components/_pagination_container.html.eex:41
msgid "of"
diff --git a/apps/block_scout_web/test/block_scout_web/controllers/address_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/address_controller_test.exs
index b69e683fe132..d02c8a1e4e8a 100644
--- a/apps/block_scout_web/test/block_scout_web/controllers/address_controller_test.exs
+++ b/apps/block_scout_web/test/block_scout_web/controllers/address_controller_test.exs
@@ -85,7 +85,13 @@ defmodule BlockScoutWeb.AddressControllerTest do
assert conn.status == 200
{:ok, response} = Jason.decode(conn.resp_body)
- assert %{"transaction_count" => 0, "token_transfer_count" => 0, "validation_count" => 0, "gas_usage_count" => 0} ==
+ assert %{
+ "transaction_count" => 0,
+ "token_transfer_count" => 0,
+ "validation_count" => 0,
+ "gas_usage_count" => 0,
+ "crc_total_worth" => "0"
+ } ==
response
end
end
diff --git a/apps/block_scout_web/test/block_scout_web/views/tokens/holder_view_test.exs b/apps/block_scout_web/test/block_scout_web/views/tokens/holder_view_test.exs
index 20bd85e7efe2..6b5c6c853dd8 100644
--- a/apps/block_scout_web/test/block_scout_web/views/tokens/holder_view_test.exs
+++ b/apps/block_scout_web/test/block_scout_web/views/tokens/holder_view_test.exs
@@ -56,19 +56,19 @@ defmodule BlockScoutWeb.Tokens.HolderViewTest do
end
end
- describe "format_token_balance_value/1" do
+ describe "format_token_balance_value/3" do
test "formats according to token decimals when it's a ERC-20" do
token = build(:token, type: "ERC-20", decimals: Decimal.new(2))
token_balance = build(:token_balance, value: 2_000_000)
- assert HolderView.format_token_balance_value(token_balance.value, token) == "20,000"
+ assert HolderView.format_token_balance_value(token_balance.value, nil, token) == "20,000"
end
test "returns the value when it's ERC-721" do
token = build(:token, type: "ERC-721")
token_balance = build(:token_balance, value: 1)
- assert HolderView.format_token_balance_value(token_balance.value, token) == 1
+ assert HolderView.format_token_balance_value(token_balance.value, nil, token) == 1
end
end
end
diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex
index 418db5f679a3..d4e08e18038c 100644
--- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex
+++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex
@@ -167,8 +167,12 @@ defmodule EthereumJSONRPC do
]}
"""
@spec execute_contract_functions([Contract.call()], [map()], json_rpc_named_arguments) :: [Contract.call_result()]
- def execute_contract_functions(functions, abi, json_rpc_named_arguments) do
- Contract.execute_contract_functions(functions, abi, json_rpc_named_arguments)
+ def execute_contract_functions(functions, abi, json_rpc_named_arguments, leave_error_as_map \\ false) do
+ if Enum.count(functions) > 0 do
+ Contract.execute_contract_functions(functions, abi, json_rpc_named_arguments, leave_error_as_map)
+ else
+ []
+ end
end
@spec execute_contract_functions_by_name([Contract.call_by_name()], [map()], json_rpc_named_arguments) :: [
diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/contract.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/contract.ex
index e311846d3bc8..6804d5a69c73 100644
--- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/contract.ex
+++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/contract.ex
@@ -40,8 +40,10 @@ defmodule EthereumJSONRPC.Contract do
"""
@type call_result :: {:ok, term()} | {:error, String.t()}
- @spec execute_contract_functions([call()], [map()], EthereumJSONRPC.json_rpc_named_arguments()) :: [call_result()]
- def execute_contract_functions(requests, abi, json_rpc_named_arguments) do
+ @spec execute_contract_functions([call()], [map()], EthereumJSONRPC.json_rpc_named_arguments(), true | false) :: [
+ call_result()
+ ]
+ def execute_contract_functions(requests, abi, json_rpc_named_arguments, leave_error_as_map \\ false) do
parsed_abi =
abi
|> ABI.parse_specification()
@@ -82,7 +84,7 @@ defmodule EthereumJSONRPC.Contract do
response ->
selectors = define_selectors(parsed_abi, method_id)
- {^index, result} = Encoder.decode_result(response, selectors)
+ {^index, result} = Encoder.decode_result(response, selectors, leave_error_as_map)
result
end
end)
diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/encoder.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/encoder.ex
index 64d45c144437..0f6ab1e4db93 100644
--- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/encoder.ex
+++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/encoder.ex
@@ -45,17 +45,27 @@ defmodule EthereumJSONRPC.Encoder do
@doc """
Given a result from the blockchain, and the function selector, returns the result decoded.
"""
+ def decode_result(_, _, leave_error_as_map \\ false)
+
@spec decode_result(map(), %ABI.FunctionSelector{} | [%ABI.FunctionSelector{}]) ::
{String.t(), {:ok, any()} | {:error, String.t() | :invalid_data}}
- def decode_result(%{error: %{code: code, data: data, message: message}, id: id}, _selector) do
- {id, {:error, "(#{code}) #{message} (#{data})"}}
+ def decode_result(%{error: %{code: code, data: data, message: message}, id: id}, _selector, leave_error_as_map) do
+ if leave_error_as_map do
+ {id, {:error, %{code: code, message: message, data: data}}}
+ else
+ {id, {:error, "(#{code}) #{message} (#{data})"}}
+ end
end
- def decode_result(%{error: %{code: code, message: message}, id: id}, _selector) do
- {id, {:error, "(#{code}) #{message}"}}
+ def decode_result(%{error: %{code: code, message: message}, id: id}, _selector, leave_error_as_map) do
+ if leave_error_as_map do
+ {id, {:error, %{code: code, message: message}}}
+ else
+ {id, {:error, "(#{code}) #{message}"}}
+ end
end
- def decode_result(result, selectors) when is_list(selectors) do
+ def decode_result(result, selectors, _leave_error_as_map) when is_list(selectors) do
selectors
|> Enum.map(fn selector ->
try do
@@ -72,7 +82,7 @@ defmodule EthereumJSONRPC.Encoder do
end)
end
- def decode_result(%{id: id, result: result}, function_selector) do
+ def decode_result(%{id: id, result: result}, function_selector, _leave_error_as_map) do
types_list = List.wrap(function_selector.returns)
decoded_data =
diff --git a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/encoder_test.exs b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/encoder_test.exs
index 01fe9036defe..38f87e08af4c 100644
--- a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/encoder_test.exs
+++ b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/encoder_test.exs
@@ -78,6 +78,55 @@ defmodule EthereumJSONRPC.EncoderTest do
{"sum", {:error, "(-32602) Invalid params: Invalid hex: Invalid character 'x' at position 134."}}
end
+ test "correctly handles the blockchain error response with returning error as map without data" do
+ result = %{
+ error: %{
+ code: -32602,
+ message: "Invalid params: Invalid hex: Invalid character 'x' at position 134."
+ },
+ id: "sum",
+ jsonrpc: "2.0"
+ }
+
+ selector = %ABI.FunctionSelector{
+ function: "get",
+ returns: {:uint, 256},
+ types: [{:uint, 256}]
+ }
+
+ assert Encoder.decode_result(result, selector, true) ==
+ {"sum",
+ {:error,
+ %{code: -32602, message: "Invalid params: Invalid hex: Invalid character 'x' at position 134."}}}
+ end
+
+ test "correctly handles the blockchain error response with returning error as map with data" do
+ result = %{
+ error: %{
+ code: -32602,
+ message: "Invalid params: Invalid hex: Invalid character 'x' at position 134.",
+ data: "0x01"
+ },
+ id: "sum",
+ jsonrpc: "2.0"
+ }
+
+ selector = %ABI.FunctionSelector{
+ function: "get",
+ returns: {:uint, 256},
+ types: [{:uint, 256}]
+ }
+
+ assert Encoder.decode_result(result, selector, true) ==
+ {"sum",
+ {:error,
+ %{
+ code: -32602,
+ message: "Invalid params: Invalid hex: Invalid character 'x' at position 134.",
+ data: "0x01"
+ }}}
+ end
+
test "correctly decodes string types" do
result =
"0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000441494f4e00000000000000000000000000000000000000000000000000000000"
diff --git a/apps/explorer/config/config.exs b/apps/explorer/config/config.exs
index f8668de3a279..70482b6c69b7 100644
--- a/apps/explorer/config/config.exs
+++ b/apps/explorer/config/config.exs
@@ -273,19 +273,12 @@ config :explorer, Explorer.Chain.Cache.Accounts,
ttl_check_interval: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(1), else: false),
global_ttl: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(5))
-config :explorer, Explorer.Chain.Cache.PendingTransactions,
- enabled:
- if(System.get_env("ETHEREUM_JSONRPC_VARIANT") == "besu",
- do: false,
- else: true
- ),
- ttl_check_interval: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(1), else: false),
- global_ttl: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(5))
-
config :explorer, Explorer.Chain.Cache.Uncles,
ttl_check_interval: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(1), else: false),
global_ttl: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(5))
+config :explorer, Explorer.Chain.Cache.GasUsage, enabled: false
+
config :explorer, Explorer.ThirdPartyIntegrations.Sourcify,
server_url: System.get_env("SOURCIFY_SERVER_URL") || "https://sourcify.dev/server",
enabled: System.get_env("ENABLE_SOURCIFY_INTEGRATION") == "true",
diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex
index 9787ebaec445..4eb4e7fa7adc 100644
--- a/apps/explorer/lib/explorer/chain.ex
+++ b/apps/explorer/lib/explorer/chain.ex
@@ -569,7 +569,7 @@ defmodule Explorer.Chain do
from_block = from_block(options)
to_block = to_block(options)
- {block_number, _transaction_index, log_index} = paging_options.key || {BlockNumber.get_max(), 0, 0}
+ {block_number, transaction_index, log_index} = paging_options.key || {BlockNumber.get_max(), 0, 0}
base_query =
from(log in Log,
@@ -813,7 +813,7 @@ defmodule Explorer.Chain do
select:
sum(
fragment(
- "CASE
+ "CASE
WHEN ? = 0 THEN 0
WHEN ? < ? THEN ?
ELSE ? END",
@@ -2398,7 +2398,7 @@ defmodule Explorer.Chain do
base_query =
from(t in Token,
where: t.total_supply > ^0,
- order_by: [desc: t.holder_count, asc: t.name],
+ order_by: [desc_nulls_last: t.holder_count, asc: t.name],
preload: [:contract_address]
)
@@ -3514,6 +3514,31 @@ defmodule Explorer.Chain do
|> Repo.all(timeout: :infinity)
end
+ @doc """
+ Returns the list of empty blocks from the DB which have not marked with `t:Explorer.Chain.Block.is_empty/0`.
+ This query used for initializtion of Indexer.EmptyBlocksSanitizer
+ """
+ def unprocessed_empty_blocks_query_list(limit) do
+ query =
+ from(block in Block,
+ as: :block,
+ where: block.consensus == true,
+ where: is_nil(block.is_empty),
+ where:
+ not exists(
+ from(transaction in Transaction,
+ where: transaction.block_number == parent_as(:block).number
+ )
+ ),
+ select: {block.number, block.hash},
+ order_by: [desc: block.number],
+ limit: ^limit
+ )
+
+ query
+ |> Repo.all(timeout: :infinity)
+ end
+
@doc """
The `string` must start with `0x`, then is converted to an integer and then to `t:Explorer.Chain.Hash.Address.t/0`.
@@ -3856,7 +3881,7 @@ defmodule Explorer.Chain do
formatted_revert_reason
end
- defp format_revert_reason_message(revert_reason) do
+ def format_revert_reason_message(revert_reason) do
case revert_reason do
@revert_msg_prefix_1 <> rest ->
rest
@@ -4792,7 +4817,7 @@ defmodule Explorer.Chain do
nft_tokens =
from(
token in Token,
- where: token.type == ^"ERC-721",
+ where: token.type == ^"ERC-721" or token.type == ^"ERC-1155",
select: token.contract_address_hash
)
@@ -5036,6 +5061,8 @@ defmodule Explorer.Chain do
else
{:ok, false}
end
+ else
+ {:ok, false}
end
end
@@ -5946,6 +5973,13 @@ defmodule Explorer.Chain do
|> Repo.one() || Decimal.new(0)
end
+ # @spec fetch_last_token_balance_1155(Hash.Address.t(), Hash.Address.t()) :: Decimal.t()
+ def fetch_last_token_balance_1155(address_hash, token_contract_address_hash, token_id) do
+ address_hash
+ |> CurrentTokenBalance.last_token_balance_1155(token_contract_address_hash, token_id)
+ |> Repo.one() || Decimal.new(0)
+ end
+
@spec address_to_coin_balances(Hash.Address.t(), [paging_options]) :: []
def address_to_coin_balances(address_hash, options) do
paging_options = Keyword.get(options, :paging_options, @default_paging_options)
@@ -6066,9 +6100,36 @@ defmodule Explorer.Chain do
|> Repo.all()
end
+ def fetch_token_holders_from_token_hash_and_token_id(contract_address_hash, token_id, options \\ []) do
+ contract_address_hash
+ |> CurrentTokenBalance.token_holders_1155_by_token_id(token_id, options)
+ |> Repo.all()
+ end
+
+ def token_id_1155_is_unique?(_, nil), do: false
+
+ def token_id_1155_is_unique?(contract_address_hash, token_id) do
+ result = contract_address_hash |> CurrentTokenBalance.token_balances_by_id_limit_2(token_id) |> Repo.all()
+
+ if length(result) == 1 do
+ Decimal.cmp(Enum.at(result, 0), 1) == :eq
+ else
+ false
+ end
+ end
+
+ def get_token_ids_1155(contract_address_hash) do
+ contract_address_hash
+ |> CurrentTokenBalance.token_ids_query()
+ |> Repo.all()
+ end
+
@spec count_token_holders_from_token_hash(Hash.Address.t()) :: non_neg_integer()
def count_token_holders_from_token_hash(contract_address_hash) do
- query = from(ctb in CurrentTokenBalance.token_holders_query(contract_address_hash), select: fragment("COUNT(*)"))
+ query =
+ from(ctb in CurrentTokenBalance.token_holders_query_for_count(contract_address_hash),
+ select: fragment("COUNT(DISTINCT(address_hash))")
+ )
Repo.one!(query, timeout: :infinity)
end
@@ -6106,7 +6167,7 @@ defmodule Explorer.Chain do
end
@spec transaction_token_transfer_type(Transaction.t()) ::
- :erc20 | :erc721 | :token_transfer | nil
+ :erc20 | :erc721 | :erc1155 | :token_transfer | nil
def transaction_token_transfer_type(
%Transaction{
status: :ok,
@@ -6153,10 +6214,24 @@ defmodule Explorer.Chain do
find_erc721_token_transfer(transaction.token_transfers, {from_address, to_address})
+ # safeTransferFrom(address,address,uint256,uint256,bytes)
+ {"0xf242432a" <> params, ^zero_wei} ->
+ types = [:address, :address, {:uint, 256}, {:uint, 256}, :bytes]
+ [from_address, to_address, _id, _value, _data] = decode_params(params, types)
+
+ find_erc1155_token_transfer(transaction.token_transfers, {from_address, to_address})
+
+ # safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)
+ {"0x2eb2c2d6" <> params, ^zero_wei} ->
+ types = [:address, :address, [{:uint, 256}], [{:uint, 256}], :bytes]
+ [from_address, to_address, _ids, _values, _data] = decode_params(params, types)
+
+ find_erc1155_token_transfer(transaction.token_transfers, {from_address, to_address})
+
{"0xf907fc5b" <> _params, ^zero_wei} ->
:erc20
- # check for ERC 20 or for old ERC 721 token versions
+ # check for ERC-20 or for old ERC-721, ERC-1155 token versions
{unquote(TokenTransfer.transfer_function_signature()) <> params, ^zero_wei} ->
types = [:address, {:uint, 256}]
@@ -6164,7 +6239,7 @@ defmodule Explorer.Chain do
decimal_value = Decimal.new(value)
- find_erc721_or_erc20_token_transfer(transaction.token_transfers, {address, decimal_value})
+ find_erc721_or_erc20_or_erc1155_token_transfer(transaction.token_transfers, {address, decimal_value})
_ ->
nil
@@ -6180,7 +6255,16 @@ defmodule Explorer.Chain do
if token_transfer, do: :erc721
end
- defp find_erc721_or_erc20_token_transfer(token_transfers, {address, decimal_value}) do
+ defp find_erc1155_token_transfer(token_transfers, {from_address, to_address}) do
+ token_transfer =
+ Enum.find(token_transfers, fn token_transfer ->
+ token_transfer.from_address_hash.bytes == from_address && token_transfer.to_address_hash.bytes == to_address
+ end)
+
+ if token_transfer, do: :erc1155
+ end
+
+ defp find_erc721_or_erc20_or_erc1155_token_transfer(token_transfers, {address, decimal_value}) do
token_transfer =
Enum.find(token_transfers, fn token_transfer ->
token_transfer.to_address_hash.bytes == address && token_transfer.amount == decimal_value
@@ -6190,6 +6274,7 @@ defmodule Explorer.Chain do
case token_transfer.token do
%Token{type: "ERC-20"} -> :erc20
%Token{type: "ERC-721"} -> :erc721
+ %Token{type: "ERC-1155"} -> :erc1155
_ -> nil
end
else
@@ -6489,7 +6574,9 @@ defmodule Explorer.Chain do
)
end
- def get_total_staked_and_ordered(address_hash) do
+ def get_total_staked_and_ordered(""), do: nil
+
+ def get_total_staked_and_ordered(address_hash) when is_binary(address_hash) do
StakingPoolsDelegator
|> where([delegator], delegator.address_hash == ^address_hash and not delegator.is_deleted)
|> select([delegator], %{
@@ -6865,6 +6952,8 @@ defmodule Explorer.Chain do
end
end
+ def get_total_staked_and_ordered(_), do: nil
+
defp with_decompiled_code_flag(query, _hash, false), do: query
defp with_decompiled_code_flag(query, hash, true) do
@@ -6888,6 +6977,17 @@ defmodule Explorer.Chain do
|> TypeDecoder.decode_raw(types)
end
+ def get_token_type(hash) do
+ query =
+ from(
+ token in Token,
+ where: token.contract_address_hash == ^hash,
+ select: token.type
+ )
+
+ Repo.one(query)
+ end
+
@doc """
Checks if an `t:Explorer.Chain.Address.t/0` with the given `hash` exists.
@@ -7391,12 +7491,59 @@ defmodule Explorer.Chain do
abi_decode_address_output(if status == :ok, do: implementation_address, else: "0x")
end
+ # changes requested by https://github.com/blockscout/blockscout/issues/4770
+ # for support BeaconProxy pattern
+ defp fetch_beacon_proxy_implementation(proxy_address_hash, json_rpc_named_arguments) do
+ # https://eips.ethereum.org/EIPS/eip-1967
+ storage_slot_beacon_contract_address = "0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50"
+
+ implementation_method_abi = [
+ %{
+ "type" => "function",
+ "stateMutability" => "view",
+ "outputs" => [%{"type" => "address", "name" => "", "internalType" => "address"}],
+ "name" => "implementation",
+ "inputs" => []
+ }
+ ]
+
+ case Contract.eth_get_storage_at_request(
+ proxy_address_hash,
+ storage_slot_beacon_contract_address,
+ nil,
+ json_rpc_named_arguments
+ ) do
+ {:ok, empty_address}
+ when empty_address in ["0x", "0x0000000000000000000000000000000000000000000000000000000000000000"] ->
+ {:ok, "0x"}
+
+ {:ok, beacon_contract_address} ->
+ case beacon_contract_address
+ |> abi_decode_address_output()
+ |> get_implementation_address_hash_basic(implementation_method_abi) do
+ <> ->
+ {:ok, implementation_address}
+
+ _ ->
+ {:ok, beacon_contract_address}
+ end
+
+ {:error, _} ->
+ {:ok, "0x"}
+ end
+ end
+
defp get_implementation_address_hash_basic(proxy_address_hash, abi) do
# 5c60da1b = keccak256(implementation())
implementation_address =
- case Reader.query_contract(proxy_address_hash, abi, %{
- "5c60da1b" => []
- }) do
+ case Reader.query_contract(
+ proxy_address_hash,
+ abi,
+ %{
+ "5c60da1b" => []
+ },
+ false
+ ) do
%{"5c60da1b" => {:ok, [result]}} -> result
_ -> nil
end
@@ -7441,11 +7588,11 @@ defmodule Explorer.Chain do
end)
end
- defp abi_decode_address_output(address) when is_nil(address), do: nil
+ defp abi_decode_address_output(nil), do: nil
defp abi_decode_address_output("0x"), do: @burn_address_hash_str
- defp abi_decode_address_output(address) do
+ defp abi_decode_address_output(address) when is_binary(address) do
if String.length(address) > 42 do
"0x" <> String.slice(address, -40, 40)
else
@@ -7453,6 +7600,8 @@ defmodule Explorer.Chain do
end
end
+ defp abi_decode_address_output(_), do: nil
+
defp address_to_hex(address) do
if address do
if String.starts_with?(address, "0x") do
@@ -7610,7 +7759,7 @@ defmodule Explorer.Chain do
{_, block_index} =
sorted_traces
- |> Enum.find(fn {trace, _} ->
+ |> Enum.find({nil, -1}, fn {trace, _} ->
trace.transaction_index == transaction_index &&
trace.transaction_hash == transaction_hash
end)
diff --git a/apps/explorer/lib/explorer/chain/address/current_token_balance.ex b/apps/explorer/lib/explorer/chain/address/current_token_balance.ex
index db279755858c..58d0b7a19312 100644
--- a/apps/explorer/lib/explorer/chain/address/current_token_balance.ex
+++ b/apps/explorer/lib/explorer/chain/address/current_token_balance.ex
@@ -23,6 +23,8 @@ defmodule Explorer.Chain.Address.CurrentTokenBalance do
* `token_contract_address_hash` - The contract address hash foreign key.
* `block_number` - The block's number that the transfer took place.
* `value` - The value that's represents the balance.
+ * `token_id` - The token_id of the transferred token (applicable for ERC-1155 and ERC-721 tokens)
+ * `token_type` - The type of the token
"""
@type t :: %__MODULE__{
address: %Ecto.Association.NotLoaded{} | Address.t(),
@@ -30,15 +32,21 @@ defmodule Explorer.Chain.Address.CurrentTokenBalance do
token: %Ecto.Association.NotLoaded{} | Token.t(),
token_contract_address_hash: Hash.Address,
block_number: Block.block_number(),
+ max_block_number: Block.block_number(),
inserted_at: DateTime.t(),
updated_at: DateTime.t(),
- value: Decimal.t() | nil
+ value: Decimal.t() | nil,
+ token_id: non_neg_integer() | nil,
+ token_type: String.t()
}
schema "address_current_token_balances" do
field(:value, :decimal)
field(:block_number, :integer)
+ field(:max_block_number, :integer, virtual: true)
field(:value_fetched_at, :utc_datetime_usec)
+ field(:token_id, :decimal)
+ field(:token_type, :string)
# A transient field for deriving token holder count deltas during address_current_token_balances upserts
field(:old_value, :decimal)
@@ -56,8 +64,8 @@ defmodule Explorer.Chain.Address.CurrentTokenBalance do
timestamps()
end
- @optional_fields ~w(value value_fetched_at)a
- @required_fields ~w(address_hash block_number token_contract_address_hash)a
+ @optional_fields ~w(value value_fetched_at token_id)a
+ @required_fields ~w(address_hash block_number token_contract_address_hash token_type)a
@allowed_fields @optional_fields ++ @required_fields
@doc false
@@ -96,6 +104,57 @@ defmodule Explorer.Chain.Address.CurrentTokenBalance do
|> offset(^offset)
end
+ @doc """
+ Builds an `Ecto.Query` to fetch the token holders from the given token contract address hash and token_id.
+
+ The Token Holders are the addresses that own a positive amount of the Token. So this query is
+ considering the following conditions:
+
+ * The token balance from the last block.
+ * Balances greater than 0.
+ * Excluding the burn address (0x0000000000000000000000000000000000000000).
+
+ """
+ def token_holders_1155_by_token_id(token_contract_address_hash, token_id, options \\ []) do
+ paging_options = Keyword.get(options, :paging_options, @default_paging_options)
+ offset = (max(paging_options.page_number, 1) - 1) * paging_options.page_size
+
+ token_contract_address_hash
+ |> token_holders_by_token_id_query(token_id)
+ |> preload(:address)
+ |> order_by([tb], desc: :value, desc: :address_hash)
+ |> Chain.page_token_balances(paging_options)
+ |> limit(^paging_options.page_size)
+ |> offset(^offset)
+ end
+
+ @doc """
+ Builds an `Ecto.Query` to fetch all available token_ids
+ """
+ def token_ids_query(token_contract_address_hash) do
+ from(
+ ctb in __MODULE__,
+ where: ctb.token_contract_address_hash == ^token_contract_address_hash,
+ where: ctb.address_hash != ^@burn_address_hash,
+ where: ctb.value > 0,
+ select: ctb.token_id,
+ distinct: ctb.token_id
+ )
+ end
+
+ @doc """
+ Builds an `Ecto.Query` to fetch all token holders, to count it
+ Used in `Explorer.Chain.count_token_holders_from_token_hash/1`
+ """
+ def token_holders_query_for_count(token_contract_address_hash) do
+ from(
+ ctb in __MODULE__,
+ where: ctb.token_contract_address_hash == ^token_contract_address_hash,
+ where: ctb.address_hash != ^@burn_address_hash,
+ where: ctb.value > 0
+ )
+ end
+
@doc """
Builds an `t:Ecto.Query.t/0` to fetch the current token balances of the given address.
"""
@@ -137,6 +196,48 @@ defmodule Explorer.Chain.Address.CurrentTokenBalance do
)
end
+ @doc """
+ Builds an `t:Ecto.Query.t/0` to fetch the current balance of the given address for the given token and token_id
+ """
+ def last_token_balance_1155(address_hash, token_contract_address_hash, token_id) do
+ from(
+ ctb in __MODULE__,
+ where: ctb.token_contract_address_hash == ^token_contract_address_hash,
+ where: ctb.address_hash == ^address_hash,
+ where: ctb.token_id == ^token_id,
+ select: ctb.value
+ )
+ end
+
+ @doc """
+ Builds an `t:Ecto.Query.t/0` to check if the token_id corresponds to the unique token or not.
+ Used in `Explorer.Chain.token_id_1155_is_unique?/2`
+ """
+ def token_balances_by_id_limit_2(token_contract_address_hash, token_id) do
+ from(
+ ctb in __MODULE__,
+ where: ctb.token_contract_address_hash == ^token_contract_address_hash,
+ where: ctb.token_id == ^token_id,
+ where: ctb.address_hash != ^@burn_address_hash,
+ where: ctb.value > 0,
+ select: ctb.value,
+ limit: 2
+ )
+ end
+
+ @doc """
+ Builds an `t:Ecto.Query.t/0` to fetch holders of the particular token_id in ERC-1155
+ """
+ def token_holders_by_token_id_query(token_contract_address_hash, token_id) do
+ from(
+ ctb in __MODULE__,
+ where: ctb.token_contract_address_hash == ^token_contract_address_hash,
+ where: ctb.address_hash != ^@burn_address_hash,
+ where: ctb.value > 0,
+ where: ctb.token_id == ^token_id
+ )
+ end
+
@doc """
Builds an `t:Ecto.Query.t/0` to fetch addresses that hold the token.
diff --git a/apps/explorer/lib/explorer/chain/address/token.ex b/apps/explorer/lib/explorer/chain/address/token.ex
index 1dd167b779d7..b2bfabeaf2b8 100644
--- a/apps/explorer/lib/explorer/chain/address/token.ex
+++ b/apps/explorer/lib/explorer/chain/address/token.ex
@@ -34,17 +34,16 @@ defmodule Explorer.Chain.Address.Token do
address_hash
|> join_with_last_balance()
- |> order_filter_and_group()
+ |> filter_and_group()
+ |> order()
|> page_tokens(paging_options)
|> limit(^paging_options.page_size)
end
- defp order_filter_and_group(query) do
+ defp filter_and_group(query) do
from(
[token, balance] in query,
- order_by: fragment("? DESC, LOWER(?) ASC NULLS LAST", token.type, token.name),
where: balance.value > 0,
- group_by: [token.name, token.symbol, balance.value, token.type, token.contract_address_hash],
select: %Address.Token{
contract_address_hash: token.contract_address_hash,
inserted_at: max(token.inserted_at),
@@ -53,22 +52,40 @@ defmodule Explorer.Chain.Address.Token do
balance: balance.value,
decimals: max(token.decimals),
type: token.type
- }
+ },
+ group_by: [token.name, token.symbol, balance.value, token.type, token.contract_address_hash, balance.block_number]
+ )
+ end
+
+ defp order(query) do
+ from(
+ token in subquery(query),
+ order_by: fragment("? DESC, ? ASC NULLS LAST", token.type, token.name)
)
end
defp join_with_last_balance(address_hash) do
last_balance_query =
from(
- tb in CurrentTokenBalance,
- where: tb.address_hash == ^address_hash,
- select: %{value: tb.value, token_contract_address_hash: tb.token_contract_address_hash}
+ ctb in CurrentTokenBalance,
+ where: ctb.address_hash == ^address_hash,
+ select: %{
+ value: ctb.value,
+ token_contract_address_hash: ctb.token_contract_address_hash,
+ block_number: ctb.block_number,
+ max_block_number: over(max(ctb.block_number), :w)
+ },
+ windows: [
+ w: [partition_by: [ctb.token_contract_address_hash, ctb.address_hash]]
+ ]
)
from(
t in Chain.Token,
join: tb in subquery(last_balance_query),
- on: tb.token_contract_address_hash == t.contract_address_hash
+ on: tb.token_contract_address_hash == t.contract_address_hash,
+ where: tb.block_number == tb.max_block_number,
+ distinct: t.contract_address_hash
)
end
diff --git a/apps/explorer/lib/explorer/chain/address/token_balance.ex b/apps/explorer/lib/explorer/chain/address/token_balance.ex
index d3941fd9e287..cc0d579da1bd 100644
--- a/apps/explorer/lib/explorer/chain/address/token_balance.ex
+++ b/apps/explorer/lib/explorer/chain/address/token_balance.ex
@@ -23,6 +23,8 @@ defmodule Explorer.Chain.Address.TokenBalance do
* `token_contract_address_hash` - The contract address hash foreign key.
* `block_number` - The block's number that the transfer took place.
* `value` - The value that's represents the balance.
+ * `token_id` - The token_id of the transferred token (applicable for ERC-1155 and ERC-721 tokens)
+ * `token_type` - The type of the token
"""
@type t :: %__MODULE__{
address: %Ecto.Association.NotLoaded{} | Address.t(),
@@ -32,13 +34,17 @@ defmodule Explorer.Chain.Address.TokenBalance do
block_number: Block.block_number(),
inserted_at: DateTime.t(),
updated_at: DateTime.t(),
- value: Decimal.t() | nil
+ value: Decimal.t() | nil,
+ token_id: non_neg_integer() | nil,
+ token_type: String.t()
}
schema "address_token_balances" do
field(:value, :decimal)
field(:block_number, :integer)
field(:value_fetched_at, :utc_datetime_usec)
+ field(:token_id, :decimal)
+ field(:token_type, :string)
belongs_to(:address, Address, foreign_key: :address_hash, references: :hash, type: Hash.Address)
@@ -53,8 +59,8 @@ defmodule Explorer.Chain.Address.TokenBalance do
timestamps()
end
- @optional_fields ~w(value value_fetched_at)a
- @required_fields ~w(address_hash block_number token_contract_address_hash)a
+ @optional_fields ~w(value value_fetched_at token_id)a
+ @required_fields ~w(address_hash block_number token_contract_address_hash token_type)a
@allowed_fields @optional_fields ++ @required_fields
@doc false
@@ -82,8 +88,9 @@ defmodule Explorer.Chain.Address.TokenBalance do
tb in TokenBalance,
join: t in Token,
on: tb.token_contract_address_hash == t.contract_address_hash,
- where: is_nil(tb.value_fetched_at) or is_nil(tb.value),
- where: (tb.address_hash != ^@burn_address_hash and t.type != "ERC-721") or t.type == "ERC-20"
+ where:
+ ((tb.address_hash != ^@burn_address_hash and t.type != "ERC-721") or t.type == "ERC-20" or t.type == "ERC-1155") and
+ (is_nil(tb.value_fetched_at) or is_nil(tb.value))
)
end
end
diff --git a/apps/explorer/lib/explorer/chain/block.ex b/apps/explorer/lib/explorer/chain/block.ex
index 44ad0d5a5a35..c1c0733ae0e9 100644
--- a/apps/explorer/lib/explorer/chain/block.ex
+++ b/apps/explorer/lib/explorer/chain/block.ex
@@ -81,7 +81,8 @@ defmodule Explorer.Chain.Block do
round: non_neg_integer(),
extra_data: Data.t(),
online: %Ecto.Association.NotLoaded{} | boolean(),
- base_fee_per_gas: Wei.t()
+ base_fee_per_gas: Wei.t(),
+ is_empty: boolean()
}
@primary_key {:hash, Hash.Full, autogenerate: false}
@@ -100,6 +101,7 @@ defmodule Explorer.Chain.Block do
field(:round, :integer)
field(:extra_data, Data)
field(:base_fee_per_gas, Wei)
+ field(:is_empty, :boolean)
timestamps()
diff --git a/apps/explorer/lib/explorer/chain/block/reward.ex b/apps/explorer/lib/explorer/chain/block/reward.ex
index 1b62de29e8d1..f3c9bfa05eb6 100644
--- a/apps/explorer/lib/explorer/chain/block/reward.ex
+++ b/apps/explorer/lib/explorer/chain/block/reward.ex
@@ -200,7 +200,7 @@ defmodule Explorer.Chain.Block.Reward do
|> Enum.map(fn {key, _value} -> key end)
|> List.first()
- case Reader.query_contract(address, abi, params) do
+ case Reader.query_contract(address, abi, params, false) do
%{^method_id => {:ok, [result]}} -> result
_ -> @empty_address
end
diff --git a/apps/explorer/lib/explorer/chain/cache/gas_usage.ex b/apps/explorer/lib/explorer/chain/cache/gas_usage.ex
index 132f414683b8..ca9c29b455cc 100644
--- a/apps/explorer/lib/explorer/chain/cache/gas_usage.ex
+++ b/apps/explorer/lib/explorer/chain/cache/gas_usage.ex
@@ -6,6 +6,8 @@ defmodule Explorer.Chain.Cache.GasUsage do
require Logger
@default_cache_period :timer.hours(2)
+ config = Application.get_env(:explorer, __MODULE__)
+ @enabled Keyword.get(config, :enabled)
use Explorer.Chain.MapCache,
name: :gas_usage,
@@ -26,25 +28,29 @@ defmodule Explorer.Chain.Cache.GasUsage do
end
defp handle_fallback(:async_task) do
- # If this gets called it means an async task was requested, but none exists
- # so a new one needs to be launched
- {:ok, task} =
- Task.start(fn ->
- try do
- result = Chain.fetch_sum_gas_used()
+ if @enabled do
+ # If this gets called it means an async task was requested, but none exists
+ # so a new one needs to be launched
+ {:ok, task} =
+ Task.start(fn ->
+ try do
+ result = Chain.fetch_sum_gas_used()
- set_sum(result)
- rescue
- e ->
- Logger.debug([
- "Coudn't update gas used sum test #{inspect(e)}"
- ])
- end
+ set_sum(result)
+ rescue
+ e ->
+ Logger.debug([
+ "Coudn't update gas used sum test #{inspect(e)}"
+ ])
+ end
- set_async_task(nil)
- end)
+ set_async_task(nil)
+ end)
- {:update, task}
+ {:update, task}
+ else
+ {:update, nil}
+ end
end
# By setting this as a `callback` an async task will be started each time the
diff --git a/apps/explorer/lib/explorer/chain/import/runner/address/coin_balances_daily.ex b/apps/explorer/lib/explorer/chain/import/runner/address/coin_balances_daily.ex
index d8f279d9ad46..7d1a091984b0 100644
--- a/apps/explorer/lib/explorer/chain/import/runner/address/coin_balances_daily.ex
+++ b/apps/explorer/lib/explorer/chain/import/runner/address/coin_balances_daily.ex
@@ -83,7 +83,7 @@ defmodule Explorer.Chain.Import.Runner.Address.CoinBalancesDaily do
end)
if target_item do
- if change.value > target_item.value do
+ if Map.has_key?(change, :value) && Map.has_key?(target_item, :value) && change.value > target_item.value do
acc_updated = List.delete(acc, target_item)
[change | acc_updated]
else
diff --git a/apps/explorer/lib/explorer/chain/import/runner/address/current_token_balances.ex b/apps/explorer/lib/explorer/chain/import/runner/address/current_token_balances.ex
index ce3a22a36504..cb691d60327f 100644
--- a/apps/explorer/lib/explorer/chain/import/runner/address/current_token_balances.ex
+++ b/apps/explorer/lib/explorer/chain/import/runner/address/current_token_balances.ex
@@ -109,8 +109,16 @@ defmodule Explorer.Chain.Import.Runner.Address.CurrentTokenBalances do
# Enforce ShareLocks tables order (see docs: sharelocks.md)
multi
|> Multi.run(:acquire_contract_address_tokens, fn repo, _ ->
- contract_address_hashes = changes_list |> Enum.map(& &1.token_contract_address_hash) |> Enum.uniq()
- Tokens.acquire_contract_address_tokens(repo, contract_address_hashes)
+ token_contract_address_hashes_and_ids =
+ changes_list
+ |> Enum.map(fn change ->
+ token_id = get_tokend_id(change)
+
+ {change.token_contract_address_hash, token_id}
+ end)
+ |> Enum.uniq()
+
+ Tokens.acquire_contract_address_tokens(repo, token_contract_address_hashes_and_ids)
end)
|> Multi.run(:address_current_token_balances, fn repo, _ ->
insert(repo, changes_list, insert_options)
@@ -131,6 +139,10 @@ defmodule Explorer.Chain.Import.Runner.Address.CurrentTokenBalances do
end)
end
+ defp get_tokend_id(change) do
+ if Map.has_key?(change, :token_id), do: change.token_id, else: nil
+ end
+
@impl Import.Runner
def timeout, do: @timeout
@@ -198,21 +210,107 @@ defmodule Explorer.Chain.Import.Runner.Address.CurrentTokenBalances do
| {:error, [Changeset.t()]}
defp insert(repo, changes_list, %{timeout: timeout, timestamps: timestamps} = options)
when is_atom(repo) and is_list(changes_list) do
+ inserted_changes_list =
+ insert_changes_list_with_and_without_token_id(changes_list, repo, timestamps, timeout, options)
+
+ {:ok, inserted_changes_list}
+ end
+
+ def insert_changes_list_with_and_without_token_id(changes_list, repo, timestamps, timeout, options) do
on_conflict = Map.get_lazy(options, :on_conflict, &default_on_conflict/0)
# Enforce CurrentTokenBalance ShareLocks order (see docs: sharelocks.md)
- ordered_changes_list = Enum.sort_by(changes_list, &{&1.token_contract_address_hash, &1.address_hash})
-
- Import.insert_changes_list(
- repo,
- ordered_changes_list,
- conflict_target: ~w(address_hash token_contract_address_hash)a,
- on_conflict: on_conflict,
- for: CurrentTokenBalance,
- returning: true,
- timeout: timeout,
- timestamps: timestamps
- )
+ %{
+ changes_list_no_token_id: changes_list_no_token_id,
+ changes_list_with_token_id: changes_list_with_token_id
+ } =
+ changes_list
+ |> Enum.reduce(%{changes_list_no_token_id: [], changes_list_with_token_id: []}, fn change, acc ->
+ updated_change =
+ if Map.has_key?(change, :token_id) and Map.get(change, :token_type) == "ERC-1155" do
+ change
+ else
+ Map.put(change, :token_id, nil)
+ end
+
+ if updated_change.token_id do
+ changes_list_with_token_id = [updated_change | acc.changes_list_with_token_id]
+
+ %{
+ changes_list_no_token_id: acc.changes_list_no_token_id,
+ changes_list_with_token_id: changes_list_with_token_id
+ }
+ else
+ changes_list_no_token_id = [updated_change | acc.changes_list_no_token_id]
+
+ %{
+ changes_list_no_token_id: changes_list_no_token_id,
+ changes_list_with_token_id: acc.changes_list_with_token_id
+ }
+ end
+ end)
+
+ ordered_changes_list_no_token_id =
+ changes_list_no_token_id
+ |> Enum.group_by(fn %{
+ address_hash: address_hash,
+ token_contract_address_hash: token_contract_address_hash
+ } ->
+ {address_hash, token_contract_address_hash}
+ end)
+ |> Enum.map(fn {_, grouped_address_token_balances} ->
+ Enum.max_by(grouped_address_token_balances, fn %{block_number: block_number} -> block_number end)
+ end)
+ |> Enum.sort_by(&{&1.token_contract_address_hash, &1.address_hash})
+
+ ordered_changes_list_with_token_id =
+ changes_list_with_token_id
+ |> Enum.group_by(fn %{
+ address_hash: address_hash,
+ token_contract_address_hash: token_contract_address_hash,
+ token_id: token_id
+ } ->
+ {address_hash, token_contract_address_hash, token_id}
+ end)
+ |> Enum.map(fn {_, grouped_address_token_balances} ->
+ Enum.max_by(grouped_address_token_balances, fn %{block_number: block_number} -> block_number end)
+ end)
+ |> Enum.sort_by(&{&1.token_contract_address_hash, &1.token_id, &1.address_hash})
+
+ {:ok, inserted_changes_list_no_token_id} =
+ if Enum.count(ordered_changes_list_no_token_id) > 0 do
+ Import.insert_changes_list(
+ repo,
+ ordered_changes_list_no_token_id,
+ conflict_target: {:unsafe_fragment, ~s<(address_hash, token_contract_address_hash) WHERE token_id IS NULL>},
+ on_conflict: on_conflict,
+ for: CurrentTokenBalance,
+ returning: true,
+ timeout: timeout,
+ timestamps: timestamps
+ )
+ else
+ {:ok, []}
+ end
+
+ {:ok, inserted_changes_list_with_token_id} =
+ if Enum.count(ordered_changes_list_with_token_id) > 0 do
+ Import.insert_changes_list(
+ repo,
+ ordered_changes_list_with_token_id,
+ conflict_target:
+ {:unsafe_fragment, ~s<(address_hash, token_contract_address_hash, token_id) WHERE token_id IS NOT NULL>},
+ on_conflict: on_conflict,
+ for: CurrentTokenBalance,
+ returning: true,
+ timeout: timeout,
+ timestamps: timestamps
+ )
+ else
+ {:ok, []}
+ end
+
+ inserted_changes_list_no_token_id ++ inserted_changes_list_with_token_id
end
defp default_on_conflict do
@@ -224,6 +322,7 @@ defmodule Explorer.Chain.Import.Runner.Address.CurrentTokenBalances do
value: fragment("EXCLUDED.value"),
value_fetched_at: fragment("EXCLUDED.value_fetched_at"),
old_value: current_token_balance.value,
+ token_type: fragment("EXCLUDED.token_type"),
inserted_at: fragment("LEAST(EXCLUDED.inserted_at, ?)", current_token_balance.inserted_at),
updated_at: fragment("GREATEST(EXCLUDED.updated_at, ?)", current_token_balance.updated_at)
]
diff --git a/apps/explorer/lib/explorer/chain/import/runner/address/token_balances.ex b/apps/explorer/lib/explorer/chain/import/runner/address/token_balances.ex
index 8189a5845ca3..d80f561bbe7a 100644
--- a/apps/explorer/lib/explorer/chain/import/runner/address/token_balances.ex
+++ b/apps/explorer/lib/explorer/chain/import/runner/address/token_balances.ex
@@ -60,20 +60,113 @@ defmodule Explorer.Chain.Import.Runner.Address.TokenBalances do
on_conflict = Map.get_lazy(options, :on_conflict, &default_on_conflict/0)
# Enforce TokenBalance ShareLocks order (see docs: sharelocks.md)
- ordered_changes_list =
- Enum.sort_by(changes_list, &{&1.token_contract_address_hash, &1.address_hash, &1.block_number})
-
- {:ok, _} =
- Import.insert_changes_list(
- repo,
- ordered_changes_list,
- conflict_target: ~w(address_hash token_contract_address_hash block_number)a,
- on_conflict: on_conflict,
- for: TokenBalance,
- returning: true,
- timeout: timeout,
- timestamps: timestamps
- )
+ %{
+ changes_list_no_token_id: changes_list_no_token_id,
+ changes_list_with_token_id: changes_list_with_token_id
+ } =
+ changes_list
+ |> Enum.reduce(%{changes_list_no_token_id: [], changes_list_with_token_id: []}, fn change, acc ->
+ updated_change =
+ if Map.has_key?(change, :token_id) and Map.get(change, :token_type) == "ERC-1155" do
+ change
+ else
+ Map.put(change, :token_id, nil)
+ end
+
+ if updated_change.token_id do
+ changes_list_with_token_id = [updated_change | acc.changes_list_with_token_id]
+
+ %{
+ changes_list_no_token_id: acc.changes_list_no_token_id,
+ changes_list_with_token_id: changes_list_with_token_id
+ }
+ else
+ changes_list_no_token_id = [updated_change | acc.changes_list_no_token_id]
+
+ %{
+ changes_list_no_token_id: changes_list_no_token_id,
+ changes_list_with_token_id: acc.changes_list_with_token_id
+ }
+ end
+ end)
+
+ ordered_changes_list_no_token_id =
+ changes_list_no_token_id
+ |> Enum.group_by(fn %{
+ address_hash: address_hash,
+ token_contract_address_hash: token_contract_address_hash,
+ block_number: block_number
+ } ->
+ {token_contract_address_hash, address_hash, block_number}
+ end)
+ |> Enum.map(fn {_, grouped_address_token_balances} ->
+ dedup = Enum.dedup(grouped_address_token_balances)
+
+ if Enum.count(dedup) > 1 do
+ Enum.max_by(dedup, fn %{value_fetched_at: value_fetched_at} -> value_fetched_at end)
+ else
+ Enum.at(dedup, 0)
+ end
+ end)
+ |> Enum.sort_by(&{&1.token_contract_address_hash, &1.address_hash, &1.block_number})
+
+ ordered_changes_list_with_token_id =
+ changes_list_with_token_id
+ |> Enum.group_by(fn %{
+ address_hash: address_hash,
+ token_contract_address_hash: token_contract_address_hash,
+ token_id: token_id,
+ block_number: block_number
+ } ->
+ {token_contract_address_hash, token_id, address_hash, block_number}
+ end)
+ |> Enum.map(fn {_, grouped_address_token_balances} ->
+ if Enum.count(grouped_address_token_balances) > 1 do
+ Enum.max_by(grouped_address_token_balances, fn %{value_fetched_at: value_fetched_at} -> value_fetched_at end)
+ else
+ Enum.at(grouped_address_token_balances, 0)
+ end
+ end)
+ |> Enum.sort_by(&{&1.token_contract_address_hash, &1.token_id, &1.address_hash, &1.block_number})
+
+ {:ok, inserted_changes_list_no_token_id} =
+ if Enum.count(ordered_changes_list_no_token_id) > 0 do
+ Import.insert_changes_list(
+ repo,
+ ordered_changes_list_no_token_id,
+ conflict_target:
+ {:unsafe_fragment, ~s<(address_hash, token_contract_address_hash, block_number) WHERE token_id IS NULL>},
+ on_conflict: on_conflict,
+ for: TokenBalance,
+ returning: true,
+ timeout: timeout,
+ timestamps: timestamps
+ )
+ else
+ {:ok, []}
+ end
+
+ {:ok, inserted_changes_list_with_token_id} =
+ if Enum.count(ordered_changes_list_with_token_id) > 0 do
+ Import.insert_changes_list(
+ repo,
+ ordered_changes_list_with_token_id,
+ conflict_target:
+ {:unsafe_fragment,
+ ~s<(address_hash, token_contract_address_hash, token_id, block_number) WHERE token_id IS NOT NULL>},
+ on_conflict: on_conflict,
+ for: TokenBalance,
+ returning: true,
+ timeout: timeout,
+ timestamps: timestamps
+ )
+ else
+ {:ok, []}
+ end
+
+ inserted_changes_list = inserted_changes_list_no_token_id ++ inserted_changes_list_with_token_id
+
+ {:ok, inserted_changes_list}
end
defp default_on_conflict do
@@ -83,6 +176,7 @@ defmodule Explorer.Chain.Import.Runner.Address.TokenBalances do
set: [
value: fragment("EXCLUDED.value"),
value_fetched_at: fragment("EXCLUDED.value_fetched_at"),
+ token_type: fragment("EXCLUDED.token_type"),
inserted_at: fragment("LEAST(EXCLUDED.inserted_at, ?)", token_balance.inserted_at),
updated_at: fragment("GREATEST(EXCLUDED.updated_at, ?)", token_balance.updated_at)
]
diff --git a/apps/explorer/lib/explorer/chain/import/runner/addresses.ex b/apps/explorer/lib/explorer/chain/import/runner/addresses.ex
index 5eb7194e4493..71771239daa5 100644
--- a/apps/explorer/lib/explorer/chain/import/runner/addresses.ex
+++ b/apps/explorer/lib/explorer/chain/import/runner/addresses.ex
@@ -123,7 +123,8 @@ defmodule Explorer.Chain.Import.Runner.Addresses do
"GREATEST(EXCLUDED.fetched_coin_balance_block_number, ?)",
address.fetched_coin_balance_block_number
),
- nonce: fragment("GREATEST(EXCLUDED.nonce, ?)", address.nonce)
+ nonce: fragment("GREATEST(EXCLUDED.nonce, ?)", address.nonce),
+ updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", address.updated_at)
]
],
# where any of `set`s would make a change
diff --git a/apps/explorer/lib/explorer/chain/import/runner/blocks.ex b/apps/explorer/lib/explorer/chain/import/runner/blocks.ex
index 4b9fdb60a798..0a4b1392e0d5 100644
--- a/apps/explorer/lib/explorer/chain/import/runner/blocks.ex
+++ b/apps/explorer/lib/explorer/chain/import/runner/blocks.ex
@@ -6,12 +6,15 @@ defmodule Explorer.Chain.Import.Runner.Blocks do
require Ecto.Query
require Logger
- import Ecto.Query, only: [from: 2]
+ import Ecto.Query, only: [from: 2, subquery: 1]
alias Ecto.{Changeset, Multi, Repo}
alias Explorer.Chain.{Block, CeloPendingEpochOperation, Import, PendingBlockOperation, Transaction}
alias Explorer.Chain.Block.Reward
alias Explorer.Chain.Import.Runner
+ alias Explorer.Chain.Import.Runner.Address.CurrentTokenBalances
+ alias Explorer.Chain.Import.Runner.Tokens
+ alias Explorer.Repo, as: ExplorerRepo
@behaviour Runner
@@ -124,18 +127,18 @@ defmodule Explorer.Chain.Import.Runner.Blocks do
@impl Runner
def timeout, do: @timeout
- # defp acquire_contract_address_tokens(repo, consensus_block_numbers) do
- # query =
- # from(ctb in Address.CurrentTokenBalance,
- # where: ctb.block_number in ^consensus_block_numbers,
- # select: ctb.token_contract_address_hash,
- # distinct: ctb.token_contract_address_hash
- # )
- #
- # contract_address_hashes = repo.all(query)
- #
- # Tokens.acquire_contract_address_tokens(repo, contract_address_hashes)
- # end
+ # defp acquire_contract_address_tokens(repo, consensus_block_numbers) do
+ # query =
+ # from(ctb in Address.CurrentTokenBalance,
+ # where: ctb.block_number in ^consensus_block_numbers,
+ # select: {ctb.token_contract_address_hash, ctb.token_id},
+ # distinct: [ctb.token_contract_address_hash, ctb.token_id]
+ # )
+
+ # contract_address_hashes_and_token_ids = repo.all(query)
+
+ # Tokens.acquire_contract_address_tokens(repo, contract_address_hashes_and_token_ids)
+ # end
defp fork_transactions(%{
repo: repo,
@@ -322,6 +325,15 @@ defmodule Explorer.Chain.Import.Runner.Blocks do
{:error, %{exception: postgrex_error, consensus_block_numbers: consensus_block_numbers}}
end
+ # def invalidate_consensus_blocks(block_numbers) do
+ # opts = %{
+ # timeout: 60_000,
+ # timestamps: %{updated_at: DateTime.utc_now()}
+ # }
+
+ # lose_consensus(ExplorerRepo, [], block_numbers, [], opts)
+ # end
+
defp new_pending_operations(repo, nonconsensus_hashes, hashes, %{timeout: timeout, timestamps: timestamps}) do
if Application.get_env(:explorer, :json_rpc_named_arguments)[:variant] == EthereumJSONRPC.RSK do
{:ok, []}
@@ -400,129 +412,90 @@ defmodule Explorer.Chain.Import.Runner.Blocks do
# tb.token_contract_address_hash and
# ordered_address_token_balance.block_number == tb.block_number
# )
- #
- # try do
- # {_count, deleted_address_token_balances} = repo.delete_all(query, timeout: timeout)
- #
- # {:ok, deleted_address_token_balances}
- # rescue
- # postgrex_error in Postgrex.Error ->
- # {:error, %{exception: postgrex_error, block_numbers: consensus_block_numbers}}
- # end
- # end
-
- # defp delete_address_current_token_balances(_, [], _), do: {:ok, []}
- #
- # defp delete_address_current_token_balances(repo, consensus_block_numbers, %{timeout: timeout}) do
- # ordered_query =
- # from(ctb in Address.CurrentTokenBalance,
- # where: ctb.block_number in ^consensus_block_numbers,
- # select: map(ctb, [:address_hash, :token_contract_address_hash]),
- # # Enforce CurrentTokenBalance ShareLocks order (see docs: sharelocks.md)
- # order_by: [
- # ctb.token_contract_address_hash,
- # ctb.address_hash
- # ],
- # lock: "FOR UPDATE"
- # )
- #
- # query =
- # from(ctb in Address.CurrentTokenBalance,
- # select:
- # map(ctb, [
- # :address_hash,
- # :token_contract_address_hash,
- # # Used to determine if `address_hash` was a holder of `token_contract_address_hash` before
- #
- # # `address_current_token_balance` is deleted in `update_tokens_holder_count`.
- # :value
- # ]),
- # inner_join: ordered_address_current_token_balance in subquery(ordered_query),
- # on:
- # ordered_address_current_token_balance.address_hash == ctb.address_hash and
- # ordered_address_current_token_balance.token_contract_address_hash ==
- # ctb.token_contract_address_hash
- # )
- #
- # try do
- # {_count, deleted_address_current_token_balances} = repo.delete_all(query, timeout: timeout)
- #
- # {:ok, deleted_address_current_token_balances}
- # rescue
- # postgrex_error in Postgrex.Error ->
- # {:error, %{exception: postgrex_error, block_numbers: consensus_block_numbers}}
- # end
- # end
-
- # defp derive_address_current_token_balances(_, [], _), do: {:ok, []}
- #
- # defp derive_address_current_token_balances(repo, deleted_address_current_token_balances, %{timeout: timeout})
- # when is_list(deleted_address_current_token_balances) do
- # initial_query =
- # from(tb in Address.TokenBalance,
- # select: %{
- # address_hash: tb.address_hash,
- # token_contract_address_hash: tb.token_contract_address_hash,
- # block_number: max(tb.block_number)
- # },
- # group_by: [tb.address_hash, tb.token_contract_address_hash]
- # )
- #
- # final_query =
- # Enum.reduce(deleted_address_current_token_balances, initial_query, fn %{
- # address_hash: address_hash,
- # token_contract_address_hash:
- # token_contract_address_hash
- # },
- # acc_query ->
- # from(tb in acc_query,
- # or_where:
- # tb.address_hash == ^address_hash and
- # tb.token_contract_address_hash == ^token_contract_address_hash
- # )
- # end)
- #
- # new_current_token_balance_query =
- # from(new_current_token_balance in subquery(final_query),
- # inner_join: tb in Address.TokenBalance,
- # on:
- # tb.address_hash == new_current_token_balance.address_hash and
- # tb.token_contract_address_hash == new_current_token_balance.token_contract_address_hash and
- # tb.block_number == new_current_token_balance.block_number,
- # select: %{
- # address_hash: new_current_token_balance.address_hash,
- # token_contract_address_hash: new_current_token_balance.token_contract_address_hash,
- # block_number: new_current_token_balance.block_number,
- # value: tb.value,
- # inserted_at: over(min(tb.inserted_at), :w),
- # updated_at: over(max(tb.updated_at), :w)
- # },
- # windows: [
- # w: [partition_by: [tb.address_hash, tb.token_contract_address_hash]]
- # ]
- # )
- #
- # ordered_current_token_balance =
- # new_current_token_balance_query
- # |> repo.all()
- # # Enforce CurrentTokenBalance ShareLocks order (see docs: sharelocks.md)
- # |> Enum.sort_by(&{&1.token_contract_address_hash, &1.address_hash})
- #
- # {_total, result} =
- # repo.insert_all(
- # Address.CurrentTokenBalance,
- # ordered_current_token_balance,
- # # No `ON CONFLICT` because `delete_address_current_token_balances`
- # # should have removed any conflicts.
- # returning: [:address_hash, :token_contract_address_hash, :block_number, :value],
- # timeout: timeout
- # )
- #
- # derived_address_current_token_balances =
- # Enum.map(result, &Map.take(&1, [:address_hash, :token_contract_address_hash, :block_number, :value]))
- #
- # {:ok, derived_address_current_token_balances}
- # end
+ # when is_list(deleted_address_current_token_balances) do
+ # final_query = derive_address_current_token_balances_grouped_query(deleted_address_current_token_balances)
+
+ # new_current_token_balance_query =
+ # from(new_current_token_balance in subquery(final_query),
+ # inner_join: tb in Address.TokenBalance,
+ # on:
+ # tb.address_hash == new_current_token_balance.address_hash and
+ # tb.token_contract_address_hash == new_current_token_balance.token_contract_address_hash and
+ # ((is_nil(tb.token_id) and is_nil(new_current_token_balance.token_id)) or
+ # (tb.token_id == new_current_token_balance.token_id and
+ # not is_nil(tb.token_id) and not is_nil(new_current_token_balance.token_id))) and
+ # tb.block_number == new_current_token_balance.block_number,
+ # select: %{
+ # address_hash: new_current_token_balance.address_hash,
+ # token_contract_address_hash: new_current_token_balance.token_contract_address_hash,
+ # token_id: new_current_token_balance.token_id,
+ # block_number: new_current_token_balance.block_number,
+ # value: tb.value,
+ # inserted_at: over(min(tb.inserted_at), :w),
+ # updated_at: over(max(tb.updated_at), :w)
+ # },
+ # windows: [
+ # w: [partition_by: [tb.address_hash, tb.token_contract_address_hash, tb.token_id]]
+ # ]
+ # )
+
+ # current_token_balance =
+ # new_current_token_balance_query
+ # |> repo.all()
+
+ # timestamps = Import.timestamps()
+
+ # result =
+ # CurrentTokenBalances.insert_changes_list_with_and_without_token_id(
+ # current_token_balance,
+ # repo,
+ # timestamps,
+ # timeout,
+ # options
+ # )
+
+ # derived_address_current_token_balances =
+ # Enum.map(result, &Map.take(&1, [:address_hash, :token_contract_address_hash, :token_id, :block_number, :value]))
+
+ # {:ok, derived_address_current_token_balances}
+ # end
+
+ # defp derive_address_current_token_balances_grouped_query(deleted_address_current_token_balances) do
+ # initial_query =
+ # from(tb in Address.TokenBalance,
+ # select: %{
+ # address_hash: tb.address_hash,
+ # token_contract_address_hash: tb.token_contract_address_hash,
+ # token_id: tb.token_id,
+ # block_number: max(tb.block_number)
+ # },
+ # group_by: [tb.address_hash, tb.token_contract_address_hash, tb.token_id]
+ # )
+
+ # Enum.reduce(deleted_address_current_token_balances, initial_query, fn %{
+ # address_hash: address_hash,
+ # token_contract_address_hash:
+ # token_contract_address_hash,
+ # token_id: token_id
+ # },
+ # acc_query ->
+ # if token_id do
+ # from(tb in acc_query,
+ # or_where:
+ # tb.address_hash == ^address_hash and
+ # tb.token_contract_address_hash == ^token_contract_address_hash and
+ # tb.token_id == ^token_id
+ # )
+ # else
+ # from(tb in acc_query,
+ # or_where:
+ # tb.address_hash == ^address_hash and
+ # tb.token_contract_address_hash == ^token_contract_address_hash and
+ # is_nil(tb.token_id)
+ # )
+ # end
+ # end)
+ # end
# `block_rewards` are linked to `blocks.hash`, but fetched by `blocks.number`, so when a block with the same number is
# inserted, the old block rewards need to be deleted, so that the old and new rewards aren't combined.
diff --git a/apps/explorer/lib/explorer/chain/import/runner/internal_transactions.ex b/apps/explorer/lib/explorer/chain/import/runner/internal_transactions.ex
index dfb2f1ffe016..5b90336ecfa1 100644
--- a/apps/explorer/lib/explorer/chain/import/runner/internal_transactions.ex
+++ b/apps/explorer/lib/explorer/chain/import/runner/internal_transactions.ex
@@ -229,7 +229,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
block_numbers =
changes_list
|> Enum.map(& &1.block_number)
- |> Enum.dedup()
+ |> Enum.uniq()
query =
from(
@@ -270,7 +270,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
where: t.block_hash in ^pending_block_hashes,
select: map(t, [:hash, :block_hash, :block_number, :cumulative_gas_used]),
# Enforce Transaction ShareLocks order (see docs: sharelocks.md)
- order_by: t.hash,
+ order_by: [asc: t.hash],
lock: "FOR UPDATE"
)
diff --git a/apps/explorer/lib/explorer/chain/import/runner/tokens.ex b/apps/explorer/lib/explorer/chain/import/runner/tokens.ex
index 5d1dcc4c8dcc..193072979963 100644
--- a/apps/explorer/lib/explorer/chain/import/runner/tokens.ex
+++ b/apps/explorer/lib/explorer/chain/import/runner/tokens.ex
@@ -21,17 +21,70 @@ defmodule Explorer.Chain.Import.Runner.Tokens do
@type holder_count :: non_neg_integer()
@type token_holder_count :: %{contract_address_hash: Hash.Address.t(), count: holder_count()}
- def acquire_contract_address_tokens(repo, contract_address_hashes) do
- token_query =
- from(
- token in Token,
- where: token.contract_address_hash in ^contract_address_hashes,
- # Enforce Token ShareLocks order (see docs: sharelocks.md)
- order_by: token.contract_address_hash,
- lock: "FOR UPDATE"
+ def acquire_contract_address_tokens(repo, contract_address_hashes_and_token_ids) do
+ initial_query_no_token_id =
+ from(token in Token,
+ select: token
)
- tokens = repo.all(token_query)
+ initial_query_with_token_id =
+ from(token in Token,
+ left_join: instance in Token.Instance,
+ on: token.contract_address_hash == instance.token_contract_address_hash,
+ select: token
+ )
+
+ {query_no_token_id, query_with_token_id} =
+ contract_address_hashes_and_token_ids
+ |> Enum.reduce({initial_query_no_token_id, initial_query_with_token_id}, fn {contract_address_hash, token_id},
+ {query_no_token_id,
+ query_with_token_id} ->
+ if is_nil(token_id) do
+ {from(
+ token in query_no_token_id,
+ or_where: token.contract_address_hash == ^contract_address_hash
+ ), query_with_token_id}
+ else
+ {query_no_token_id,
+ from(
+ [token, instance] in query_with_token_id,
+ or_where: token.contract_address_hash == ^contract_address_hash and instance.token_id == ^token_id
+ )}
+ end
+ end)
+
+ final_query_no_token_id =
+ if query_no_token_id == initial_query_no_token_id do
+ nil
+ else
+ from(
+ token in query_no_token_id,
+ # Enforce Token ShareLocks order (see docs: sharelocks.md)
+ order_by: [
+ token.contract_address_hash
+ ],
+ lock: "FOR UPDATE"
+ )
+ end
+
+ final_query_with_token_id =
+ if query_with_token_id == initial_query_with_token_id do
+ nil
+ else
+ from(
+ [token, instance] in query_with_token_id,
+ # Enforce Token ShareLocks order (see docs: sharelocks.md)
+ order_by: [
+ token.contract_address_hash,
+ instance.token_id
+ ],
+ lock: "FOR UPDATE"
+ )
+ end
+
+ tokens_no_token_id = (final_query_no_token_id && repo.all(final_query_no_token_id)) || []
+ tokens_with_token_id = (final_query_with_token_id && repo.all(final_query_with_token_id)) || []
+ tokens = tokens_no_token_id ++ tokens_with_token_id
{:ok, tokens}
end
diff --git a/apps/explorer/lib/explorer/chain/import/runner/transactions.ex b/apps/explorer/lib/explorer/chain/import/runner/transactions.ex
index 7cc1306fb8f5..d88291b4b3f6 100644
--- a/apps/explorer/lib/explorer/chain/import/runner/transactions.ex
+++ b/apps/explorer/lib/explorer/chain/import/runner/transactions.ex
@@ -123,6 +123,11 @@ defmodule Explorer.Chain.Import.Runner.Transactions do
to_address_hash: fragment("EXCLUDED.to_address_hash"),
v: fragment("EXCLUDED.v"),
value: fragment("EXCLUDED.value"),
+ earliest_processing_start: fragment("EXCLUDED.earliest_processing_start"),
+ revert_reason: fragment("EXCLUDED.revert_reason"),
+ max_priority_fee_per_gas: fragment("EXCLUDED.max_priority_fee_per_gas"),
+ max_fee_per_gas: fragment("EXCLUDED.max_fee_per_gas"),
+ type: fragment("EXCLUDED.type"),
# Don't update `hash` as it is part of the primary key and used for the conflict target
inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", transaction.inserted_at),
updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", transaction.updated_at)
@@ -130,13 +135,12 @@ defmodule Explorer.Chain.Import.Runner.Transactions do
],
where:
fragment(
- "(EXCLUDED.block_hash, EXCLUDED.block_number, EXCLUDED.created_contract_address_hash, EXCLUDED.created_contract_code_indexed_at, EXCLUDED.cumulative_gas_used, EXCLUDED.cumulative_gas_used, EXCLUDED.from_address_hash, EXCLUDED.gas, EXCLUDED.gas_price, EXCLUDED.gas_currency_hash, EXCLUDED.gas_fee_recipient_hash, EXCLUDED.gas_used, EXCLUDED.gateway_fee, EXCLUDED.index, EXCLUDED.input, EXCLUDED.nonce, EXCLUDED.r, EXCLUDED.s, EXCLUDED.status, EXCLUDED.to_address_hash, EXCLUDED.v, EXCLUDED.value) IS DISTINCT FROM (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+ "(EXCLUDED.block_hash, EXCLUDED.block_number, EXCLUDED.created_contract_address_hash, EXCLUDED.created_contract_code_indexed_at, EXCLUDED.cumulative_gas_used, EXCLUDED.from_address_hash, EXCLUDED.gas, EXCLUDED.gas_price, EXCLUDED.gas_currency_hash, EXCLUDED.gas_fee_recipient_hash, EXCLUDED.gas_used, EXCLUDED.gateway_fee, EXCLUDED.index, EXCLUDED.input, EXCLUDED.nonce, EXCLUDED.r, EXCLUDED.s, EXCLUDED.status, EXCLUDED.to_address_hash, EXCLUDED.v, EXCLUDED.value, EXCLUDED.earliest_processing_start, EXCLUDED.revert_reason, EXCLUDED.max_priority_fee_per_gas, EXCLUDED.max_fee_per_gas, EXCLUDED.type) IS DISTINCT FROM (?, ?, ?, ? ,?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
transaction.block_hash,
transaction.block_number,
transaction.created_contract_address_hash,
transaction.created_contract_code_indexed_at,
transaction.cumulative_gas_used,
- transaction.cumulative_gas_used,
transaction.from_address_hash,
transaction.gas,
transaction.gas_price,
@@ -152,7 +156,12 @@ defmodule Explorer.Chain.Import.Runner.Transactions do
transaction.status,
transaction.to_address_hash,
transaction.v,
- transaction.value
+ transaction.value,
+ transaction.earliest_processing_start,
+ transaction.revert_reason,
+ transaction.max_priority_fee_per_gas,
+ transaction.max_fee_per_gas,
+ transaction.type
)
)
end
diff --git a/apps/explorer/lib/explorer/chain/supply/token_bridge.ex b/apps/explorer/lib/explorer/chain/supply/token_bridge.ex
index 5e7caaf9896f..c83d757657ac 100644
--- a/apps/explorer/lib/explorer/chain/supply/token_bridge.ex
+++ b/apps/explorer/lib/explorer/chain/supply/token_bridge.ex
@@ -146,7 +146,7 @@ defmodule Explorer.Chain.Supply.TokenBridge do
|> Map.get("type", "")
value =
- case Reader.query_contract(address, abi, params) do
+ case Reader.query_contract(address, abi, params, false) do
%{^method_id => {:ok, [result]}} ->
result
diff --git a/apps/explorer/lib/explorer/chain/token.ex b/apps/explorer/lib/explorer/chain/token.ex
index 990ac1fa3479..686ee7c70f33 100644
--- a/apps/explorer/lib/explorer/chain/token.ex
+++ b/apps/explorer/lib/explorer/chain/token.ex
@@ -8,6 +8,7 @@ defmodule Explorer.Chain.Token do
* ERC-20
* ERC-721
+ * ERC-1155
## Token Specifications
diff --git a/apps/explorer/lib/explorer/chain/token_transfer.ex b/apps/explorer/lib/explorer/chain/token_transfer.ex
index 61c788bf4016..fcb27251e079 100644
--- a/apps/explorer/lib/explorer/chain/token_transfer.ex
+++ b/apps/explorer/lib/explorer/chain/token_transfer.ex
@@ -47,9 +47,12 @@ defmodule Explorer.Chain.TokenTransfer do
* `:transaction` - The `t:Explorer.Chain.Transaction.t/0` ledger
* `:transaction_hash` - Transaction foreign key
* `:log_index` - Index of the corresponding `t:Explorer.Chain.Log.t/0` in the transaction.
+ * `:amounts` - Tokens transferred amounts in case of batched transfer in ERC-1155
+ * `:token_ids` - IDs of the tokens (applicable to ERC-1155 tokens)
+ * `:comment` - Comment
"""
@type t :: %TokenTransfer{
- amount: Decimal.t(),
+ amount: Decimal.t() | nil,
block_number: non_neg_integer() | nil,
block_hash: Hash.Full.t(),
from_address: %Ecto.Association.NotLoaded{} | Address.t(),
@@ -61,14 +64,18 @@ defmodule Explorer.Chain.TokenTransfer do
token_id: non_neg_integer() | nil,
transaction: %Ecto.Association.NotLoaded{} | Transaction.t(),
transaction_hash: Hash.Full.t(),
- comment: String.t(),
- log_index: non_neg_integer()
+ log_index: non_neg_integer(),
+ amounts: [Decimal.t()] | nil,
+ token_ids: [non_neg_integer()] | nil,
+ comment: String.t()
}
@typep paging_options :: {:paging_options, PagingOptions.t()}
@constant "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
@comment_event "0xe5d4e30fb8364e57bc4d662a07d0cf36f4c34552004c4c3624620a2c1d1c03dc"
+ @erc1155_single_transfer_signature "0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62"
+ @erc1155_batch_transfer_signature "0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb"
@transfer_function_signature "0xa9059cbb"
@@ -79,6 +86,8 @@ defmodule Explorer.Chain.TokenTransfer do
field(:log_index, :integer, primary_key: true)
field(:token_id, :decimal)
field(:comment, :string)
+ field(:amounts, {:array, :decimal})
+ field(:token_ids, {:array, :decimal})
belongs_to(:from_address, Address, foreign_key: :from_address_hash, references: :hash, type: Hash.Address)
belongs_to(:to_address, Address, foreign_key: :to_address_hash, references: :hash, type: Hash.Address)
@@ -117,8 +126,8 @@ defmodule Explorer.Chain.TokenTransfer do
timestamps()
end
- @required_attrs ~w(block_number log_index from_address_hash to_address_hash token_contract_address_hash block_hash)a
- @optional_attrs ~w(amount token_id transaction_hash comment )a
+ @required_attrs ~w(block_number log_index from_address_hash to_address_hash token_contract_address_hash transaction_hash block_hash)a
+ @optional_attrs ~w(amount token_id amounts token_ids comment)a
@doc false
def changeset(%TokenTransfer{} = struct, params \\ %{}) do
@@ -140,6 +149,9 @@ defmodule Explorer.Chain.TokenTransfer do
def constant, do: @constant
def comment_event, do: @comment_event
+ def erc1155_single_transfer_signature, do: @erc1155_single_transfer_signature
+
+ def erc1155_batch_transfer_signature, do: @erc1155_batch_transfer_signature
@doc """
ERC 20's transfer(address,uint256) function signature
@@ -301,6 +313,7 @@ defmodule Explorer.Chain.TokenTransfer do
left_join: instance in Instance,
on: tt.token_contract_address_hash == instance.token_contract_address_hash and tt.token_id == instance.token_id,
where: tt.token_contract_address_hash == ^contract_address_hash,
+ where: tt.to_address_hash != ^"0x0000000000000000000000000000000000000000",
order_by: [desc: tt.block_number],
distinct: [desc: tt.token_id],
preload: [:to_address],
diff --git a/apps/explorer/lib/explorer/chain/transaction.ex b/apps/explorer/lib/explorer/chain/transaction.ex
index 57a6863c4ac9..5a1adae5df1e 100644
--- a/apps/explorer/lib/explorer/chain/transaction.ex
+++ b/apps/explorer/lib/explorer/chain/transaction.ex
@@ -471,6 +471,27 @@ defmodule Explorer.Chain.Transaction do
preload(query, [tt], token_transfers: ^token_transfers_query)
end
+ def decoded_revert_reason(transaction, revert_reason) do
+ case revert_reason do
+ "0x" <> hex_part ->
+ proccess_hex_revert_reason(hex_part, transaction)
+
+ hex_part ->
+ proccess_hex_revert_reason(hex_part, transaction)
+ end
+ end
+
+ defp proccess_hex_revert_reason(hex_revert_reason, %__MODULE__{to_address: smart_contract, hash: hash}) do
+ case Integer.parse(hex_revert_reason, 16) do
+ {number, ""} ->
+ binary_revert_reason = :binary.encode_unsigned(number)
+ decoded_input_data(%Transaction{to_address: smart_contract, hash: hash, input: %{bytes: binary_revert_reason}})
+
+ _ ->
+ hex_revert_reason
+ end
+ end
+
# Because there is no contract association, we know the contract was not verified
def decoded_input_data(%__MODULE__{to_address: nil}), do: {:error, :no_to_address}
def decoded_input_data(%__MODULE__{input: %{bytes: bytes}}) when bytes in [nil, <<>>], do: {:error, :no_input_data}
diff --git a/apps/explorer/lib/explorer/chain/transaction/history/historian.ex b/apps/explorer/lib/explorer/chain/transaction/history/historian.ex
index 664a5e7b294d..e6728cb70a1b 100644
--- a/apps/explorer/lib/explorer/chain/transaction/history/historian.ex
+++ b/apps/explorer/lib/explorer/chain/transaction/history/historian.ex
@@ -2,13 +2,14 @@ defmodule Explorer.Chain.Transaction.History.Historian do
@moduledoc """
Implements behaviour Historian which will compile TransactionStats from Block/Transaction data and then save the TransactionStats into the database for later retrevial.
"""
+ require Logger
use Explorer.History.Historian
+ alias Explorer.{Chain, Repo}
alias Explorer.Chain.{Block, Transaction}
alias Explorer.Chain.Events.Publisher
alias Explorer.Chain.Transaction.History.TransactionStats
alias Explorer.History.Process, as: HistoryProcess
- alias Explorer.Repo
import Ecto.Query, only: [from: 2, subquery: 1]
@@ -16,7 +17,12 @@ defmodule Explorer.Chain.Transaction.History.Historian do
@impl Historian
def compile_records(num_days, records \\ []) do
- if num_days == 0 do
+ Logger.info("tx/per day chart: collect records for txs per day stats")
+
+ if num_days == 1 do
+ Logger.info("tx/per day chart: records collected #{inspect(records)}")
+
+ records = [%{date: date_today(), number_of_transactions: 0, gas_used: 0, total_fee: 0} | records]
# base case
{:ok, records}
else
@@ -25,60 +31,105 @@ defmodule Explorer.Chain.Transaction.History.Historian do
earliest = datetime(day_to_fetch, ~T[00:00:00])
latest = datetime(day_to_fetch, ~T[23:59:59])
- min_max_block_query =
- from(block in Block,
- where: block.timestamp >= ^earliest and block.timestamp <= ^latest,
- select: {min(block.number), max(block.number)}
- )
-
- {min_block, max_block} = Repo.one(min_max_block_query, timeout: :infinity)
-
- if min_block && max_block do
- all_transactions_query =
- from(
- transaction in Transaction,
- where: transaction.block_number >= ^min_block and transaction.block_number <= ^max_block
- )
-
- query =
- from(transaction in subquery(all_transactions_query),
- join: block in Block,
- on: transaction.block_hash == block.hash,
- where: block.consensus == true,
- select: transaction
- )
-
- num_transactions = Repo.aggregate(query, :count, :hash, timeout: :infinity)
- gas_used = Repo.aggregate(query, :sum, :gas_used, timeout: :infinity)
-
- total_fee_query =
- from(transaction in subquery(all_transactions_query),
- join: block in Block,
- on: transaction.block_hash == block.hash,
- where: block.consensus == true,
- select: fragment("SUM(? * ?)", transaction.gas_price, transaction.gas_used)
- )
-
- total_fee = Repo.one(total_fee_query, timeout: :infinity)
+ Logger.info("tx/per day chart: earliest date #{DateTime.to_string(earliest)}")
+
+ Logger.info("tx/per day chart: latest date #{DateTime.to_string(latest)}")
+
+ with {:ok, min_block} <- Chain.timestamp_to_block_number(earliest, :after),
+ {:ok, max_block} <- Chain.timestamp_to_block_number(latest, :after) do
+ record =
+ min_block
+ |> compile_records_in_range(max_block)
+ |> Map.put(:date, day_to_fetch)
records = [
- %{date: day_to_fetch, number_of_transactions: num_transactions, gas_used: gas_used, total_fee: total_fee}
+ record
| records
]
compile_records(num_days - 1, records)
else
- records = [%{date: day_to_fetch, number_of_transactions: 0, gas_used: 0, total_fee: 0} | records]
- compile_records(num_days - 1, records)
+ _ ->
+ min_max_block_query =
+ from(block in Block,
+ where: block.timestamp >= ^earliest and block.timestamp <= ^latest,
+ select: {min(block.number), max(block.number)}
+ )
+
+ {min_block, max_block} = Repo.one(min_max_block_query, timeout: :infinity)
+
+ if min_block && max_block do
+ record =
+ min_block
+ |> compile_records_in_range(max_block)
+ |> Map.put(:date, day_to_fetch)
+
+ records = [
+ record
+ | records
+ ]
+
+ compile_records(num_days - 1, records)
+ else
+ records = [%{date: day_to_fetch, number_of_transactions: 0, gas_used: 0, total_fee: 0} | records]
+ compile_records(num_days - 1, records)
+ end
end
end
end
+ defp compile_records_in_range(min_block, max_block) do
+ Logger.info("tx/per day chart: min/max block numbers [#{min_block}, #{max_block}]")
+
+ all_transactions_query =
+ from(
+ transaction in Transaction,
+ where: transaction.block_number >= ^min_block and transaction.block_number <= ^max_block
+ )
+
+ all_blocks_query =
+ from(
+ block in Block,
+ where: block.consensus == true,
+ where: block.number >= ^min_block and block.number <= ^max_block,
+ select: block.number
+ )
+
+ query =
+ from(transaction in subquery(all_transactions_query),
+ join: block in subquery(all_blocks_query),
+ on: transaction.block_number == block.number,
+ select: transaction
+ )
+
+ num_transactions = Repo.aggregate(query, :count, :hash, timeout: :infinity)
+ Logger.info("tx/per day chart: num of transactions #{num_transactions}")
+ gas_used = Repo.aggregate(query, :sum, :gas_used, timeout: :infinity)
+ Logger.info("tx/per day chart: total gas used #{gas_used}")
+
+ total_fee_query =
+ from(transaction in subquery(all_transactions_query),
+ join: block in Block,
+ on: transaction.block_hash == block.hash,
+ where: block.consensus == true,
+ select: fragment("SUM(? * ?)", transaction.gas_price, transaction.gas_used)
+ )
+
+ total_fee = Repo.one(total_fee_query, timeout: :infinity)
+ Logger.info("tx/per day chart: total fee #{total_fee}")
+
+ %{number_of_transactions: num_transactions, gas_used: gas_used, total_fee: total_fee}
+ end
+
@impl Historian
def save_records(records) do
+ Logger.info("tx/per day chart: save records")
+
{num_inserted, _} =
Repo.insert_all(TransactionStats, records, on_conflict: {:replace_all_except, [:id]}, conflict_target: [:date])
+ Logger.info("tx/per day chart: number of inserted #{num_inserted}")
+
Publisher.broadcast(:transaction_stats)
num_inserted
end
diff --git a/apps/explorer/lib/explorer/chain_spec/poa/importer.ex b/apps/explorer/lib/explorer/chain_spec/poa/importer.ex
index 47c02e0c7550..7f69e59be156 100644
--- a/apps/explorer/lib/explorer/chain_spec/poa/importer.ex
+++ b/apps/explorer/lib/explorer/chain_spec/poa/importer.ex
@@ -98,10 +98,10 @@ defmodule Explorer.ChainSpec.POA.Importer do
|> Enum.map(fn {key, _value} -> key end)
|> List.first()
- Reader.query_contract(address, abi, params)
+ Reader.query_contract(address, abi, params, false)
value =
- case Reader.query_contract(address, abi, params) do
+ case Reader.query_contract(address, abi, params, false) do
%{^method_id => {:ok, [result]}} -> result
_ -> 0
end
diff --git a/apps/explorer/lib/explorer/counters/address_gas_usage_counter.ex b/apps/explorer/lib/explorer/counters/address_gas_usage_counter.ex
index d4e40620cad9..fbffbe1ccff4 100644
--- a/apps/explorer/lib/explorer/counters/address_gas_usage_counter.ex
+++ b/apps/explorer/lib/explorer/counters/address_gas_usage_counter.ex
@@ -48,9 +48,7 @@ defmodule Explorer.Counters.AddressTransactionsGasUsageCounter do
def fetch(address) do
if cache_expired?(address) do
- Task.start_link(fn ->
- update_cache(address)
- end)
+ update_cache(address)
end
address_hash_string = get_address_hash_string(address)
diff --git a/apps/explorer/lib/explorer/counters/address_token_transfers_counter.ex b/apps/explorer/lib/explorer/counters/address_token_transfers_counter.ex
index 696852037083..c5488afd9971 100644
--- a/apps/explorer/lib/explorer/counters/address_token_transfers_counter.ex
+++ b/apps/explorer/lib/explorer/counters/address_token_transfers_counter.ex
@@ -48,9 +48,7 @@ defmodule Explorer.Counters.AddressTokenTransfersCounter do
def fetch(address) do
if cache_expired?(address) do
- Task.start_link(fn ->
- update_cache(address)
- end)
+ update_cache(address)
end
address_hash_string = get_address_hash_string(address)
diff --git a/apps/explorer/lib/explorer/counters/address_transactions_counter.ex b/apps/explorer/lib/explorer/counters/address_transactions_counter.ex
index 2116ec3b4df2..ac73176e450a 100644
--- a/apps/explorer/lib/explorer/counters/address_transactions_counter.ex
+++ b/apps/explorer/lib/explorer/counters/address_transactions_counter.ex
@@ -48,9 +48,7 @@ defmodule Explorer.Counters.AddressTransactionsCounter do
def fetch(address) do
if cache_expired?(address) do
- Task.start_link(fn ->
- update_cache(address)
- end)
+ update_cache(address)
end
address_hash_string = get_address_hash_string(address)
diff --git a/apps/explorer/lib/explorer/history/process.ex b/apps/explorer/lib/explorer/history/process.ex
index 473ed68e64c8..af39ab39e612 100644
--- a/apps/explorer/lib/explorer/history/process.ex
+++ b/apps/explorer/lib/explorer/history/process.ex
@@ -60,7 +60,7 @@ defmodule Explorer.History.Process do
defp schedule_next_compilation do
delay = config_or_default(:history_fetch_interval, :timer.minutes(60))
- Process.send_after(self(), {:compile_historical_records, 1}, delay)
+ Process.send_after(self(), {:compile_historical_records, 2}, delay)
end
@spec failed_compilation(non_neg_integer(), module(), non_neg_integer()) :: any()
diff --git a/apps/explorer/lib/explorer/smart_contract/reader.ex b/apps/explorer/lib/explorer/smart_contract/reader.ex
index fae6c533fd6b..648ba651f50f 100644
--- a/apps/explorer/lib/explorer/smart_contract/reader.ex
+++ b/apps/explorer/lib/explorer/smart_contract/reader.ex
@@ -60,25 +60,32 @@ defmodule Explorer.SmartContract.Reader do
)
# => %{"sum" => {:error, "Data overflow encoding int, data `abc` cannot fit in 256 bits"}}
"""
- @spec query_verified_contract(Hash.Address.t(), functions(), String.t() | nil, SmartContract.abi()) ::
+ @spec query_verified_contract(Hash.Address.t(), functions(), String.t() | nil, true | false, SmartContract.abi()) ::
functions_results()
- def query_verified_contract(address_hash, functions, from, mabi) do
- query_verified_contract_inner(address_hash, functions, mabi, from)
+ def query_verified_contract(address_hash, functions, from, leave_error_as_map, mabi) do
+ query_verified_contract_inner(address_hash, functions, mabi, from, leave_error_as_map)
end
- @spec query_verified_contract(Hash.Address.t(), functions(), SmartContract.abi() | nil) :: functions_results()
- def query_verified_contract(address_hash, functions, mabi \\ nil) do
- query_verified_contract_inner(address_hash, functions, mabi, nil)
+ @spec query_verified_contract(Hash.Address.t(), functions(), true | false, SmartContract.abi() | nil) ::
+ functions_results()
+ def query_verified_contract(address_hash, functions, leave_error_as_map, mabi \\ nil) do
+ query_verified_contract_inner(address_hash, functions, mabi, nil, leave_error_as_map)
end
- @spec query_verified_contract_inner(Hash.Address.t(), functions(), SmartContract.abi() | nil, String.t() | nil) ::
+ @spec query_verified_contract_inner(
+ Hash.Address.t(),
+ functions(),
+ SmartContract.abi() | nil,
+ String.t() | nil,
+ true | false
+ ) ::
functions_results()
- defp query_verified_contract_inner(address_hash, functions, mabi, from) do
+ defp query_verified_contract_inner(address_hash, functions, mabi, from, leave_error_as_map) do
contract_address = Hash.to_string(address_hash)
abi = prepare_abi(mabi, address_hash)
- query_contract(contract_address, from, abi, functions)
+ query_contract(contract_address, from, abi, functions, leave_error_as_map)
end
defp prepare_abi(nil, address_hash) do
@@ -103,20 +110,22 @@ defmodule Explorer.SmartContract.Reader do
@spec query_contract(
String.t(),
term(),
- functions()
+ functions(),
+ true | false
) :: functions_results()
- def query_contract(contract_address, abi, functions) do
- query_contract_inner(contract_address, abi, functions, nil, nil)
+ def query_contract(contract_address, abi, functions, leave_error_as_map) do
+ query_contract_inner(contract_address, abi, functions, nil, nil, leave_error_as_map)
end
@spec query_contract(
String.t(),
String.t() | nil,
term(),
- functions()
+ functions(),
+ true | false
) :: functions_results()
- def query_contract(contract_address, from, abi, functions) do
- query_contract_inner(contract_address, abi, functions, nil, from)
+ def query_contract(contract_address, from, abi, functions, leave_error_as_map) do
+ query_contract_inner(contract_address, abi, functions, nil, from, leave_error_as_map)
end
@spec query_contract_by_block_number(
@@ -125,11 +134,11 @@ defmodule Explorer.SmartContract.Reader do
functions(),
non_neg_integer()
) :: functions_results()
- def query_contract_by_block_number(contract_address, abi, functions, block_number) do
- query_contract_inner(contract_address, abi, functions, block_number, nil)
+ def query_contract_by_block_number(contract_address, abi, functions, block_number, leave_error_as_map \\ false) do
+ query_contract_inner(contract_address, abi, functions, block_number, nil, leave_error_as_map)
end
- defp query_contract_inner(contract_address, abi, functions, block_number, from) do
+ defp query_contract_inner(contract_address, abi, functions, block_number, from, leave_error_as_map) do
requests =
functions
|> Enum.map(fn {method_id, args} ->
@@ -143,7 +152,7 @@ defmodule Explorer.SmartContract.Reader do
end)
requests
- |> query_contracts(abi)
+ |> query_contracts(abi, [], leave_error_as_map)
|> Enum.zip(requests)
|> Enum.into(%{}, fn {response, request} ->
{request.method_id, response}
@@ -207,6 +216,13 @@ defmodule Explorer.SmartContract.Reader do
end
end
+ @spec query_contracts([Contract.call()], term(), true | false) :: [Contract.call_result()]
+ def query_contracts(requests, abi, [], leave_error_as_map) do
+ json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments)
+
+ EthereumJSONRPC.execute_contract_functions(requests, abi, json_rpc_named_arguments, leave_error_as_map)
+ end
+
@doc """
List all the smart contract functions with its current value from the
blockchain, following the ABI order.
@@ -254,7 +270,7 @@ defmodule Explorer.SmartContract.Reader do
abi_with_method_id
|> Enum.filter(&Helper.queriable_method?(&1))
- |> Enum.map(&fetch_current_value_from_blockchain(&1, abi_with_method_id, contract_address_hash))
+ |> Enum.map(&fetch_current_value_from_blockchain(&1, abi_with_method_id, contract_address_hash, false))
end
end
@@ -270,7 +286,9 @@ defmodule Explorer.SmartContract.Reader do
implementation_abi_with_method_id
|> Enum.filter(&Helper.queriable_method?(&1))
- |> Enum.map(&fetch_current_value_from_blockchain(&1, implementation_abi_with_method_id, contract_address_hash))
+ |> Enum.map(
+ &fetch_current_value_from_blockchain(&1, implementation_abi_with_method_id, contract_address_hash, false)
+ )
end
end
@@ -388,7 +406,7 @@ defmodule Explorer.SmartContract.Reader do
"tuple[#{tuple_types}]"
end
- def fetch_current_value_from_blockchain(function, abi, contract_address_hash) do
+ def fetch_current_value_from_blockchain(function, abi, contract_address_hash, leave_error_as_map) do
values =
case function do
%{"inputs" => []} ->
@@ -397,7 +415,7 @@ defmodule Explorer.SmartContract.Reader do
outputs = function["outputs"]
contract_address_hash
- |> query_verified_contract(%{method_id => normalize_args(args)}, abi)
+ |> query_verified_contract(%{method_id => normalize_args(args)}, leave_error_as_map, abi)
|> link_outputs_and_values(outputs, method_id)
_ ->
@@ -410,13 +428,14 @@ defmodule Explorer.SmartContract.Reader do
@doc """
Method performs query of read functions of a smart contract.
`type` could be :proxy or :reqular
+ if ethereumJSONRPC will return some errors it will represented as map
"""
@spec query_function_with_names(Hash.t(), %{method_id: String.t(), args: [term()] | nil}, atom(), String.t()) :: %{
:names => [any()],
:output => [%{}]
}
def query_function_with_names(contract_address_hash, %{method_id: method_id, args: args}, type, function_name) do
- outputs = query_function(contract_address_hash, %{method_id: method_id, args: args}, type)
+ outputs = query_function(contract_address_hash, %{method_id: method_id, args: args}, type, true)
names = parse_names_from_abi(get_abi(contract_address_hash, type), function_name)
%{output: outputs, names: names}
end
@@ -434,7 +453,7 @@ defmodule Explorer.SmartContract.Reader do
String.t()
) :: %{:names => [any()], :output => [%{}]}
def query_function_with_names(contract_address_hash, %{method_id: method_id, args: args}, type, function_name, from) do
- outputs = query_function(contract_address_hash, %{method_id: method_id, args: args}, type, from)
+ outputs = query_function(contract_address_hash, %{method_id: method_id, args: args}, type, from, true)
names = parse_names_from_abi(get_abi(contract_address_hash, type), function_name)
%{output: outputs, names: names}
end
@@ -442,42 +461,44 @@ defmodule Explorer.SmartContract.Reader do
@doc """
Fetches the blockchain value of a function that requires arguments.
"""
- @spec query_function(String.t(), %{method_id: String.t(), args: nil}, atom()) :: [%{}]
- def query_function(contract_address_hash, %{method_id: method_id, args: nil}, type) do
- query_function(contract_address_hash, %{method_id: method_id, args: []}, type)
+ @spec query_function(String.t(), %{method_id: String.t(), args: nil}, atom(), true | false) :: [%{}]
+ def query_function(contract_address_hash, %{method_id: method_id, args: nil}, type, leave_error_as_map) do
+ query_function(contract_address_hash, %{method_id: method_id, args: []}, type, leave_error_as_map)
end
- @spec query_function(Hash.t(), %{method_id: String.t(), args: [term()]}, atom()) :: [%{}]
- def query_function(contract_address_hash, %{method_id: method_id, args: args}, type) do
- query_function_inner(contract_address_hash, method_id, args, type, nil)
+ @spec query_function(Hash.t(), %{method_id: String.t(), args: [term()]}, atom(), true | false) :: [%{}]
+ def query_function(contract_address_hash, %{method_id: method_id, args: args}, type, leave_error_as_map) do
+ query_function_inner(contract_address_hash, method_id, args, type, nil, leave_error_as_map)
end
- @spec query_function(String.t(), %{method_id: String.t(), args: nil}, atom(), String.t() | nil) :: [%{}]
- def query_function(contract_address_hash, %{method_id: method_id, args: nil}, type, from) do
- query_function(contract_address_hash, %{method_id: method_id, args: []}, type, from)
+ @spec query_function(String.t(), %{method_id: String.t(), args: nil}, atom(), String.t() | nil, true | false) :: [%{}]
+ def query_function(contract_address_hash, %{method_id: method_id, args: nil}, type, from, leave_error_as_map) do
+ query_function(contract_address_hash, %{method_id: method_id, args: []}, type, from, leave_error_as_map)
end
- @spec query_function(Hash.t(), %{method_id: String.t(), args: [term()]}, atom(), String.t() | nil) :: [%{}]
- def query_function(contract_address_hash, %{method_id: method_id, args: args}, type, from) do
- query_function_inner(contract_address_hash, method_id, args, type, from)
+ @spec query_function(Hash.t(), %{method_id: String.t(), args: [term()]}, atom(), String.t() | nil, true | false) :: [
+ %{}
+ ]
+ def query_function(contract_address_hash, %{method_id: method_id, args: args}, type, from, leave_error_as_map) do
+ query_function_inner(contract_address_hash, method_id, args, type, from, leave_error_as_map)
end
- @spec query_function_inner(Hash.t(), String.t(), [term()], atom(), String.t() | nil) :: [%{}]
- defp query_function_inner(contract_address_hash, method_id, args, type, from) do
+ @spec query_function_inner(Hash.t(), String.t(), [term()], atom(), String.t() | nil, true | false) :: [%{}]
+ defp query_function_inner(contract_address_hash, method_id, args, type, from, leave_error_as_map) do
abi = get_abi(contract_address_hash, type)
parsed_final_abi =
abi
|> ABI.parse_specification()
- %{outputs: outputs, method_id: method_id} = process_abi(parsed_final_abi, method_id)
+ %{outputs: outputs, method_id: method_id} = proccess_abi(parsed_final_abi, method_id)
- query_contract_and_link_outputs(contract_address_hash, args, from, abi, outputs, method_id)
+ query_contract_and_link_outputs(contract_address_hash, args, from, abi, outputs, method_id, leave_error_as_map)
end
- defp process_abi([nil], _method_id), do: nil
+ defp proccess_abi(nil, _method_id), do: nil
- defp process_abi(abi, method_id) do
+ defp proccess_abi(abi, method_id) do
function_object = find_function_by_method(abi, method_id)
%ABI.FunctionSelector{returns: returns, method_id: method_id} = function_object
outputs = extract_outputs(returns)
@@ -485,9 +506,9 @@ defmodule Explorer.SmartContract.Reader do
%{outputs: outputs, method_id: method_id}
end
- defp query_contract_and_link_outputs(contract_address_hash, args, from, abi, outputs, method_id) do
+ defp query_contract_and_link_outputs(contract_address_hash, args, from, abi, outputs, method_id, leave_error_as_map) do
contract_address_hash
- |> query_verified_contract(%{method_id => normalize_args(args)}, from, abi)
+ |> query_verified_contract(%{method_id => normalize_args(args)}, from, leave_error_as_map, abi)
|> link_outputs_and_values(outputs, method_id)
end
diff --git a/apps/explorer/lib/explorer/smart_contract/verifier/constructor_arguments.ex b/apps/explorer/lib/explorer/smart_contract/verifier/constructor_arguments.ex
index 5126e4b280c6..15f4c89d43a1 100644
--- a/apps/explorer/lib/explorer/smart_contract/verifier/constructor_arguments.ex
+++ b/apps/explorer/lib/explorer/smart_contract/verifier/constructor_arguments.ex
@@ -220,6 +220,7 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do
)
<<>> ->
+ # we should consdider change: use just :false
check_func.("")
<<_::binary-size(2)>> <> rest ->
@@ -278,10 +279,46 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do
if check_func_result do
check_func_result
else
- extract_constructor_arguments(filtered_constructor_arguments, check_func, contract_source_code, contract_name)
+ output =
+ extract_constructor_arguments(filtered_constructor_arguments, check_func, contract_source_code, contract_name)
+
+ if output do
+ output
+ else
+ # https://github.com/blockscout/blockscout/pull/4764
+ clean_constructor_arguments =
+ remove_substring_of_require_messages(filtered_constructor_arguments, contract_source_code, contract_name)
+
+ check_func.(clean_constructor_arguments)
+ end
end
end
+ defp remove_substring_of_require_messages(constructor_arguments, contract_source_code, contract_name) do
+ require_msgs =
+ contract_source_code
+ |> extract_require_messages_from_constructor(contract_name)
+ |> Enum.filter(fn require_msg -> require_msg != nil end)
+
+ longest_substring_to_delete =
+ Enum.reduce(require_msgs, "", fn msg, best_match ->
+ case String.myers_difference(constructor_arguments, msg) do
+ [{:eq, match} | _] ->
+ if String.length(match) > String.length(best_match) do
+ match
+ else
+ best_match
+ end
+
+ _ ->
+ best_match
+ end
+ end)
+
+ [_, cleaned_constructor_arguments] = String.split(constructor_arguments, longest_substring_to_delete, parts: 2)
+ cleaned_constructor_arguments
+ end
+
def remove_require_messages_from_constructor_arguments(contract_source_code, constructor_arguments, contract_name) do
all_msgs =
contract_source_code
@@ -345,7 +382,8 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do
assumed_arguments
rescue
- _ -> false
+ _ ->
+ false
end
end
diff --git a/apps/explorer/lib/explorer/smart_contract/vyper/publisher.ex b/apps/explorer/lib/explorer/smart_contract/vyper/publisher.ex
index c22590e58cd6..258f0ea888b4 100644
--- a/apps/explorer/lib/explorer/smart_contract/vyper/publisher.ex
+++ b/apps/explorer/lib/explorer/smart_contract/vyper/publisher.ex
@@ -63,6 +63,7 @@ defmodule Explorer.SmartContract.Vyper.Publisher do
secondary_sources: [],
abi: abi,
verified_via_sourcify: false,
+ partially_verified: false,
is_vyper_contract: true
}
end
diff --git a/apps/explorer/lib/explorer/staking/contract_state.ex b/apps/explorer/lib/explorer/staking/contract_state.ex
index 3bbeeff18a8a..7f75551adedc 100644
--- a/apps/explorer/lib/explorer/staking/contract_state.ex
+++ b/apps/explorer/lib/explorer/staking/contract_state.ex
@@ -104,18 +104,28 @@ defmodule Explorer.Staking.ContractState do
"2d21d217" => {:ok, [token_contract_address]},
"dfc8bf4e" => {:ok, [validator_set_contract_address]}
} =
- Reader.query_contract(staking_contract_address, staking_abi, %{
- "#{erc_677_token_contract_signature}" => [],
- "#{validator_set_contract_signature}" => []
- })
+ Reader.query_contract(
+ staking_contract_address,
+ staking_abi,
+ %{
+ "#{erc_677_token_contract_signature}" => [],
+ "#{validator_set_contract_signature}" => []
+ },
+ false
+ )
# 56b54bae = keccak256(blockRewardContract())
block_reward_contract_signature = "56b54bae"
%{"56b54bae" => {:ok, [block_reward_contract_address]}} =
- Reader.query_contract(validator_set_contract_address, validator_set_abi, %{
- "#{block_reward_contract_signature}" => []
- })
+ Reader.query_contract(
+ validator_set_contract_address,
+ validator_set_abi,
+ %{
+ "#{block_reward_contract_signature}" => []
+ },
+ false
+ )
state = %__MODULE__{
eth_blocknumber_pull_interval: eth_blocknumber_pull_interval,
diff --git a/apps/explorer/lib/explorer/token/balance_reader.ex b/apps/explorer/lib/explorer/token/balance_reader.ex
index 22a45fec547d..0a093c8cd7b0 100644
--- a/apps/explorer/lib/explorer/token/balance_reader.ex
+++ b/apps/explorer/lib/explorer/token/balance_reader.ex
@@ -27,6 +27,18 @@ defmodule Explorer.Token.BalanceReader do
}
]
+ @erc1155_balance_function_abi [
+ %{
+ "constant" => true,
+ "inputs" => [%{"name" => "_owner", "type" => "address"}, %{"name" => "_id", "type" => "uint256"}],
+ "name" => "balanceOf",
+ "outputs" => [%{"name" => "", "type" => "uint256"}],
+ "payable" => false,
+ "stateMutability" => "view",
+ "type" => "function"
+ }
+ ]
+
@spec get_balances_of([
%{token_contract_address_hash: String.t(), address_hash: String.t(), block_number: non_neg_integer()}
]) :: [{:ok, non_neg_integer()} | {:error, String.t()}]
@@ -37,6 +49,31 @@ defmodule Explorer.Token.BalanceReader do
|> Enum.map(&format_balance_result/1)
end
+ @spec get_balances_of_with_abi(
+ [
+ %{token_contract_address_hash: String.t(), address_hash: String.t(), block_number: non_neg_integer()}
+ ],
+ [%{}]
+ ) :: [{:ok, non_neg_integer()} | {:error, String.t()}]
+ def get_balances_of_with_abi(token_balance_requests, abi) do
+ formatted_balances_requests =
+ if abi == @erc1155_balance_function_abi do
+ token_balance_requests
+ |> Enum.map(&format_erc_1155_balance_request/1)
+ else
+ token_balance_requests
+ |> Enum.map(&format_balance_request/1)
+ end
+
+ if Enum.count(formatted_balances_requests) > 0 do
+ formatted_balances_requests
+ |> Reader.query_contracts(abi)
+ |> Enum.map(&format_balance_result/1)
+ else
+ []
+ end
+ end
+
defp format_balance_request(%{
address_hash: address_hash,
block_number: block_number,
@@ -50,6 +87,20 @@ defmodule Explorer.Token.BalanceReader do
}
end
+ defp format_erc_1155_balance_request(%{
+ address_hash: address_hash,
+ block_number: block_number,
+ token_contract_address_hash: token_contract_address_hash,
+ token_id: token_id
+ }) do
+ %{
+ contract_address: token_contract_address_hash,
+ method_id: "00fdd58e",
+ args: [address_hash, token_id],
+ block_number: block_number
+ }
+ end
+
defp format_balance_result({:ok, [balance]}) do
{:ok, balance}
end
diff --git a/apps/explorer/lib/explorer/token/instance_metadata_retriever.ex b/apps/explorer/lib/explorer/token/instance_metadata_retriever.ex
index e9c6c06ad332..aa10d73c493b 100644
--- a/apps/explorer/lib/explorer/token/instance_metadata_retriever.ex
+++ b/apps/explorer/lib/explorer/token/instance_metadata_retriever.ex
@@ -29,9 +29,36 @@ defmodule Explorer.Token.InstanceMetadataRetriever do
}
]
+ @uri "0e89341c"
+
+ @abi_uri [
+ %{
+ "type" => "function",
+ "stateMutability" => "view",
+ "payable" => false,
+ "outputs" => [
+ %{
+ "type" => "string",
+ "name" => "",
+ "internalType" => "string"
+ }
+ ],
+ "name" => "uri",
+ "inputs" => [
+ %{
+ "type" => "uint256",
+ "name" => "_id",
+ "internalType" => "uint256"
+ }
+ ],
+ "constant" => true
+ }
+ ]
+
@cryptokitties_address_hash "0x06012c8cf97bead5deae237070f9587f8e7a266d"
@no_uri_error "no uri"
+ @vm_execution_error "VM execution error"
def fetch_metadata(unquote(@cryptokitties_address_hash), token_id) do
%{"tokenURI" => {:ok, ["https://api.cryptokitties.co/kitties/#{token_id}"]}}
@@ -42,31 +69,62 @@ defmodule Explorer.Token.InstanceMetadataRetriever do
# c87b56dd = keccak256(tokenURI(uint256))
contract_functions = %{@token_uri => [token_id]}
- contract_address_hash
- |> query_contract(contract_functions)
- |> fetch_json()
+ res =
+ contract_address_hash
+ |> query_contract(contract_functions, @abi)
+ |> fetch_json()
+
+ if res == {:ok, %{error: @vm_execution_error}} do
+ contract_functions_uri = %{@uri => [token_id]}
+
+ contract_address_hash
+ |> query_contract(contract_functions_uri, @abi_uri)
+ |> fetch_json()
+ else
+ res
+ end
end
- def query_contract(contract_address_hash, contract_functions) do
- Reader.query_contract(contract_address_hash, @abi, contract_functions)
+ def query_contract(contract_address_hash, contract_functions, abi) do
+ Reader.query_contract(contract_address_hash, abi, contract_functions, false)
end
- def fetch_json(%{@token_uri => {:ok, [""]}}) do
+ def fetch_json(uri) when uri in [%{@token_uri => {:ok, [""]}}, %{@uri => {:ok, [""]}}] do
{:ok, %{error: @no_uri_error}}
end
- def fetch_json(%{@token_uri => {:error, "(-32015) VM execution error."}}) do
- {:ok, %{error: @no_uri_error}}
+ def fetch_json(uri)
+ when uri in [
+ %{@token_uri => {:error, "(-32015) VM execution error."}},
+ %{@uri => {:error, "(-32015) VM execution error."}}
+ ] do
+ {:ok, %{error: @vm_execution_error}}
+ end
+
+ def fetch_json(%{@token_uri => {:error, "(-32015) VM execution error." <> _}}) do
+ {:ok, %{error: @vm_execution_error}}
+ end
+
+ def fetch_json(%{@uri => {:error, "(-32015) VM execution error." <> _}}) do
+ {:ok, %{error: @vm_execution_error}}
end
def fetch_json(%{@token_uri => {:ok, ["http://" <> _ = token_uri]}}) do
fetch_metadata(token_uri)
end
+ def fetch_json(%{@uri => {:ok, ["http://" <> _ = token_uri]}}) do
+ fetch_metadata(token_uri)
+ end
+
def fetch_json(%{@token_uri => {:ok, ["https://" <> _ = token_uri]}}) do
fetch_metadata(token_uri)
end
+ def fetch_json(%{@uri => {:ok, ["https://" <> _ = token_uri]}}) do
+ fetch_metadata(token_uri)
+ end
+
def fetch_json(%{@token_uri => {:ok, ["data:application/json," <> json]}}) do
decoded_json = URI.decode(json)
@@ -80,16 +138,39 @@ defmodule Explorer.Token.InstanceMetadataRetriever do
{:error, json}
end
+ def fetch_json(%{@uri => {:ok, ["data:application/json," <> json]}}) do
+ decoded_json = URI.decode(json)
+
+ fetch_json(%{@token_uri => {:ok, [decoded_json]}})
+ rescue
+ e ->
+ Logger.debug(["Unknown metadata format #{inspect(json)}. error #{inspect(e)}"],
+ fetcher: :token_instances
+ )
+
+ {:error, json}
+ end
+
def fetch_json(%{@token_uri => {:ok, ["ipfs://ipfs/" <> ipfs_uid]}}) do
ipfs_url = "https://ipfs.io/ipfs/" <> ipfs_uid
fetch_metadata(ipfs_url)
end
+ def fetch_json(%{@uri => {:ok, ["ipfs://ipfs/" <> ipfs_uid]}}) do
+ ipfs_url = "https://ipfs.io/ipfs/" <> ipfs_uid
+ fetch_metadata(ipfs_url)
+ end
+
def fetch_json(%{@token_uri => {:ok, ["ipfs://" <> ipfs_uid]}}) do
ipfs_url = "https://ipfs.io/ipfs/" <> ipfs_uid
fetch_metadata(ipfs_url)
end
+ def fetch_json(%{@uri => {:ok, ["ipfs://" <> ipfs_uid]}}) do
+ ipfs_url = "https://ipfs.io/ipfs/" <> ipfs_uid
+ fetch_metadata(ipfs_url)
+ end
+
def fetch_json(%{@token_uri => {:ok, [json]}}) do
{:ok, json} = decode_json(json)
@@ -103,17 +184,30 @@ defmodule Explorer.Token.InstanceMetadataRetriever do
{:error, json}
end
+ def fetch_json(%{@uri => {:ok, [json]}}) do
+ {:ok, json} = decode_json(json)
+
+ check_type(json)
+ rescue
+ e ->
+ Logger.debug(["Unknown metadata format #{inspect(json)}. error #{inspect(e)}"],
+ fetcher: :token_instances
+ )
+
+ {:error, json}
+ end
+
def fetch_json(result) do
Logger.debug(["Unknown metadata format #{inspect(result)}."], fetcher: :token_instances)
{:error, result}
end
- defp fetch_metadata(token_uri) do
- case HTTPoison.get(token_uri) do
+ defp fetch_metadata(uri) do
+ case HTTPoison.get(uri) do
{:ok, %Response{body: body, status_code: 200, headers: headers}} ->
if Enum.member?(headers, {"Content-Type", "image/png"}) do
- json = %{"image" => token_uri}
+ json = %{"image" => uri}
check_type(json)
else
@@ -135,7 +229,7 @@ defmodule Explorer.Token.InstanceMetadataRetriever do
end
rescue
e ->
- Logger.debug(["Could not send request to token uri #{inspect(token_uri)}. error #{inspect(e)}"],
+ Logger.debug(["Could not send request to token uri #{inspect(uri)}. error #{inspect(e)}"],
fetcher: :token_instances
)
diff --git a/apps/explorer/lib/explorer/token/metadata_retriever.ex b/apps/explorer/lib/explorer/token/metadata_retriever.ex
index 9d9e6cbdb2d6..304b9330b68d 100644
--- a/apps/explorer/lib/explorer/token/metadata_retriever.ex
+++ b/apps/explorer/lib/explorer/token/metadata_retriever.ex
@@ -206,7 +206,7 @@ defmodule Explorer.Token.MetadataRetriever do
defp fetch_functions_with_retries(contract_address_hash, contract_functions, accumulator, retries_left)
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, false)
functions_with_errors =
Enum.filter(contract_functions_result, fn function ->
diff --git a/apps/explorer/lib/explorer/validator/metadata_retriever.ex b/apps/explorer/lib/explorer/validator/metadata_retriever.ex
index 1ad306413077..99fb83db799d 100644
--- a/apps/explorer/lib/explorer/validator/metadata_retriever.ex
+++ b/apps/explorer/lib/explorer/validator/metadata_retriever.ex
@@ -17,9 +17,14 @@ defmodule Explorer.Validator.MetadataRetriever do
defp fetch_validators_list do
# b7ab4db5 = keccak256(getValidators())
- case Reader.query_contract(config(:validators_contract_address), contract_abi("validators.json"), %{
- "b7ab4db5" => []
- }) do
+ case Reader.query_contract(
+ config(:validators_contract_address),
+ contract_abi("validators.json"),
+ %{
+ "b7ab4db5" => []
+ },
+ false
+ ) do
%{"b7ab4db5" => {:ok, [validators]}} -> validators
_ -> []
end
@@ -28,9 +33,14 @@ defmodule Explorer.Validator.MetadataRetriever do
defp fetch_validator_metadata(validator_address) do
# fa52c7d8 = keccak256(validators(address))
%{"fa52c7d8" => {:ok, fields}} =
- Reader.query_contract(config(:metadata_contract_address), contract_abi("metadata.json"), %{
- "fa52c7d8" => [validator_address]
- })
+ Reader.query_contract(
+ config(:metadata_contract_address),
+ contract_abi("metadata.json"),
+ %{
+ "fa52c7d8" => [validator_address]
+ },
+ false
+ )
fields
end
diff --git a/apps/explorer/package-lock.json b/apps/explorer/package-lock.json
index a56446b608dc..5ad4b481c6e7 100644
--- a/apps/explorer/package-lock.json
+++ b/apps/explorer/package-lock.json
@@ -1,7 +1,255 @@
{
"name": "blockscout",
+ "lockfileVersion": 2,
"requires": true,
- "lockfileVersion": 1,
+ "packages": {
+ "": {
+ "name": "blockscout",
+ "license": "GPL-3.0",
+ "dependencies": {
+ "solc": "^0.8.10"
+ },
+ "engines": {
+ "node": "16.x",
+ "npm": "8.x"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/command-exists": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz",
+ "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w=="
+ },
+ "node_modules/commander": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
+ "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.14.5",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz",
+ "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/fs-extra": {
+ "version": "0.30.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz",
+ "integrity": "sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A=",
+ "dependencies": {
+ "graceful-fs": "^4.1.2",
+ "jsonfile": "^2.1.0",
+ "klaw": "^1.0.0",
+ "path-is-absolute": "^1.0.0",
+ "rimraf": "^2.2.8"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+ },
+ "node_modules/glob": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
+ "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.8",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz",
+ "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg=="
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "node_modules/js-sha3": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz",
+ "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q=="
+ },
+ "node_modules/jsonfile": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
+ "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/klaw": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz",
+ "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=",
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.9"
+ }
+ },
+ "node_modules/memorystream": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
+ "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=",
+ "engines": {
+ "node": ">= 0.10.0"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ }
+ },
+ "node_modules/semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "bin": {
+ "semver": "bin/semver"
+ }
+ },
+ "node_modules/solc": {
+ "version": "0.8.10",
+ "resolved": "https://registry.npmjs.org/solc/-/solc-0.8.10.tgz",
+ "integrity": "sha512-I/Mcn6J5bEtJqveNLplQm9IRrhemm6v+qkw5S2+wM4x9HItJ1sYdrqVTN3j4DMhFDM3ZbvM0QywVzpPx666PHw==",
+ "dependencies": {
+ "command-exists": "^1.2.8",
+ "commander": "^8.1.0",
+ "follow-redirects": "^1.12.1",
+ "fs-extra": "^0.30.0",
+ "js-sha3": "0.8.0",
+ "memorystream": "^0.3.1",
+ "require-from-string": "^2.0.0",
+ "semver": "^5.5.0",
+ "tmp": "0.0.33"
+ },
+ "bin": {
+ "solcjs": "solcjs"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/tmp": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "dependencies": {
+ "os-tmpdir": "~1.0.2"
+ },
+ "engines": {
+ "node": ">=0.6.0"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ }
+ },
"dependencies": {
"balanced-match": {
"version": "1.0.2",
diff --git a/apps/explorer/package.json b/apps/explorer/package.json
index 1c9a2260d7bf..87d9f769af52 100644
--- a/apps/explorer/package.json
+++ b/apps/explorer/package.json
@@ -8,8 +8,8 @@
"author": "POA Network",
"license": "GPL-3.0",
"engines": {
- "node": "14.x",
- "npm": "7.x"
+ "node": "16.x",
+ "npm": "8.x"
},
"scripts": {},
"dependencies": {
diff --git a/apps/explorer/priv/repo/migrations/20200214152058_add_token_id_to_token_balances.exs b/apps/explorer/priv/repo/migrations/20200214152058_add_token_id_to_token_balances.exs
new file mode 100644
index 000000000000..b009091f81fd
--- /dev/null
+++ b/apps/explorer/priv/repo/migrations/20200214152058_add_token_id_to_token_balances.exs
@@ -0,0 +1,12 @@
+defmodule Explorer.Repo.Migrations.AddTokenIdToTokenBalances do
+ use Ecto.Migration
+
+ def change do
+ alter table(:address_token_balances) do
+ add(:token_id, :numeric, precision: 78, scale: 0, null: true)
+ add(:token_type, :string, null: true)
+ end
+
+ create(index(:address_token_balances, [:token_id]))
+ end
+end
diff --git a/apps/explorer/priv/repo/migrations/20210422115740_add_token_id_to_current_token_balances.exs b/apps/explorer/priv/repo/migrations/20210422115740_add_token_id_to_current_token_balances.exs
new file mode 100644
index 000000000000..4d8d8705298d
--- /dev/null
+++ b/apps/explorer/priv/repo/migrations/20210422115740_add_token_id_to_current_token_balances.exs
@@ -0,0 +1,12 @@
+defmodule Explorer.Repo.Migrations.AddTokenIdToCurrentTokenBalances do
+ use Ecto.Migration
+
+ def change do
+ alter table(:address_current_token_balances) do
+ add(:token_id, :numeric, precision: 78, scale: 0, null: true)
+ add(:token_type, :string, null: true)
+ end
+
+ create(index(:address_current_token_balances, [:token_id]))
+ end
+end
diff --git a/apps/explorer/priv/repo/migrations/20210423084253_address_current_token_balances_add_token_id_to_unique_index.exs b/apps/explorer/priv/repo/migrations/20210423084253_address_current_token_balances_add_token_id_to_unique_index.exs
new file mode 100644
index 000000000000..b71de0448ee1
--- /dev/null
+++ b/apps/explorer/priv/repo/migrations/20210423084253_address_current_token_balances_add_token_id_to_unique_index.exs
@@ -0,0 +1,25 @@
+defmodule Explorer.Repo.Migrations.AddressCurrentTokenBalancesAddTokenIdToUniqueIndex do
+ use Ecto.Migration
+
+ def change do
+ drop(unique_index(:address_current_token_balances, ~w(address_hash token_contract_address_hash)a))
+
+ create(
+ unique_index(
+ :address_current_token_balances,
+ ~w(address_hash token_contract_address_hash token_id)a,
+ name: :fetched_current_token_balances_with_token_id,
+ where: "token_id IS NOT NULL"
+ )
+ )
+
+ create(
+ unique_index(
+ :address_current_token_balances,
+ ~w(address_hash token_contract_address_hash)a,
+ name: :fetched_current_token_balances,
+ where: "token_id IS NULL"
+ )
+ )
+ end
+end
diff --git a/apps/explorer/priv/repo/migrations/20210423091652_address_token_balances_add_token_id_to_unique_index.exs b/apps/explorer/priv/repo/migrations/20210423091652_address_token_balances_add_token_id_to_unique_index.exs
new file mode 100644
index 000000000000..1968f52132f4
--- /dev/null
+++ b/apps/explorer/priv/repo/migrations/20210423091652_address_token_balances_add_token_id_to_unique_index.exs
@@ -0,0 +1,25 @@
+defmodule Explorer.Repo.Migrations.AddressTokenBalancesAddTokenIdToUniqueIndex do
+ use Ecto.Migration
+
+ def change do
+ drop(unique_index(:address_token_balances, ~w(address_hash token_contract_address_hash block_number)a))
+
+ create(
+ unique_index(
+ :address_token_balances,
+ ~w(address_hash token_contract_address_hash token_id block_number)a,
+ name: :fetched_token_balances_with_token_id,
+ where: "token_id IS NOT NULL"
+ )
+ )
+
+ create(
+ unique_index(
+ :address_token_balances,
+ ~w(address_hash token_contract_address_hash block_number)a,
+ name: :fetched_token_balances,
+ where: "token_id IS NULL"
+ )
+ )
+ end
+end
diff --git a/apps/explorer/priv/repo/migrations/20210423094801_address_token_balances_change_unfetched_token_balances_unique_index.exs b/apps/explorer/priv/repo/migrations/20210423094801_address_token_balances_change_unfetched_token_balances_unique_index.exs
new file mode 100644
index 000000000000..911a7affbb41
--- /dev/null
+++ b/apps/explorer/priv/repo/migrations/20210423094801_address_token_balances_change_unfetched_token_balances_unique_index.exs
@@ -0,0 +1,32 @@
+defmodule Explorer.Repo.Migrations.AddressTokenBalancesChangeUnfetchedTokenBalancesUniqueIndex do
+ use Ecto.Migration
+
+ def change do
+ drop(
+ unique_index(
+ :address_token_balances,
+ ~w(address_hash token_contract_address_hash block_number)a,
+ name: :unfetched_token_balances,
+ where: "value_fetched_at IS NULL"
+ )
+ )
+
+ create(
+ unique_index(
+ :address_token_balances,
+ ~w(address_hash token_contract_address_hash block_number)a,
+ name: :unfetched_token_balances,
+ where: "value_fetched_at IS NULL and token_id IS NULL"
+ )
+ )
+
+ create(
+ unique_index(
+ :address_token_balances,
+ ~w(address_hash token_contract_address_hash token_id block_number)a,
+ name: :unfetched_token_balances_with_token_id,
+ where: "value_fetched_at IS NULL and token_id IS NOT NULL"
+ )
+ )
+ end
+end
diff --git a/apps/explorer/priv/repo/migrations/20210423115108_extend_token_transfers_for_erc1155.exs b/apps/explorer/priv/repo/migrations/20210423115108_extend_token_transfers_for_erc1155.exs
new file mode 100644
index 000000000000..211524e95492
--- /dev/null
+++ b/apps/explorer/priv/repo/migrations/20210423115108_extend_token_transfers_for_erc1155.exs
@@ -0,0 +1,10 @@
+defmodule Explorer.Repo.Migrations.ExtendTokenTransfersForErc1155 do
+ use Ecto.Migration
+
+ def change do
+ alter table(:token_transfers) do
+ add(:amounts, {:array, :decimal}, null: true)
+ add(:token_ids, {:array, :numeric}, precision: 78, scale: 0, null: true)
+ end
+ end
+end
diff --git a/apps/explorer/priv/repo/migrations/20211006121008_add_block_is_empty_flag.exs b/apps/explorer/priv/repo/migrations/20211006121008_add_block_is_empty_flag.exs
new file mode 100644
index 000000000000..609f342453bf
--- /dev/null
+++ b/apps/explorer/priv/repo/migrations/20211006121008_add_block_is_empty_flag.exs
@@ -0,0 +1,11 @@
+defmodule Explorer.Repo.Migrations.AddBlockIsEmptyFlag do
+ use Ecto.Migration
+
+ def change do
+ alter table(:blocks) do
+ add(:is_empty, :bool, null: true)
+ end
+
+ create(index(:blocks, [:is_empty]))
+ end
+end
diff --git a/apps/explorer/priv/repo/migrations/20211013190346_remove_duplicates_of_current_token_balances.exs b/apps/explorer/priv/repo/migrations/20211013190346_remove_duplicates_of_current_token_balances.exs
new file mode 100644
index 000000000000..ec26db4b7364
--- /dev/null
+++ b/apps/explorer/priv/repo/migrations/20211013190346_remove_duplicates_of_current_token_balances.exs
@@ -0,0 +1,55 @@
+defmodule Explorer.Repo.Migrations.RemoveDuplicatesOfCurrentTokenBalances do
+ use Ecto.Migration
+
+ def change do
+ execute("""
+ DELETE FROM address_current_token_balances
+ WHERE id in (
+ SELECT a.id FROM (SELECT actb.*
+ FROM address_current_token_balances actb
+ INNER JOIN tokens t
+ ON actb.token_contract_address_hash = t.contract_address_hash
+ WHERE t.type='ERC-721') AS a
+ LEFT JOIN
+ (SELECT actb.address_hash, actb.token_contract_address_hash, MAX(actb.value_fetched_at) AS max_value_fetched_at
+ FROM address_current_token_balances actb
+ INNER JOIN tokens t
+ ON actb.token_contract_address_hash = t.contract_address_hash
+ WHERE t.type='ERC-721'
+ GROUP BY token_contract_address_hash, address_hash) c
+ ON a.address_hash=c.address_hash AND a.token_contract_address_hash = c.token_contract_address_hash AND a.value_fetched_at = c.max_value_fetched_at
+ WHERE c.address_hash IS NULL
+ );
+ """)
+
+ execute("""
+ UPDATE address_current_token_balances
+ SET token_id = NULL
+ WHERE id in (
+ SELECT a.id FROM (SELECT actb.*
+ FROM address_current_token_balances actb
+ INNER JOIN tokens t
+ ON actb.token_contract_address_hash = t.contract_address_hash
+ WHERE t.type='ERC-721'
+ AND actb.token_id IS NOT NULL
+ ) a
+ );
+ """)
+
+ execute("""
+ UPDATE address_current_token_balances
+ SET token_type = t.type
+ FROM tokens t
+ WHERE address_current_token_balances.token_type IS NULL
+ AND t.contract_address_hash = address_current_token_balances.token_contract_address_hash;
+ """)
+
+ execute("""
+ UPDATE address_token_balances
+ SET token_type = t.type
+ FROM tokens t
+ WHERE address_token_balances.token_type IS NULL
+ AND t.contract_address_hash = address_token_balances.token_contract_address_hash;
+ """)
+ end
+end
diff --git a/apps/explorer/priv/repo/migrations/20211017135545_migrate_optimization_runs_to_int8.exs b/apps/explorer/priv/repo/migrations/20211017135545_migrate_optimization_runs_to_int8.exs
new file mode 100644
index 000000000000..0ff9a6d23d80
--- /dev/null
+++ b/apps/explorer/priv/repo/migrations/20211017135545_migrate_optimization_runs_to_int8.exs
@@ -0,0 +1,15 @@
+defmodule Explorer.Repo.Migrations.MigrateOptimizationRunsToInt8 do
+ use Ecto.Migration
+
+ def up do
+ alter table(:smart_contracts) do
+ modify(:optimization_runs, :bigint)
+ end
+ end
+
+ def down do
+ alter table(:smart_contracts) do
+ modify(:optimization_runs, :integer)
+ end
+ end
+end
diff --git a/apps/explorer/priv/repo/migrations/20211018072347_add_is_empty_index.exs b/apps/explorer/priv/repo/migrations/20211018072347_add_is_empty_index.exs
new file mode 100644
index 000000000000..8631b31307d7
--- /dev/null
+++ b/apps/explorer/priv/repo/migrations/20211018072347_add_is_empty_index.exs
@@ -0,0 +1,17 @@
+defmodule Explorer.Repo.Migrations.AddIsEmptyIndex do
+ use Ecto.Migration
+ @disable_ddl_transaction true
+ @disable_migration_lock true
+
+ def change do
+ create(
+ index(
+ :blocks,
+ ~w(consensus)a,
+ name: :empty_consensus_blocks,
+ where: "is_empty IS NULL",
+ concurrently: true
+ )
+ )
+ end
+end
diff --git a/apps/explorer/priv/repo/migrations/20211018073652_add_token_balances_contract_address_hash_index.exs b/apps/explorer/priv/repo/migrations/20211018073652_add_token_balances_contract_address_hash_index.exs
new file mode 100644
index 000000000000..ebfa8920e2f4
--- /dev/null
+++ b/apps/explorer/priv/repo/migrations/20211018073652_add_token_balances_contract_address_hash_index.exs
@@ -0,0 +1,12 @@
+defmodule Explorer.Repo.Migrations.AddTokenBalancesContractAddressHashIndex do
+ use Ecto.Migration
+
+ def change do
+ create(
+ index(
+ :address_token_balances,
+ ~w(token_contract_address_hash)a
+ )
+ )
+ end
+end
diff --git a/apps/explorer/priv/repo/migrations/20211018164843_transactions_block_number_index.exs b/apps/explorer/priv/repo/migrations/20211018164843_transactions_block_number_index.exs
new file mode 100644
index 000000000000..bb561b7c2375
--- /dev/null
+++ b/apps/explorer/priv/repo/migrations/20211018164843_transactions_block_number_index.exs
@@ -0,0 +1,12 @@
+defmodule Explorer.Repo.Migrations.TransactionsBlockNumberIndex do
+ use Ecto.Migration
+
+ def change do
+ create(
+ index(
+ :transactions,
+ ~w(block_number)a
+ )
+ )
+ end
+end
diff --git a/apps/explorer/priv/repo/migrations/20211018170533_add_address_token_balances_address_hash_token_contract_address_hash_block_number_index.exs b/apps/explorer/priv/repo/migrations/20211018170533_add_address_token_balances_address_hash_token_contract_address_hash_block_number_index.exs
new file mode 100644
index 000000000000..4965726c5dc7
--- /dev/null
+++ b/apps/explorer/priv/repo/migrations/20211018170533_add_address_token_balances_address_hash_token_contract_address_hash_block_number_index.exs
@@ -0,0 +1,12 @@
+defmodule Explorer.Repo.Migrations.AddAddressTokenBalancesAddressHashTokenContractAddressHashBlockNumberIndex do
+ use Ecto.Migration
+
+ def change do
+ create(
+ index(
+ :address_token_balances,
+ ~w(address_hash token_contract_address_hash block_number)a
+ )
+ )
+ end
+end
diff --git a/apps/explorer/priv/repo/migrations/20211018170638_add_logs_address_hash_transaction_hash_index.exs b/apps/explorer/priv/repo/migrations/20211018170638_add_logs_address_hash_transaction_hash_index.exs
new file mode 100644
index 000000000000..5d15ac38cd9a
--- /dev/null
+++ b/apps/explorer/priv/repo/migrations/20211018170638_add_logs_address_hash_transaction_hash_index.exs
@@ -0,0 +1,12 @@
+defmodule Explorer.Repo.Migrations.AddLogsAddressHashTransactionHashIndex do
+ use Ecto.Migration
+
+ def change do
+ create(
+ index(
+ :logs,
+ ~w(address_hash transaction_hash)a
+ )
+ )
+ end
+end
diff --git a/apps/explorer/priv/repo/migrations/20211029085117_drop_block_rewards_block_hash_partial_index.exs b/apps/explorer/priv/repo/migrations/20211029085117_drop_block_rewards_block_hash_partial_index.exs
new file mode 100644
index 000000000000..4d6bb4014e63
--- /dev/null
+++ b/apps/explorer/priv/repo/migrations/20211029085117_drop_block_rewards_block_hash_partial_index.exs
@@ -0,0 +1,14 @@
+defmodule Explorer.Repo.Migrations.DropBlockRewardsBlockHashPartialIndex do
+ use Ecto.Migration
+
+ def change do
+ drop_if_exists(
+ index(
+ :block_rewadrs,
+ ~w(block_hash)a,
+ name: :block_rewards_block_hash_partial_index,
+ where: "address_type='validator'"
+ )
+ )
+ end
+end
diff --git a/apps/explorer/test/explorer/chain/import/runner/address/current_token_balances_test.exs b/apps/explorer/test/explorer/chain/import/runner/address/current_token_balances_test.exs
index e7dcfe4b06f8..7c34c72719cd 100644
--- a/apps/explorer/test/explorer/chain/import/runner/address/current_token_balances_test.exs
+++ b/apps/explorer/test/explorer/chain/import/runner/address/current_token_balances_test.exs
@@ -65,6 +65,141 @@ defmodule Explorer.Chain.Import.Runner.Address.CurrentTokenBalancesTest do
assert current_token_balances == 1
end
+ test "inserts values for multiple token IDs in the current token balances", %{
+ address: %Address{hash: address_hash},
+ token: %Token{contract_address_hash: token_contract_address_hash},
+ options: options
+ } do
+ value_1 = Decimal.new(111)
+ token_id_1 = Decimal.new(1)
+
+ value_2 = Decimal.new(222)
+ token_id_2 = Decimal.new(2)
+
+ token_erc_20 = insert(:token, holder_count: 0)
+ token_erc_20_contract_address_hash = token_erc_20.contract_address_hash
+ value_3 = Decimal.new(333)
+ token_id_3 = nil
+
+ token_erc_721 = insert(:token, holder_count: 0)
+ token_erc_721_contract_address_hash = token_erc_721.contract_address_hash
+ value_4 = Decimal.new(1)
+ token_id_4 = Decimal.new(1)
+
+ value_5 = Decimal.new(2)
+ token_id_5 = Decimal.new(555)
+
+ block_number = 1
+
+ assert {:ok,
+ %{
+ address_current_token_balances: [
+ %Explorer.Chain.Address.CurrentTokenBalance{
+ address_hash: ^address_hash,
+ block_number: ^block_number,
+ token_contract_address_hash: ^token_erc_20_contract_address_hash,
+ value: ^value_3,
+ token_id: ^token_id_3
+ },
+ %Explorer.Chain.Address.CurrentTokenBalance{
+ address_hash: ^address_hash,
+ block_number: ^block_number,
+ token_contract_address_hash: ^token_erc_721_contract_address_hash,
+ value: ^value_5,
+ token_id: nil
+ },
+ %Explorer.Chain.Address.CurrentTokenBalance{
+ address_hash: ^address_hash,
+ block_number: ^block_number,
+ token_contract_address_hash: ^token_contract_address_hash,
+ value: ^value_1,
+ token_id: ^token_id_1
+ },
+ %Explorer.Chain.Address.CurrentTokenBalance{
+ address_hash: ^address_hash,
+ block_number: ^block_number,
+ token_contract_address_hash: ^token_contract_address_hash,
+ value: ^value_2,
+ token_id: ^token_id_2
+ }
+ ],
+ address_current_token_balances_update_token_holder_counts: [
+ %{
+ contract_address_hash: ^token_contract_address_hash,
+ holder_count: 2
+ },
+ %{
+ contract_address_hash: ^token_erc_20_contract_address_hash,
+ holder_count: 1
+ },
+ %{
+ contract_address_hash: ^token_erc_721_contract_address_hash,
+ holder_count: 1
+ }
+ ]
+ }} =
+ run_changes_list(
+ [
+ %{
+ address_hash: address_hash,
+ block_number: block_number,
+ token_contract_address_hash: token_contract_address_hash,
+ value: value_1,
+ value_fetched_at: DateTime.utc_now(),
+ token_id: token_id_1,
+ token_type: "ERC-1155"
+ },
+ %{
+ address_hash: address_hash,
+ block_number: block_number,
+ token_contract_address_hash: token_contract_address_hash,
+ value: value_2,
+ value_fetched_at: DateTime.utc_now(),
+ token_id: token_id_2,
+ token_type: "ERC-1155"
+ },
+ %{
+ address_hash: address_hash,
+ block_number: block_number,
+ token_contract_address_hash: token_erc_20.contract_address_hash,
+ value: value_3,
+ value_fetched_at: DateTime.utc_now(),
+ token_id: token_id_3,
+ token_type: "ERC-20"
+ },
+ %{
+ address_hash: address_hash,
+ block_number: block_number,
+ token_contract_address_hash: token_erc_721.contract_address_hash,
+ value: value_4,
+ value_fetched_at: DateTime.utc_now(),
+ token_id: token_id_4,
+ token_type: "ERC-721"
+ },
+ %{
+ address_hash: address_hash,
+ block_number: block_number,
+ token_contract_address_hash: token_erc_721.contract_address_hash,
+ value: value_5,
+ value_fetched_at: DateTime.utc_now(),
+ token_id: token_id_5,
+ token_type: "ERC-721"
+ }
+ ],
+ options
+ )
+
+ current_token_balances =
+ CurrentTokenBalance
+ |> Repo.all()
+
+ current_token_balances_count =
+ current_token_balances
+ |> Enum.count()
+
+ assert current_token_balances_count == 4
+ end
+
test "updates when the new block number is greater", %{
address: address,
token: token,
diff --git a/apps/explorer/test/explorer/chain/import/runner/address/token_balances_test.exs b/apps/explorer/test/explorer/chain/import/runner/address/token_balances_test.exs
index a35fbcc57847..39e40c7e73e4 100644
--- a/apps/explorer/test/explorer/chain/import/runner/address/token_balances_test.exs
+++ b/apps/explorer/test/explorer/chain/import/runner/address/token_balances_test.exs
@@ -29,7 +29,9 @@ defmodule Explorer.Chain.Import.Runner.Address.TokenBalancesTest do
block_number: block_number,
token_contract_address_hash: token_contract_address_hash,
value: value,
- value_fetched_at: value_fetched_at
+ value_fetched_at: value_fetched_at,
+ token_id: 11,
+ token_type: "ERC-20"
}
assert {:ok,
@@ -69,7 +71,9 @@ defmodule Explorer.Chain.Import.Runner.Address.TokenBalancesTest do
block_number: block_number,
token_contract_address_hash: token_contract_address_hash,
value: nil,
- value_fetched_at: value_fetched_at
+ value_fetched_at: value_fetched_at,
+ token_id: nil,
+ token_type: "ERC-20"
}
assert {:ok,
@@ -97,6 +101,58 @@ defmodule Explorer.Chain.Import.Runner.Address.TokenBalancesTest do
end
end
+ test "does not nillifies existing value ERC-1155" do
+ address = insert(:address)
+ token = insert(:token)
+
+ options = %{
+ timeout: :infinity,
+ timestamps: %{inserted_at: DateTime.utc_now(), updated_at: DateTime.utc_now()}
+ }
+
+ value_fetched_at = DateTime.utc_now()
+
+ block_number = 1
+
+ value = Decimal.new(100)
+
+ token_contract_address_hash = token.contract_address_hash
+ address_hash = address.hash
+
+ changes = %{
+ address_hash: address_hash,
+ block_number: block_number,
+ token_contract_address_hash: token_contract_address_hash,
+ value: nil,
+ value_fetched_at: value_fetched_at,
+ token_id: 11,
+ token_type: "ERC-1155"
+ }
+
+ assert {:ok,
+ %{
+ address_token_balances: [
+ %TokenBalance{
+ address_hash: address_hash,
+ block_number: ^block_number,
+ token_contract_address_hash: ^token_contract_address_hash,
+ value: nil,
+ value_fetched_at: ^value_fetched_at
+ }
+ ]
+ }} = run_changes(changes, options)
+
+ new_changes = %{
+ address_hash: address_hash,
+ block_number: block_number,
+ token_contract_address_hash: token_contract_address_hash,
+ value: value,
+ value_fetched_at: DateTime.utc_now()
+ }
+
+ run_changes(new_changes, options)
+ end
+
defp run_changes(changes, options) when is_map(changes) do
run_changes_list([changes], options)
end
diff --git a/apps/explorer/test/explorer/chain/import_test.exs b/apps/explorer/test/explorer/chain/import_test.exs
index 81434ed585f2..1a2842f69a17 100644
--- a/apps/explorer/test/explorer/chain/import_test.exs
+++ b/apps/explorer/test/explorer/chain/import_test.exs
@@ -380,17 +380,20 @@ defmodule Explorer.Chain.ImportTest do
%{
address_hash: "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca",
token_contract_address_hash: "0x8bf38d4764929064f2d4d3a56520a76ab3df415b",
- block_number: "37"
+ block_number: "37",
+ token_type: "ERC-20"
},
%{
address_hash: "0x515c09c5bba1ed566b02a5b0599ec5d5d0aee73d",
token_contract_address_hash: "0x8bf38d4764929064f2d4d3a56520a76ab3df415b",
- block_number: "37"
+ block_number: "37",
+ token_type: "ERC-20"
},
%{
address_hash: "0x8bf38d4764929064f2d4d3a56520a76ab3df415b",
token_contract_address_hash: "0x8bf38d4764929064f2d4d3a56520a76ab3df415b",
- block_number: "37"
+ block_number: "37",
+ token_type: "ERC-20"
}
],
timeout: 5
@@ -430,13 +433,15 @@ defmodule Explorer.Chain.ImportTest do
address_hash: "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca",
token_contract_address_hash: "0x8bf38d4764929064f2d4d3a56520a76ab3df415b",
block_number: "37",
- value: 200
+ value: 200,
+ token_type: "ERC-20"
},
%{
address_hash: "0x515c09c5bba1ed566b02a5b0599ec5d5d0aee73d",
token_contract_address_hash: "0x8bf38d4764929064f2d4d3a56520a76ab3df415b",
block_number: "37",
- value: 100
+ value: 100,
+ token_type: "ERC-20"
}
],
timeout: 5
@@ -1569,8 +1574,8 @@ defmodule Explorer.Chain.ImportTest do
},
address_coin_balances: %{
params: [
- %{address_hash: miner_hash, block_number: block_number, value: nil},
- %{address_hash: uncle_miner_hash, block_number: block_number, value: nil}
+ %{address_hash: miner_hash, block_number: block_number, value: nil, token_type: "ERC-20"},
+ %{address_hash: uncle_miner_hash, block_number: block_number, value: nil, token_type: "ERC-20"}
],
timeout: 1
},
@@ -2262,7 +2267,8 @@ defmodule Explorer.Chain.ImportTest do
address_hash: address_hash,
token_contract_address_hash: token_contract_address_hash,
block_number: block_number,
- value: value_after
+ value: value_after,
+ token_type: "ERC-20"
}
]
},
@@ -2272,7 +2278,8 @@ defmodule Explorer.Chain.ImportTest do
address_hash: address_hash,
token_contract_address_hash: token_contract_address_hash,
block_number: block_number,
- value: value_after
+ value: value_after,
+ token_type: "ERC-20"
}
]
},
diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs
index 65e2e2cad402..92eeb808afc2 100644
--- a/apps/explorer/test/explorer/chain_test.exs
+++ b/apps/explorer/test/explorer/chain_test.exs
@@ -5912,6 +5912,18 @@ defmodule Explorer.ChainTest do
assert implementation_abi == @implementation_abi
end
+
+ test "get_total_staked_and_ordered should return just nil in case of invalid input and some response otherwise" do
+ assert Chain.get_total_staked_and_ordered(nil) == nil
+ assert Chain.get_total_staked_and_ordered(%{}) == nil
+ assert Chain.get_total_staked_and_ordered("") == nil
+ assert Chain.get_total_staked_and_ordered([]) == nil
+
+ assert Chain.get_total_staked_and_ordered("0x3f7c51ef174ee8a62e3fcfb0947aa90c97bd2784") == %{
+ stake_amount: Decimal.new(0),
+ ordered_withdraw: Decimal.new(0)
+ }
+ end
end
describe "fetch_sum_celo_unlocked/0" do
diff --git a/apps/explorer/test/explorer/history/process_test.exs b/apps/explorer/test/explorer/history/process_test.exs
index 7489585df973..fc58f2d3a5d8 100644
--- a/apps/explorer/test/explorer/history/process_test.exs
+++ b/apps/explorer/test/explorer/history/process_test.exs
@@ -52,7 +52,7 @@ defmodule Explorer.History.ProcessTest do
record = %{date: ~D[2018-04-01], closing_price: Decimal.new(10), opening_price: Decimal.new(5)}
TestHistorian
- |> expect(:compile_records, fn 1 -> {:ok, [record]} end)
+ |> expect(:compile_records, fn 2 -> {:ok, [record]} end)
|> expect(:save_records, fn _ -> :ok end)
state = %{historian: TestHistorian}
@@ -66,10 +66,10 @@ defmodule Explorer.History.ProcessTest do
assert {:noreply, state} == HistoryProcess.handle_info({nil, {1, 0, {:ok, [record]}}}, state)
# Message isn't sent before interval is up
- refute_receive {:compile_historical_records, 1}, history_fetch_interval - 1
+ refute_receive {:compile_historical_records, 2}, history_fetch_interval - 1
# Now message is sent
- assert_receive {:compile_historical_records, 1}
+ assert_receive {:compile_historical_records, 2}
end
test "handle_info with failed task" do
diff --git a/apps/explorer/test/explorer/smart_contract/reader_test.exs b/apps/explorer/test/explorer/smart_contract/reader_test.exs
index 3b9fb2d05362..befc365095bf 100644
--- a/apps/explorer/test/explorer/smart_contract/reader_test.exs
+++ b/apps/explorer/test/explorer/smart_contract/reader_test.exs
@@ -21,7 +21,7 @@ defmodule Explorer.SmartContract.ReaderTest do
blockchain_get_function_mock()
- response = Reader.query_contract(contract_address_hash, abi, %{"6d4ce63c" => []})
+ response = Reader.query_contract(contract_address_hash, abi, %{"6d4ce63c" => []}, false)
assert %{"6d4ce63c" => {:ok, [0]}} == response
end
@@ -44,7 +44,7 @@ defmodule Explorer.SmartContract.ReaderTest do
string_argument = %{"a50e1860" => ["abc"]}
- response = Reader.query_contract(contract_address_hash, [int_function_abi], string_argument)
+ response = Reader.query_contract(contract_address_hash, [int_function_abi], string_argument, false)
assert %{"a50e1860" => {:error, "Data overflow encoding int, data `abc` cannot fit in 256 bits"}} = response
end
@@ -62,7 +62,7 @@ defmodule Explorer.SmartContract.ReaderTest do
end
)
- response = Reader.query_contract(contract_address_hash, abi, %{"6d4ce63c" => []})
+ response = Reader.query_contract(contract_address_hash, abi, %{"6d4ce63c" => []}, false)
assert %{"6d4ce63c" => {:error, "(12345) Error message"}} = response
end
@@ -80,7 +80,7 @@ defmodule Explorer.SmartContract.ReaderTest do
end
)
- response = Reader.query_contract(contract_address_hash, abi, %{"6d4ce63c" => []})
+ response = Reader.query_contract(contract_address_hash, abi, %{"6d4ce63c" => []}, false)
assert %{"6d4ce63c" => {:error, "Bad gateway"}} = response
end
@@ -98,7 +98,7 @@ defmodule Explorer.SmartContract.ReaderTest do
end
)
- response = Reader.query_contract(contract_address_hash, abi, %{"6d4ce63c" => []})
+ response = Reader.query_contract(contract_address_hash, abi, %{"6d4ce63c" => []}, false)
assert %{"6d4ce63c" => {:error, "no function clause matches"}} = response
end
@@ -113,7 +113,7 @@ defmodule Explorer.SmartContract.ReaderTest do
blockchain_get_function_mock()
- assert Reader.query_verified_contract(hash, %{"6d4ce63c" => []}) == %{"6d4ce63c" => {:ok, [0]}}
+ assert Reader.query_verified_contract(hash, %{"6d4ce63c" => []}, false) == %{"6d4ce63c" => {:ok, [0]}}
end
end
@@ -264,7 +264,7 @@ defmodule Explorer.SmartContract.ReaderTest do
"type" => "uint256",
"value" => 0
}
- ] = Reader.query_function(smart_contract.address_hash, %{method_id: "6d4ce63c", args: []}, :regular)
+ ] = Reader.query_function(smart_contract.address_hash, %{method_id: "6d4ce63c", args: []}, :regular, false)
end
test "nil arguments is treated as []" do
@@ -277,7 +277,8 @@ defmodule Explorer.SmartContract.ReaderTest do
"type" => "uint256",
"value" => 0
}
- ] = Reader.query_function(smart_contract.address_hash, %{method_id: "6d4ce63c", args: nil}, :regular)
+ ] =
+ Reader.query_function(smart_contract.address_hash, %{method_id: "6d4ce63c", args: nil}, :regular, false)
end
end
diff --git a/apps/explorer/test/explorer/token/balance_reader_test.exs b/apps/explorer/test/explorer/token/balance_reader_test.exs
index 1a98dd2455ff..126e903cde3c 100644
--- a/apps/explorer/test/explorer/token/balance_reader_test.exs
+++ b/apps/explorer/test/explorer/token/balance_reader_test.exs
@@ -32,7 +32,8 @@ defmodule Explorer.Token.BalanceReaderTest do
%{
token_contract_address_hash: token_contract_address_hash,
address_hash: address_hash,
- block_number: block_number
+ block_number: block_number,
+ token_type: "ERC-20"
}
])
@@ -51,7 +52,8 @@ defmodule Explorer.Token.BalanceReaderTest do
%{
token_contract_address_hash: token_contract_address_hash,
address_hash: address_hash,
- block_number: block_number
+ block_number: block_number,
+ token_type: "ERC-20"
}
])
diff --git a/apps/explorer/test/explorer/token/instance_metadata_retriever_test.exs b/apps/explorer/test/explorer/token/instance_metadata_retriever_test.exs
index eec3b38e49b6..3afe5c937f05 100644
--- a/apps/explorer/test/explorer/token/instance_metadata_retriever_test.exs
+++ b/apps/explorer/test/explorer/token/instance_metadata_retriever_test.exs
@@ -9,6 +9,49 @@ defmodule Explorer.Token.InstanceMetadataRetrieverTest do
setup :verify_on_exit!
setup :set_mox_global
+ @abi [
+ %{
+ "type" => "function",
+ "stateMutability" => "view",
+ "payable" => false,
+ "outputs" => [
+ %{"type" => "string", "name" => ""}
+ ],
+ "name" => "tokenURI",
+ "inputs" => [
+ %{
+ "type" => "uint256",
+ "name" => "_tokenId"
+ }
+ ],
+ "constant" => true
+ }
+ ]
+
+ @abi_uri [
+ %{
+ "type" => "function",
+ "stateMutability" => "view",
+ "payable" => false,
+ "outputs" => [
+ %{
+ "type" => "string",
+ "name" => "",
+ "internalType" => "string"
+ }
+ ],
+ "name" => "uri",
+ "inputs" => [
+ %{
+ "type" => "uint256",
+ "name" => "_id",
+ "internalType" => "uint256"
+ }
+ ],
+ "constant" => true
+ }
+ ]
+
describe "fetch_metadata/2" do
@tag :no_parity
@tag :no_geth
@@ -46,9 +89,56 @@ defmodule Explorer.Token.InstanceMetadataRetrieverTest do
assert %{
"c87b56dd" => {:ok, ["https://vault.warriders.com/18290729947667102496.json"]}
} ==
- InstanceMetadataRetriever.query_contract("0x5caebd3b32e210e85ce3e9d51638b9c445481567", %{
- "c87b56dd" => [18_290_729_947_667_102_496]
- })
+ InstanceMetadataRetriever.query_contract(
+ "0x5caebd3b32e210e85ce3e9d51638b9c445481567",
+ %{
+ "c87b56dd" => [18_290_729_947_667_102_496]
+ },
+ @abi
+ )
+ end
+
+ test "fetches json metadata for ERC-1155 token", %{json_rpc_named_arguments: json_rpc_named_arguments} do
+ if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do
+ EthereumJSONRPC.Mox
+ |> expect(:json_rpc, fn [
+ %{
+ id: 0,
+ jsonrpc: "2.0",
+ method: "eth_call",
+ params: [
+ %{
+ data:
+ "0x0e89341c000000000000000000000000000000000000000000000000fdd5b9fa9d4bfb20",
+ to: "0x5caebd3b32e210e85ce3e9d51638b9c445481567"
+ },
+ "latest"
+ ]
+ }
+ ],
+ _options ->
+ {:ok,
+ [
+ %{
+ id: 0,
+ jsonrpc: "2.0",
+ result:
+ "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003568747470733a2f2f7661756c742e7761727269646572732e636f6d2f31383239303732393934373636373130323439362e6a736f6e0000000000000000000000"
+ }
+ ]}
+ end)
+ end
+
+ assert %{
+ "0e89341c" => {:ok, ["https://vault.warriders.com/18290729947667102496.json"]}
+ } ==
+ InstanceMetadataRetriever.query_contract(
+ "0x5caebd3b32e210e85ce3e9d51638b9c445481567",
+ %{
+ "0e89341c" => [18_290_729_947_667_102_496]
+ },
+ @abi_uri
+ )
end
end
diff --git a/apps/explorer/test/support/factory.ex b/apps/explorer/test/support/factory.ex
index 2a54b2338bd3..e1319188cdcf 100644
--- a/apps/explorer/test/support/factory.ex
+++ b/apps/explorer/test/support/factory.ex
@@ -605,7 +605,8 @@ defmodule Explorer.Factory do
token_contract_address_hash: insert(:token).contract_address_hash,
block_number: block_number(),
value: Enum.random(1..100_000),
- value_fetched_at: DateTime.utc_now()
+ value_fetched_at: DateTime.utc_now(),
+ token_type: "ERC-20"
}
end
diff --git a/apps/indexer/config/dev.exs b/apps/indexer/config/dev.exs
index 85c3bb8abe68..02061549a832 100644
--- a/apps/indexer/config/dev.exs
+++ b/apps/indexer/config/dev.exs
@@ -26,6 +26,11 @@ config :logger, :pending_transactions_to_refetch,
path: Path.absname("logs/dev/indexer/pending_transactions_to_refetch.log"),
metadata_filter: [fetcher: :pending_transactions_to_refetch]
+config :logger, :empty_blocks_to_refetch,
+ level: :debug,
+ path: Path.absname("logs/dev/indexer/empty_blocks_to_refetch.log"),
+ metadata_filter: [fetcher: :empty_blocks_to_refetch]
+
variant =
if is_nil(System.get_env("ETHEREUM_JSONRPC_VARIANT")) do
"ganache"
diff --git a/apps/indexer/config/prod.exs b/apps/indexer/config/prod.exs
index 5929e719424f..76f4bcac52af 100644
--- a/apps/indexer/config/prod.exs
+++ b/apps/indexer/config/prod.exs
@@ -16,17 +16,26 @@ config :logger, :indexer_token_balances,
config :logger, :failed_contract_creations,
level: :debug,
path: Path.absname("logs/prod/indexer/failed_contract_creations.log"),
- metadata_filter: [fetcher: :failed_created_addresses]
+ metadata_filter: [fetcher: :failed_created_addresses],
+ rotate: %{max_bytes: 52_428_800, keep: 19}
config :logger, :addresses_without_code,
level: :debug,
path: Path.absname("logs/prod/indexer/addresses_without_code.log"),
- metadata_filter: [fetcher: :addresses_without_code]
+ metadata_filter: [fetcher: :addresses_without_code],
+ rotate: %{max_bytes: 52_428_800, keep: 19}
config :logger, :pending_transactions_to_refetch,
level: :debug,
path: Path.absname("logs/prod/indexer/pending_transactions_to_refetch.log"),
- metadata_filter: [fetcher: :pending_transactions_to_refetch]
+ metadata_filter: [fetcher: :pending_transactions_to_refetch],
+ rotate: %{max_bytes: 52_428_800, keep: 19}
+
+config :logger, :empty_blocks_to_refetch,
+ level: :info,
+ path: Path.absname("logs/prod/indexer/empty_blocks_to_refetch.log"),
+ metadata_filter: [fetcher: :empty_blocks_to_refetch],
+ rotate: %{max_bytes: 52_428_800, keep: 19}
variant =
if is_nil(System.get_env("ETHEREUM_JSONRPC_VARIANT")) do
diff --git a/apps/indexer/lib/indexer/block/catchup/bound_interval_supervisor.ex b/apps/indexer/lib/indexer/block/catchup/bound_interval_supervisor.ex
index 235507512f2d..244febdf3669 100644
--- a/apps/indexer/lib/indexer/block/catchup/bound_interval_supervisor.ex
+++ b/apps/indexer/lib/indexer/block/catchup/bound_interval_supervisor.ex
@@ -264,6 +264,19 @@ defmodule Indexer.Block.Catchup.BoundIntervalSupervisor do
{:noreply, %__MODULE__{state | task: nil}}
end
+ def handle_info(
+ {ref, {:error, :etimedout}},
+ %__MODULE__{
+ task: %Task{ref: ref}
+ } = state
+ ) do
+ Logger.info("Index had to catch up, but the but request is timing out, so retrying immediately.")
+
+ send(self(), :catchup_index)
+
+ {:noreply, %__MODULE__{state | task: nil}}
+ end
+
def handle_info(
{:DOWN, ref, :process, pid, reason},
%__MODULE__{task: %Task{pid: pid, ref: ref}} = state
@@ -274,4 +287,15 @@ defmodule Indexer.Block.Catchup.BoundIntervalSupervisor do
{:noreply, %__MODULE__{state | task: nil}}
end
+
+ def handle_info(
+ {:DOWN, _ref, :process, _pid, reason},
+ %__MODULE__{task: nil} = state
+ ) do
+ Logger.error(fn -> "Catchup index stream exited with reason (#{inspect(reason)}). Restarting" end)
+
+ send(self(), :catchup_index)
+
+ {:noreply, %__MODULE__{state | task: nil}}
+ end
end
diff --git a/apps/indexer/lib/indexer/block/catchup/fetcher.ex b/apps/indexer/lib/indexer/block/catchup/fetcher.ex
index 08bee1d346e1..311096b0383a 100644
--- a/apps/indexer/lib/indexer/block/catchup/fetcher.ex
+++ b/apps/indexer/lib/indexer/block/catchup/fetcher.ex
@@ -71,61 +71,74 @@ defmodule Indexer.Block.Catchup.Fetcher do
) do
Logger.metadata(fetcher: :block_catchup)
- {:ok, latest_block_number} =
- case latest_block() do
- nil ->
- EthereumJSONRPC.fetch_block_number_by_tag("latest", json_rpc_named_arguments)
-
- number ->
- {:ok, number}
+ with {:ok, latest_block_number} <- fetch_last_block(json_rpc_named_arguments) do
+ case latest_block_number do
+ # let realtime indexer get the genesis block
+ 0 ->
+ %{first_block_number: 0, missing_block_count: 0, last_block_number: 0, shrunk: false}
+
+ _ ->
+ # realtime indexer gets the current latest block
+ first = latest_block_number - 1
+ last = last_block()
+
+ Logger.metadata(first_block_number: first, last_block_number: last)
+
+ missing_ranges = Chain.missing_block_number_ranges(first..last)
+
+ range_count = Enum.count(missing_ranges)
+
+ missing_block_count =
+ missing_ranges
+ |> Stream.map(&Enum.count/1)
+ |> Enum.sum()
+
+ Logger.debug(fn -> "Missed blocks in ranges." end,
+ missing_block_range_count: range_count,
+ missing_block_count: missing_block_count
+ )
+
+ shrunk =
+ case missing_block_count do
+ 0 ->
+ false
+
+ _ ->
+ step = step(first, last, blocks_batch_size)
+ sequence_opts = put_memory_monitor([ranges: missing_ranges, step: step], state)
+ gen_server_opts = [name: @sequence_name]
+ {:ok, sequence} = Sequence.start_link(sequence_opts, gen_server_opts)
+ Sequence.cap(sequence)
+
+ stream_fetch_and_import(state, sequence)
+
+ Shrinkable.shrunk?(sequence)
+ end
+
+ %{
+ first_block_number: first,
+ last_block_number: last,
+ missing_block_count: missing_block_count,
+ shrunk: shrunk
+ }
end
+ end
+ end
- case latest_block_number do
- # let realtime indexer get the genesis block
- 0 ->
- %{first_block_number: 0, missing_block_count: 0, last_block_number: 0, shrunk: false}
-
- _ ->
- # realtime indexer gets the current latest block
- first = latest_block_number - 1
- last = last_block()
-
- Logger.metadata(first_block_number: first, last_block_number: last)
-
- missing_ranges = Chain.missing_block_number_ranges(first..last)
-
- range_count = Enum.count(missing_ranges)
-
- missing_block_count =
- missing_ranges
- |> Stream.map(&Enum.count/1)
- |> Enum.sum()
-
- Logger.debug(fn -> "Missed blocks in ranges." end,
- missing_block_range_count: range_count,
- missing_block_count: missing_block_count
- )
-
- shrunk =
- case missing_block_count do
- 0 ->
- false
-
- _ ->
- sequence_opts = put_memory_monitor([ranges: missing_ranges, step: -1 * blocks_batch_size], state)
- gen_server_opts = [name: @sequence_name]
- {:ok, sequence} = Sequence.start_link(sequence_opts, gen_server_opts)
- Sequence.cap(sequence)
-
- stream_fetch_and_import(state, sequence)
-
- Shrinkable.shrunk?(sequence)
- end
+ defp fetch_last_block(json_rpc_named_arguments) do
+ case latest_block() do
+ nil ->
+ EthereumJSONRPC.fetch_block_number_by_tag("latest", json_rpc_named_arguments)
- %{first_block_number: first, last_block_number: last, missing_block_count: missing_block_count, shrunk: shrunk}
+ number ->
+ {:ok, number}
end
end
+ defp step(first, last, blocks_batch_size) do
+ if first < last, do: blocks_batch_size, else: -1 * blocks_batch_size
+ end
+
@async_import_remaining_block_data_options ~w(address_hash_to_fetched_balance_block_number)a
@impl Block.Fetcher
diff --git a/apps/indexer/lib/indexer/block/fetcher.ex b/apps/indexer/lib/indexer/block/fetcher.ex
index 3126aee06c53..4615b31abfcd 100644
--- a/apps/indexer/lib/indexer/block/fetcher.ex
+++ b/apps/indexer/lib/indexer/block/fetcher.ex
@@ -131,7 +131,13 @@ defmodule Indexer.Block.Fetcher do
defp add_celo_token_balances(celo_token, addresses, acc) do
Enum.reduce(addresses, acc, fn
%{fetched_coin_balance_block_number: bn, hash: hash}, acc ->
- MapSet.put(acc, %{address_hash: hash, token_contract_address_hash: celo_token, block_number: bn})
+ MapSet.put(acc, %{
+ address_hash: hash,
+ token_contract_address_hash: celo_token,
+ block_number: bn,
+ token_type: "ERC-20",
+ token_id: nil
+ })
_, acc ->
acc
diff --git a/apps/indexer/lib/indexer/empty_blocks_sanitizer.ex b/apps/indexer/lib/indexer/empty_blocks_sanitizer.ex
new file mode 100644
index 000000000000..4f9785e6afd3
--- /dev/null
+++ b/apps/indexer/lib/indexer/empty_blocks_sanitizer.ex
@@ -0,0 +1,117 @@
+defmodule Indexer.EmptyBlocksSanitizer do
+ @moduledoc """
+ Periodically checks empty blocks starting from the head of the chain, detects for which blocks transactions should be refetched
+ and loose consensus for block in order to refetch transactions.
+ """
+
+ use GenServer
+
+ require Logger
+
+ import EthereumJSONRPC, only: [integer_to_quantity: 1, json_rpc: 2, request: 1]
+
+ alias Ecto.Changeset
+ alias Explorer.{Chain, Repo}
+ alias Explorer.Chain.Import.Runner.Blocks
+
+ # unprocessed emty blocks to fetch at once
+ @limit 400
+
+ @interval :timer.minutes(2)
+
+ defstruct interval: @interval,
+ json_rpc_named_arguments: []
+
+ def child_spec([init_arguments]) do
+ child_spec([init_arguments, []])
+ end
+
+ def child_spec([_init_arguments, _gen_server_options] = start_link_arguments) do
+ default = %{
+ id: __MODULE__,
+ start: {__MODULE__, :start_link, start_link_arguments}
+ }
+
+ Supervisor.child_spec(default, [])
+ end
+
+ def start_link(init_opts, gen_server_opts \\ []) do
+ GenServer.start_link(__MODULE__, init_opts, gen_server_opts)
+ end
+
+ def init(opts) when is_list(opts) do
+ state = %__MODULE__{
+ json_rpc_named_arguments: Keyword.fetch!(opts, :json_rpc_named_arguments),
+ interval: opts[:interval] || @interval
+ }
+
+ Process.send_after(self(), :sanitize_empty_blocks, state.interval)
+
+ {:ok, state}
+ end
+
+ def handle_info(
+ :sanitize_empty_blocks,
+ %{interval: interval, json_rpc_named_arguments: json_rpc_named_arguments} = state
+ ) do
+ Logger.info("Start sanitizing of empty blocks. Batch size is #{@limit}",
+ fetcher: :empty_blocks_to_refetch
+ )
+
+ sanitize_empty_blocks(json_rpc_named_arguments)
+
+ Process.send_after(self(), :sanitize_empty_blocks, interval)
+
+ {:noreply, state}
+ end
+
+ defp sanitize_empty_blocks(json_rpc_named_arguments) do
+ unprocessed_empty_blocks_from_db = Chain.unprocessed_empty_blocks_query_list(@limit)
+
+ unprocessed_empty_blocks_from_db
+ |> Enum.with_index()
+ |> Enum.each(fn {{block_number, block_hash}, ind} ->
+ with {:ok, %{"transactions" => transactions}} <-
+ %{id: ind, method: "eth_getBlockByNumber", params: [integer_to_quantity(block_number), false]}
+ |> request()
+ |> json_rpc(json_rpc_named_arguments) do
+ transactions_count =
+ transactions
+ |> Enum.count()
+
+ if transactions_count > 0 do
+ Logger.info(
+ "Block with number #{block_number} and hash #{to_string(block_hash)} is full of transactions. We should set consensus=false for it in order to refetch.",
+ fetcher: :empty_blocks_to_refetch
+ )
+
+ Blocks.invalidate_consensus_blocks([block_number])
+ else
+ Logger.debug(
+ "Block with number #{block_number} and hash #{to_string(block_hash)} is empty. We should set is_empty=true for it.",
+ fetcher: :empty_blocks_to_refetch
+ )
+
+ set_is_empty_for_block(block_hash)
+ end
+ end
+ end)
+
+ Logger.info("Batch of empty blocks is sanitized",
+ fetcher: :empty_blocks_to_refetch
+ )
+ end
+
+ defp set_is_empty_for_block(block_hash) do
+ block = Chain.fetch_block_by_hash(block_hash)
+
+ token =
+ block
+ |> Changeset.change(%{is_empty: true})
+
+ Repo.update(token)
+ rescue
+ postgrex_error in Postgrex.Error ->
+ {:error, %{exception: postgrex_error}}
+ end
+end
diff --git a/apps/indexer/lib/indexer/fetcher/internal_transaction.ex b/apps/indexer/lib/indexer/fetcher/internal_transaction.ex
index 6ed940d06c56..be45213031cc 100644
--- a/apps/indexer/lib/indexer/fetcher/internal_transaction.ex
+++ b/apps/indexer/lib/indexer/fetcher/internal_transaction.ex
@@ -239,7 +239,13 @@ defmodule Indexer.Fetcher.InternalTransaction do
defp add_gold_token_balances(gold_token, addresses, acc) do
Enum.reduce(addresses, acc, fn
%{fetched_coin_balance_block_number: bn, hash: hash}, acc ->
- MapSet.put(acc, %{address_hash: decode(hash), token_contract_address_hash: decode(gold_token), block_number: bn})
+ MapSet.put(acc, %{
+ address_hash: decode(hash),
+ token_contract_address_hash: decode(gold_token),
+ block_number: bn,
+ token_type: "ERC-20",
+ token_id: nil
+ })
_, acc ->
acc
diff --git a/apps/indexer/lib/indexer/fetcher/pending_transaction.ex b/apps/indexer/lib/indexer/fetcher/pending_transaction.ex
index 59d5e945a289..528be138ac96 100644
--- a/apps/indexer/lib/indexer/fetcher/pending_transaction.ex
+++ b/apps/indexer/lib/indexer/fetcher/pending_transaction.ex
@@ -136,6 +136,11 @@ defmodule Indexer.Fetcher.PendingTransaction do
:ok
+ {:error, :etimedout} ->
+ Logger.error("timeout")
+
+ :ok
+
{:error, {:bad_gateway, _}} ->
Logger.error("bad_gateway")
diff --git a/apps/indexer/lib/indexer/fetcher/token_balance.ex b/apps/indexer/lib/indexer/fetcher/token_balance.ex
index fe32ac194399..9af2d7b34476 100644
--- a/apps/indexer/lib/indexer/fetcher/token_balance.ex
+++ b/apps/indexer/lib/indexer/fetcher/token_balance.ex
@@ -96,7 +96,7 @@ defmodule Indexer.Fetcher.TokenBalance do
retryable_params_list =
params_list
|> Enum.filter(&(&1.retries_count <= @max_retries))
- |> Enum.uniq_by(&Map.take(&1, [:token_contract_address_hash, :address_hash, :block_number]))
+ |> Enum.uniq_by(&Map.take(&1, [:token_contract_address_hash, :token_id, :address_hash, :block_number]))
Logger.metadata(count: Enum.count(retryable_params_list))
@@ -107,11 +107,14 @@ defmodule Indexer.Fetcher.TokenBalance do
def import_token_balances(token_balances_params) do
addresses_params = format_and_filter_address_params(token_balances_params)
+ formatted_token_balances_params = format_and_filter_token_balance_params(token_balances_params)
import_params = %{
addresses: %{params: addresses_params},
- address_token_balances: %{params: token_balances_params},
- address_current_token_balances: %{params: TokenBalances.to_address_current_token_balances(token_balances_params)},
+ address_token_balances: %{params: formatted_token_balances_params},
+ address_current_token_balances: %{
+ params: TokenBalances.to_address_current_token_balances(formatted_token_balances_params)
+ },
timeout: :infinity
}
@@ -134,19 +137,47 @@ defmodule Indexer.Fetcher.TokenBalance do
|> Enum.uniq()
end
+ defp format_and_filter_token_balance_params(token_balances_params) do
+ token_balances_params
+ |> Enum.map(fn token_balance ->
+ if token_balance.token_type do
+ token_balance
+ else
+ token_type = Chain.get_token_type(token_balance.token_contract_address_hash)
+
+ if token_type do
+ Map.put(token_balance, :token_type, token_type)
+ else
+ token_balance
+ end
+ end
+ end)
+ end
+
defp entry(
%{
token_contract_address_hash: token_contract_address_hash,
address_hash: address_hash,
- block_number: block_number
+ block_number: block_number,
+ token_type: token_type,
+ token_id: token_id
} = token_balance
) do
retries_count = Map.get(token_balance, :retries_count, 0)
- {address_hash.bytes, token_contract_address_hash.bytes, block_number, retries_count}
+ token_id_int =
+ case token_id do
+ %Decimal{} -> Decimal.to_integer(token_id)
+ id_int when is_integer(id_int) -> id_int
+ _ -> token_id
+ end
+
+ {address_hash.bytes, token_contract_address_hash.bytes, block_number, token_type, token_id_int, retries_count}
end
- defp format_params({address_hash_bytes, token_contract_address_hash_bytes, block_number, retries_count}) do
+ defp format_params(
+ {address_hash_bytes, token_contract_address_hash_bytes, block_number, token_type, token_id, retries_count}
+ ) do
{:ok, token_contract_address_hash} = Hash.Address.cast(token_contract_address_hash_bytes)
{:ok, address_hash} = Hash.Address.cast(address_hash_bytes)
@@ -154,7 +185,9 @@ defmodule Indexer.Fetcher.TokenBalance do
token_contract_address_hash: to_string(token_contract_address_hash),
address_hash: to_string(address_hash),
block_number: block_number,
- retries_count: retries_count
+ retries_count: retries_count,
+ token_type: token_type,
+ token_id: token_id
}
end
end
diff --git a/apps/indexer/lib/indexer/pending_transactions_sanitizer.ex b/apps/indexer/lib/indexer/pending_transactions_sanitizer.ex
index da2181c1ab80..13feeb8c391f 100644
--- a/apps/indexer/lib/indexer/pending_transactions_sanitizer.ex
+++ b/apps/indexer/lib/indexer/pending_transactions_sanitizer.ex
@@ -152,12 +152,7 @@ defmodule Indexer.PendingTransactionsSanitizer do
defp invalidate_block(block_number, block_hash, consensus, pending_tx, tx) do
if consensus do
- opts = %{
- timeout: 60_000,
- timestamps: %{updated_at: DateTime.utc_now()}
- }
-
- Blocks.lose_consensus(Repo, [], [block_number], [], opts)
+ Blocks.invalidate_consensus_blocks([block_number])
else
{:ok, hash} = Hash.cast(block_hash)
tx_info = to_elixir(tx)
diff --git a/apps/indexer/lib/indexer/supervisor.ex b/apps/indexer/lib/indexer/supervisor.ex
index a8c71e80d4e9..2d495b14286d 100644
--- a/apps/indexer/lib/indexer/supervisor.ex
+++ b/apps/indexer/lib/indexer/supervisor.ex
@@ -10,6 +10,7 @@ defmodule Indexer.Supervisor do
alias Indexer.{
Block,
CalcLpTokensTotalLiqudity,
+ EmptyBlocksSanitizer,
PendingOpsCleaner,
PendingTransactionsSanitizer,
SetAmbBridgedMetadataForTokens,
@@ -136,6 +137,7 @@ defmodule Indexer.Supervisor do
# Out-of-band fetchers
{CoinBalanceOnDemand.Supervisor, [json_rpc_named_arguments]},
+ {EmptyBlocksSanitizer, [[json_rpc_named_arguments: json_rpc_named_arguments]]},
{TokenTotalSupplyOnDemand.Supervisor, [json_rpc_named_arguments]},
{PendingTransactionsSanitizer, [[json_rpc_named_arguments: json_rpc_named_arguments]]},
diff --git a/apps/indexer/lib/indexer/token_balances.ex b/apps/indexer/lib/indexer/token_balances.ex
index 9a10dad13c76..3e60f2326080 100644
--- a/apps/indexer/lib/indexer/token_balances.ex
+++ b/apps/indexer/lib/indexer/token_balances.ex
@@ -13,6 +13,18 @@ defmodule Indexer.TokenBalances do
alias Indexer.Fetcher.TokenBalance
alias Indexer.Tracer
+ @erc1155_balance_function_abi [
+ %{
+ "constant" => true,
+ "inputs" => [%{"name" => "_owner", "type" => "address"}, %{"name" => "_id", "type" => "uint256"}],
+ "name" => "balanceOf",
+ "outputs" => [%{"name" => "", "type" => "uint256"}],
+ "payable" => false,
+ "stateMutability" => "view",
+ "type" => "function"
+ }
+ ]
+
@doc """
Fetches TokenBalances from specific Addresses and Blocks in the Blockchain
@@ -26,6 +38,8 @@ defmodule Indexer.TokenBalances do
* `token_contract_address_hash` - The contract address that represents the Token in the blockchain.
* `address_hash` - The address_hash that we want to know the balance.
* `block_number` - The block number that the address_hash has the balance.
+ * `token_type` - type of the token that balance belongs to
+ * `token_id` - token id for ERC-1155 tokens
"""
def fetch_token_balances_from_blockchain([]), do: {:ok, []}
@@ -33,12 +47,39 @@ defmodule Indexer.TokenBalances do
def fetch_token_balances_from_blockchain(token_balances) do
Logger.debug("fetching token balances", count: Enum.count(token_balances))
- requested_token_balances =
+ regular_token_balances =
+ token_balances
+ |> Enum.filter(fn request ->
+ if Map.has_key?(request, :token_type) do
+ request.token_type !== "ERC-1155"
+ else
+ true
+ end
+ end)
+
+ erc1155_token_balances =
token_balances
+ |> Enum.filter(fn request ->
+ if Map.has_key?(request, :token_type) do
+ request.token_type == "ERC-1155"
+ else
+ false
+ end
+ end)
+
+ requested_regular_token_balances =
+ regular_token_balances
|> BalanceReader.get_balances_of()
- |> Stream.zip(token_balances)
+ |> Stream.zip(regular_token_balances)
|> Enum.map(fn {result, token_balance} -> set_token_balance_value(result, token_balance) end)
+ requested_erc1155_token_balances =
+ erc1155_token_balances
+ |> BalanceReader.get_balances_of_with_abi(@erc1155_balance_function_abi)
+ |> Stream.zip(erc1155_token_balances)
+ |> Enum.map(fn {result, token_balance} -> set_token_balance_value(result, token_balance) end)
+
+ requested_token_balances = requested_regular_token_balances ++ requested_erc1155_token_balances
fetched_token_balances = Enum.filter(requested_token_balances, &ignore_request_with_errors/1)
requested_token_balances
@@ -51,13 +92,17 @@ defmodule Indexer.TokenBalances do
def to_address_current_token_balances(address_token_balances) when is_list(address_token_balances) do
address_token_balances
- |> Enum.group_by(fn %{address_hash: address_hash, token_contract_address_hash: token_contract_address_hash} ->
- {address_hash, token_contract_address_hash}
+ |> Enum.group_by(fn %{
+ address_hash: address_hash,
+ token_contract_address_hash: token_contract_address_hash,
+ token_id: token_id
+ } ->
+ {address_hash, token_contract_address_hash, token_id}
end)
|> Enum.map(fn {_, grouped_address_token_balances} ->
Enum.max_by(grouped_address_token_balances, fn %{block_number: block_number} -> block_number end)
end)
- |> Enum.sort_by(&{&1.token_contract_address_hash, &1.address_hash})
+ |> Enum.sort_by(&{&1.token_contract_address_hash, &1.token_id, &1.address_hash})
end
defp set_token_balance_value({:ok, balance}, token_balance) do
@@ -137,10 +182,20 @@ defmodule Indexer.TokenBalances do
end
defp present?(list, token_balance) do
- Enum.any?(list, fn item ->
- token_balance.address_hash == item.address_hash &&
- token_balance.token_contract_address_hash == item.token_contract_address_hash &&
- token_balance.block_number == item.block_number
- end)
+ if token_balance.token_id do
+ Enum.any?(list, fn item ->
+ token_balance.address_hash == item.address_hash &&
+ token_balance.token_contract_address_hash == item.token_contract_address_hash &&
+ token_balance.token_id == item.token_id &&
+ token_balance.block_number == item.block_number
+ end)
+ else
+ Enum.any?(list, fn item ->
+ token_balance.address_hash == item.address_hash &&
+ token_balance.token_contract_address_hash == item.token_contract_address_hash &&
+ is_nil(item.token_id) &&
+ token_balance.block_number == item.block_number
+ end)
+ end
end
end
diff --git a/apps/indexer/lib/indexer/transform/address_coin_balances_daily.ex b/apps/indexer/lib/indexer/transform/address_coin_balances_daily.ex
index 58783a03c298..408c782b08b9 100644
--- a/apps/indexer/lib/indexer/transform/address_coin_balances_daily.ex
+++ b/apps/indexer/lib/indexer/transform/address_coin_balances_daily.ex
@@ -3,6 +3,8 @@ defmodule Indexer.Transform.AddressCoinBalancesDaily do
Extracts `Explorer.Chain.Address.CoinBalanceDaily` params from other schema's params.
"""
+ import EthereumJSONRPC, only: [integer_to_quantity: 1, json_rpc: 2, quantity_to_integer: 1, request: 1]
+
def params_set(%{coin_balances_params: coin_balances_params_set, blocks: blocks}) do
coin_balances_params =
coin_balances_params_set
@@ -19,14 +21,27 @@ defmodule Indexer.Transform.AddressCoinBalancesDaily do
block.number == block_number
end)
- day = DateTime.to_date(block.timestamp)
+ day =
+ if block do
+ DateTime.to_date(block.timestamp)
+ else
+ json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments)
+
+ with {:ok, %{"timestamp" => timestamp_raw}} <-
+ %{id: 1, method: "eth_getBlockByNumber", params: [integer_to_quantity(block_number), false]}
+ |> request()
+ |> json_rpc(json_rpc_named_arguments) do
+ timestamp = quantity_to_integer(timestamp_raw)
+ DateTime.from_unix!(timestamp)
+ end
+ end
[%{address_hash: address_hash, day: day} | acc]
end)
coin_balances_daily_params_set =
coin_balances_daily_params_list
- |> Enum.dedup()
+ |> Enum.uniq()
|> Enum.into(MapSet.new())
coin_balances_daily_params_set
@@ -49,7 +64,7 @@ defmodule Indexer.Transform.AddressCoinBalancesDaily do
coin_balances_daily_params_set =
coin_balances_daily_params_list
- |> Enum.dedup()
+ |> Enum.uniq()
|> Enum.into(MapSet.new())
coin_balances_daily_params_set
diff --git a/apps/indexer/lib/indexer/transform/address_token_balances.ex b/apps/indexer/lib/indexer/transform/address_token_balances.ex
index a73820daa89c..4410799aa76d 100644
--- a/apps/indexer/lib/indexer/transform/address_token_balances.ex
+++ b/apps/indexer/lib/indexer/transform/address_token_balances.ex
@@ -17,14 +17,25 @@ defmodule Indexer.Transform.AddressTokenBalances do
block_number: block_number,
from_address_hash: from_address_hash,
to_address_hash: to_address_hash,
- token_contract_address_hash: token_contract_address_hash
- },
+ token_contract_address_hash: token_contract_address_hash,
+ token_id: token_id,
+ token_type: token_type
+ } = params,
acc
when is_integer(block_number) and is_binary(from_address_hash) and
is_binary(to_address_hash) and is_binary(token_contract_address_hash) ->
- acc
- |> add_token_balance_address(from_address_hash, token_contract_address_hash, block_number)
- |> add_token_balance_address(to_address_hash, token_contract_address_hash, block_number)
+ if params[:token_ids] && token_type == "ERC-1155" do
+ params[:token_ids]
+ |> Enum.reduce(acc, fn id, sub_acc ->
+ sub_acc
+ |> add_token_balance_address(from_address_hash, token_contract_address_hash, id, token_type, block_number)
+ |> add_token_balance_address(to_address_hash, token_contract_address_hash, id, token_type, block_number)
+ end)
+ else
+ acc
+ |> add_token_balance_address(from_address_hash, token_contract_address_hash, token_id, token_type, block_number)
+ |> add_token_balance_address(to_address_hash, token_contract_address_hash, token_id, token_type, block_number)
+ end
end)
end
@@ -32,13 +43,15 @@ defmodule Indexer.Transform.AddressTokenBalances do
Enum.filter(token_transfers_params, &do_filter_burn_address/1)
end
- defp add_token_balance_address(map_set, unquote(@burn_address), _, _), do: map_set
+ defp add_token_balance_address(map_set, unquote(@burn_address), _, _, _, _), do: map_set
- defp add_token_balance_address(map_set, address, token_contract_address, block_number) do
+ defp add_token_balance_address(map_set, address, token_contract_address, token_id, token_type, block_number) do
MapSet.put(map_set, %{
address_hash: address,
token_contract_address_hash: token_contract_address,
- block_number: block_number
+ block_number: block_number,
+ token_id: token_id,
+ token_type: token_type
})
end
diff --git a/apps/indexer/lib/indexer/transform/token_transfers.ex b/apps/indexer/lib/indexer/transform/token_transfers.ex
index 8d14e7182905..366b0a1d990b 100644
--- a/apps/indexer/lib/indexer/transform/token_transfers.ex
+++ b/apps/indexer/lib/indexer/transform/token_transfers.ex
@@ -18,7 +18,7 @@ defmodule Indexer.Transform.TokenTransfers do
def parse(logs) do
initial_acc = %{tokens: [], token_transfers: []}
- token_transfers_from_logs =
+ erc20_and_erc721_token_transfers =
logs
|> Enum.filter(
&(&1.first_topic == unquote(TokenTransfer.constant()) or
@@ -27,7 +27,16 @@ defmodule Indexer.Transform.TokenTransfers do
|> combine_comments()
|> Enum.reduce(initial_acc, &do_parse/2)
- token_transfers = token_transfers_from_logs.token_transfers
+ erc1155_token_transfers =
+ logs
+ |> Enum.filter(fn log ->
+ log.first_topic == TokenTransfer.erc1155_single_transfer_signature() ||
+ log.first_topic == TokenTransfer.erc1155_batch_transfer_signature()
+ end)
+ |> Enum.reduce(initial_acc, &do_parse(&1, &2, :erc1155))
+
+ tokens = erc1155_token_transfers.tokens ++ erc20_and_erc721_token_transfers.tokens
+ token_transfers = erc1155_token_transfers.token_transfers ++ erc20_and_erc721_token_transfers.token_transfers
token_transfers
|> Enum.filter(fn token_transfer ->
@@ -36,17 +45,17 @@ defmodule Indexer.Transform.TokenTransfers do
|> Enum.map(fn token_transfer ->
token_transfer.token_contract_address_hash
end)
- |> Enum.dedup()
+ |> Enum.uniq()
|> Enum.each(&update_token/1)
- tokens_dedup = token_transfers_from_logs.tokens |> Enum.dedup()
+ tokens_uniq = tokens |> Enum.uniq()
- token_transfers_from_logs_dedup = %{
- tokens: tokens_dedup,
- token_transfers: token_transfers_from_logs.token_transfers
+ token_transfers_from_logs_uniq = %{
+ tokens: tokens_uniq,
+ token_transfers: token_transfers
}
- token_transfers_from_logs_dedup
+ token_transfers_from_logs_uniq
end
def parse_tx(txs, gold_token) do
@@ -74,6 +83,7 @@ defmodule Indexer.Transform.TokenTransfers do
to_address_hash: to_hash,
token_contract_address_hash: gold_token,
transaction_hash: tx.hash,
+ token_id: nil,
token_type: "ERC-20"
}
@@ -103,6 +113,7 @@ defmodule Indexer.Transform.TokenTransfers do
to_address_hash: to_hash,
token_contract_address_hash: gold_token,
transaction_hash: tx.transaction_hash,
+ token_id: nil,
token_type: "ERC-20"
}
@@ -156,6 +167,7 @@ defmodule Indexer.Transform.TokenTransfers do
to_address_hash: recipient,
token_contract_address_hash: currency,
transaction_hash: tx.transaction_hash,
+ token_id: nil,
token_type: "ERC-20"
}
@@ -169,8 +181,13 @@ defmodule Indexer.Transform.TokenTransfers do
end
end
- defp do_parse(log, %{tokens: tokens, token_transfers: token_transfers} = acc) do
- {token, token_transfer} = parse_params(log)
+ defp do_parse(log, %{tokens: tokens, token_transfers: token_transfers} = acc, type \\ :erc20_erc721) do
+ {token, token_transfer} =
+ if type != :erc1155 do
+ parse_params(log)
+ else
+ parse_erc1155_params(log)
+ end
%{
tokens: [token | tokens],
@@ -197,6 +214,7 @@ defmodule Indexer.Transform.TokenTransfers do
to_address_hash: truncate_address_hash(log.third_topic),
token_contract_address_hash: log.address_hash,
transaction_hash: log.transaction_hash,
+ token_id: nil,
token_type: "ERC-20"
}
@@ -234,7 +252,14 @@ defmodule Indexer.Transform.TokenTransfers do
end
# ERC-721 token transfer with info in data field instead of in log topics
- defp parse_params(%{second_topic: nil, third_topic: nil, fourth_topic: nil, data: data} = log)
+ defp parse_params(
+ %{
+ second_topic: nil,
+ third_topic: nil,
+ fourth_topic: nil,
+ data: data
+ } = log
+ )
when not is_nil(data) do
[from_address_hash, to_address_hash, token_id] = decode_data(data, [:address, :address, {:uint, 256}])
@@ -282,6 +307,62 @@ defmodule Indexer.Transform.TokenTransfers do
:ok
end
+ def parse_erc1155_params(
+ %{
+ first_topic: unquote(TokenTransfer.erc1155_batch_transfer_signature()),
+ third_topic: third_topic,
+ fourth_topic: fourth_topic,
+ data: data
+ } = log
+ ) do
+ [token_ids, values] = decode_data(data, [{:array, {:uint, 256}}, {:array, {:uint, 256}}])
+
+ token_transfer = %{
+ block_number: log.block_number,
+ block_hash: log.block_hash,
+ log_index: log.index,
+ from_address_hash: truncate_address_hash(third_topic),
+ to_address_hash: truncate_address_hash(fourth_topic),
+ token_contract_address_hash: log.address_hash,
+ transaction_hash: log.transaction_hash,
+ token_type: "ERC-1155",
+ token_ids: token_ids,
+ token_id: nil,
+ amounts: values
+ }
+
+ token = %{
+ contract_address_hash: log.address_hash,
+ type: "ERC-1155"
+ }
+
+ {token, token_transfer}
+ end
+
+ def parse_erc1155_params(%{third_topic: third_topic, fourth_topic: fourth_topic, data: data} = log) do
+ [token_id, value] = decode_data(data, [{:uint, 256}, {:uint, 256}])
+
+ token_transfer = %{
+ amount: value,
+ block_number: log.block_number,
+ block_hash: log.block_hash,
+ log_index: log.index,
+ from_address_hash: truncate_address_hash(third_topic),
+ to_address_hash: truncate_address_hash(fourth_topic),
+ token_contract_address_hash: log.address_hash,
+ transaction_hash: log.transaction_hash,
+ token_type: "ERC-1155",
+ token_id: token_id
+ }
+
+ token = %{
+ contract_address_hash: log.address_hash,
+ type: "ERC-1155"
+ }
+
+ {token, token_transfer}
+ end
+
defp truncate_address_hash(nil), do: "0x0000000000000000000000000000000000000000"
defp truncate_address_hash("0x000000000000000000000000" <> truncated_hash) do
diff --git a/apps/indexer/test/indexer/fetcher/token_balance_test.exs b/apps/indexer/test/indexer/fetcher/token_balance_test.exs
index 2ecee3eacd5f..739182174f9e 100644
--- a/apps/indexer/test/indexer/fetcher/token_balance_test.exs
+++ b/apps/indexer/test/indexer/fetcher/token_balance_test.exs
@@ -17,13 +17,13 @@ defmodule Indexer.Fetcher.TokenBalanceTest do
%Address.TokenBalance{
address_hash: %Hash{bytes: address_hash_bytes},
token_contract_address_hash: %Hash{bytes: token_contract_address_hash_bytes},
- block_number: block_number
+ block_number: _block_number
} = insert(:token_balance, block_number: 1_000, value_fetched_at: nil)
insert(:token_balance, value_fetched_at: DateTime.utc_now())
assert TokenBalance.init([], &[&1 | &2], nil) == [
- {address_hash_bytes, token_contract_address_hash_bytes, block_number, 0}
+ {address_hash_bytes, token_contract_address_hash_bytes, 1000, "ERC-20", nil, 0}
]
end
end
@@ -58,7 +58,7 @@ defmodule Indexer.Fetcher.TokenBalanceTest do
)
assert TokenBalance.run(
- [{address_hash_bytes, token_contract_address_hash_bytes, block_number, 0}],
+ [{address_hash_bytes, token_contract_address_hash_bytes, block_number, "ERC-20", nil, 0}],
nil
) == :ok
@@ -76,26 +76,12 @@ defmodule Indexer.Fetcher.TokenBalanceTest do
token_balance_a = insert(:token_balance, value_fetched_at: nil, value: nil)
token_balance_b = insert(:token_balance, value_fetched_at: nil, value: nil)
- expect(
- EthereumJSONRPC.Mox,
- :json_rpc,
- 1,
- fn [%{id: id, method: "eth_call", params: [%{data: _, to: _}, _]}], _options ->
- {:ok,
- [
- %{
- error: %{code: -32015, data: "Reverted 0x", message: "VM execution error."},
- id: id,
- jsonrpc: "2.0"
- }
- ]}
- end
- )
-
token_balances = [
{
token_balance_a.address_hash.bytes,
token_balance_a.token_contract_address_hash.bytes,
+ "ERC-20",
+ nil,
token_balance_a.block_number,
# this token balance must be ignored
max_retries
@@ -103,6 +89,8 @@ defmodule Indexer.Fetcher.TokenBalanceTest do
{
token_balance_b.address_hash.bytes,
token_balance_b.token_contract_address_hash.bytes,
+ "ERC-20",
+ nil,
token_balance_b.block_number,
# this token balance still have to be retried
max_retries - 2
@@ -136,8 +124,8 @@ defmodule Indexer.Fetcher.TokenBalanceTest do
assert TokenBalance.run(
[
- {address_hash_bytes, token_contract_address_hash_bytes, block_number, 0},
- {address_hash_bytes, token_contract_address_hash_bytes, block_number, 0}
+ {address_hash_bytes, token_contract_address_hash_bytes, block_number, "ERC-20", nil, 0},
+ {address_hash_bytes, token_contract_address_hash_bytes, block_number, "ERC-20", nil, 0}
],
nil
) == :ok
@@ -161,7 +149,9 @@ defmodule Indexer.Fetcher.TokenBalanceTest do
address_hash: nil,
block_number: nil,
token_contract_address_hash: to_string(token_balance.token_contract_address_hash),
+ token_id: nil,
value: nil,
+ token_type: nil,
value_fetched_at: nil
}
]
@@ -177,7 +167,9 @@ defmodule Indexer.Fetcher.TokenBalanceTest do
%{
address_hash: "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
block_number: 19999,
- token_contract_address_hash: to_string(contract.contract_address_hash)
+ token_contract_address_hash: to_string(contract.contract_address_hash),
+ token_type: "ERC-20",
+ token_id: nil
}
]
@@ -186,7 +178,32 @@ defmodule Indexer.Fetcher.TokenBalanceTest do
assert {:ok, _} = Explorer.Chain.hash_to_address(address_hash)
end
- test "import the token balances and return :ok when there are multiple balances for the same address on the batch" do
+ test "import the token balances and return :ok when there are multiple balances for the same address on the batch (ERC-20)" do
+ contract = insert(:token)
+ contract2 = insert(:token)
+ insert(:block, number: 19999)
+
+ token_balances_params = [
+ %{
+ address_hash: "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
+ block_number: 19999,
+ token_contract_address_hash: to_string(contract.contract_address_hash),
+ token_id: nil,
+ token_type: "ERC-20"
+ },
+ %{
+ address_hash: "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
+ block_number: 19999,
+ token_contract_address_hash: to_string(contract2.contract_address_hash),
+ token_id: nil,
+ token_type: "ERC-20"
+ }
+ ]
+
+ assert TokenBalance.import_token_balances(token_balances_params) == :ok
+ end
+
+ test "import the token balances and return :ok when there are multiple balances for the same address on the batch (ERC-1155)" do
contract = insert(:token)
contract2 = insert(:token)
insert(:block, number: 19999)
@@ -195,12 +212,16 @@ defmodule Indexer.Fetcher.TokenBalanceTest do
%{
address_hash: "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
block_number: 19999,
- token_contract_address_hash: to_string(contract.contract_address_hash)
+ token_contract_address_hash: to_string(contract.contract_address_hash),
+ token_id: 11,
+ token_type: "ERC-20"
},
%{
address_hash: "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
block_number: 19999,
- token_contract_address_hash: to_string(contract2.contract_address_hash)
+ token_contract_address_hash: to_string(contract2.contract_address_hash),
+ token_id: 11,
+ token_type: "ERC-1155"
}
]
diff --git a/apps/indexer/test/indexer/token_balances_test.exs b/apps/indexer/test/indexer/token_balances_test.exs
index 2a84a361bdcc..12cddcaa136b 100644
--- a/apps/indexer/test/indexer/token_balances_test.exs
+++ b/apps/indexer/test/indexer/token_balances_test.exs
@@ -26,10 +26,14 @@ defmodule Indexer.TokenBalancesTest do
token = insert(:token, contract_address: build(:contract_address))
address_hash_string = Hash.to_string(address.hash)
+ token_contract_address_hash = Hash.to_string(token.contract_address_hash)
+
data = %{
- token_contract_address_hash: Hash.to_string(token.contract_address_hash),
+ token_contract_address_hash: token_contract_address_hash,
address_hash: address_hash_string,
- block_number: 1_000
+ block_number: 1_000,
+ token_id: 11,
+ token_type: "ERC-20"
}
get_balance_from_blockchain()
@@ -38,13 +42,183 @@ defmodule Indexer.TokenBalancesTest do
assert %{
value: 1_000_000_000_000_000_000_000_000,
- token_contract_address_hash: _,
- address_hash: _,
+ token_contract_address_hash: ^token_contract_address_hash,
+ address_hash: ^address_hash_string,
block_number: 1_000,
value_fetched_at: _
} = List.first(result)
end
+ test "fetches balances of ERC-1155 tokens" do
+ address = insert(:address, hash: "0x609991ca0ae39bc4eaf2669976237296d40c2f31")
+
+ address_hash_string = Hash.to_string(address.hash)
+
+ token_contract_address_hash = "0xf7f79032fd395978acb7069c74d21e5a53206559"
+
+ contract_address = insert(:address, hash: token_contract_address_hash)
+
+ token = insert(:token, contract_address: contract_address)
+
+ data = [
+ %{
+ token_contract_address_hash: Hash.to_string(token.contract_address_hash),
+ address_hash: address_hash_string,
+ block_number: 1_000,
+ token_id: 5,
+ token_type: "ERC-1155"
+ }
+ ]
+
+ get_erc1155_balance_from_blockchain()
+
+ {:ok, result} = TokenBalances.fetch_token_balances_from_blockchain(data)
+
+ assert [
+ %{
+ value: 2,
+ token_contract_address_hash: ^token_contract_address_hash,
+ address_hash: ^address_hash_string,
+ block_number: 1_000,
+ value_fetched_at: _
+ }
+ ] = result
+ end
+
+ test "fetches multiple balances of tokens" do
+ address_1 = insert(:address, hash: "0xecba3c9ea993b0e0594e0b0a0d361a1f9596e310")
+ address_2 = insert(:address, hash: "0x609991ca0ae39bc4eaf2669976237296d40c2f31")
+ address_3 = insert(:address, hash: "0xf712a82dd8e2ac923299193e9d6daeda2d5a32fd")
+
+ address_1_hash_string = Hash.to_string(address_1.hash)
+ address_2_hash_string = Hash.to_string(address_2.hash)
+ address_3_hash_string = Hash.to_string(address_3.hash)
+
+ token_1_contract_address_hash = "0x57e93bb58268de818b42e3795c97bad58afcd3fe"
+ token_2_contract_address_hash = "0xe0d0b1dbbcf3dd5cac67edaf9243863fd70745da"
+ token_3_contract_address_hash = "0x22c1f6050e56d2876009903609a2cc3fef83b415"
+ token_4_contract_address_hash = "0xf7f79032fd395978acb7069c74d21e5a53206559"
+
+ contract_address_1 = insert(:address, hash: token_1_contract_address_hash)
+ contract_address_2 = insert(:address, hash: token_2_contract_address_hash)
+ contract_address_3 = insert(:address, hash: token_3_contract_address_hash)
+ contract_address_4 = insert(:address, hash: token_4_contract_address_hash)
+
+ token_1 = insert(:token, contract_address: contract_address_1)
+ token_2 = insert(:token, contract_address: contract_address_2)
+ token_3 = insert(:token, contract_address: contract_address_3)
+ token_4 = insert(:token, contract_address: contract_address_4)
+
+ data = [
+ %{
+ token_contract_address_hash: Hash.to_string(token_1.contract_address_hash),
+ address_hash: address_1_hash_string,
+ block_number: 1_000,
+ token_id: nil,
+ token_type: "ERC-20"
+ },
+ %{
+ token_contract_address_hash: Hash.to_string(token_2.contract_address_hash),
+ address_hash: address_2_hash_string,
+ block_number: 1_000,
+ token_id: nil,
+ token_type: "ERC-20"
+ },
+ %{
+ token_contract_address_hash: Hash.to_string(token_3.contract_address_hash),
+ address_hash: address_2_hash_string,
+ block_number: 1_000,
+ token_id: 42,
+ token_type: "ERC-721"
+ },
+ %{
+ token_contract_address_hash: Hash.to_string(token_4.contract_address_hash),
+ address_hash: address_2_hash_string,
+ block_number: 1_000,
+ token_id: 5,
+ token_type: "ERC-1155"
+ },
+ %{
+ token_contract_address_hash: Hash.to_string(token_2.contract_address_hash),
+ address_hash: Hash.to_string(token_2.contract_address_hash),
+ block_number: 1_000,
+ token_id: nil,
+ token_type: "ERC-20"
+ },
+ %{
+ token_contract_address_hash: Hash.to_string(token_2.contract_address_hash),
+ address_hash: address_3_hash_string,
+ block_number: 1_000,
+ token_id: nil,
+ token_type: "ERC-20"
+ },
+ %{
+ token_contract_address_hash: Hash.to_string(token_2.contract_address_hash),
+ address_hash: Hash.to_string(token_2.contract_address_hash),
+ block_number: 1_000,
+ token_id: nil,
+ token_type: "ERC-20"
+ }
+ ]
+
+ get_multiple_balances_from_blockchain()
+ get_erc1155_balance_from_blockchain()
+
+ {:ok, result} = TokenBalances.fetch_token_balances_from_blockchain(data)
+
+ assert [
+ %{
+ value: 1_000_000_000_000_000_000_000_000,
+ token_contract_address_hash: ^token_1_contract_address_hash,
+ address_hash: ^address_1_hash_string,
+ block_number: 1_000,
+ value_fetched_at: _
+ },
+ %{
+ value: 3_000_000_000_000_000_000_000_000_000,
+ token_contract_address_hash: ^token_2_contract_address_hash,
+ address_hash: ^address_2_hash_string,
+ block_number: 1_000,
+ value_fetched_at: _
+ },
+ %{
+ value: 1,
+ token_contract_address_hash: ^token_3_contract_address_hash,
+ address_hash: ^address_2_hash_string,
+ block_number: 1_000,
+ value_fetched_at: _
+ },
+ %{
+ value: 6_000_000_000_000_000_000_000_000_000,
+ token_contract_address_hash: ^token_2_contract_address_hash,
+ address_hash: ^token_2_contract_address_hash,
+ block_number: 1_000,
+ value_fetched_at: _
+ },
+ %{
+ value: 5_000_000_000_000_000_000_000_000_000,
+ token_contract_address_hash: ^token_2_contract_address_hash,
+ address_hash: ^address_3_hash_string,
+ block_number: 1_000,
+ value_fetched_at: _
+ },
+ %{
+ value: 6_000_000_000_000_000_000_000_000_000,
+ token_contract_address_hash: ^token_2_contract_address_hash,
+ address_hash: ^token_2_contract_address_hash,
+ block_number: 1_000,
+ value_fetched_at: _
+ },
+ %{
+ value: 2,
+ token_contract_address_hash: ^token_4_contract_address_hash,
+ address_hash: ^address_2_hash_string,
+ block_number: 1_000,
+ value_fetched_at: _
+ }
+ ] = result
+ end
+
test "ignores calls that gave errors to try fetch they again later" do
address = insert(:address, hash: "0x7113ffcb9c18a97da1b9cfc43e6cb44ed9165509")
token = insert(:token, contract_address: build(:contract_address))
@@ -54,7 +228,9 @@ defmodule Indexer.TokenBalancesTest do
address_hash: to_string(address.hash),
block_number: 1_000,
token_contract_address_hash: to_string(token.contract_address_hash),
- retries_count: 1
+ retries_count: 1,
+ token_id: 11,
+ token_type: "ERC-20"
}
]
@@ -128,12 +304,14 @@ defmodule Indexer.TokenBalancesTest do
token_balance_a = %{
token_contract_address_hash: Hash.to_string(token.contract_address_hash),
+ token_id: nil,
address_hash: address_hash_string,
block_number: 1_000
}
token_balance_b = %{
token_contract_address_hash: Hash.to_string(token.contract_address_hash),
+ token_id: nil,
address_hash: address_hash_string,
block_number: 1_001
}
@@ -162,6 +340,125 @@ defmodule Indexer.TokenBalancesTest do
)
end
+ defp get_erc1155_balance_from_blockchain() do
+ expect(
+ EthereumJSONRPC.Mox,
+ :json_rpc,
+ fn requests, _options ->
+ {:ok,
+ requests
+ |> Enum.map(fn
+ %{
+ id: id,
+ method: "eth_call",
+ params: [
+ %{
+ data:
+ "0x00fdd58e000000000000000000000000609991ca0ae39bc4eaf2669976237296d40c2f310000000000000000000000000000000000000000000000000000000000000005",
+ to: "0xf7f79032fd395978acb7069c74d21e5a53206559"
+ },
+ _
+ ]
+ } ->
+ %{
+ id: id,
+ jsonrpc: "2.0",
+ result: "0x0000000000000000000000000000000000000000000000000000000000000002"
+ }
+ end)
+ |> Enum.shuffle()}
+ end
+ )
+ end
+
+ defp get_multiple_balances_from_blockchain() do
+ expect(
+ EthereumJSONRPC.Mox,
+ :json_rpc,
+ fn requests, _options ->
+ {:ok,
+ requests
+ |> Enum.map(fn
+ %{id: id, method: "eth_call", params: [%{data: _, to: "0x57e93bb58268de818b42e3795c97bad58afcd3fe"}, _]} ->
+ %{
+ id: id,
+ jsonrpc: "2.0",
+ result: "0x00000000000000000000000000000000000000000000d3c21bcecceda1000000"
+ }
+
+ %{
+ id: id,
+ method: "eth_call",
+ params: [
+ %{
+ data: "0x70a08231000000000000000000000000609991ca0ae39bc4eaf2669976237296d40c2f31",
+ to: "0xe0d0b1dbbcf3dd5cac67edaf9243863fd70745da"
+ },
+ _
+ ]
+ } ->
+ %{
+ id: id,
+ jsonrpc: "2.0",
+ result: "0x000000000000000000000000000000000000000009b18ab5df7180b6b8000000"
+ }
+
+ %{
+ id: id,
+ method: "eth_call",
+ params: [
+ %{
+ data: "0x70a08231000000000000000000000000609991ca0ae39bc4eaf2669976237296d40c2f31",
+ to: "0x22c1f6050e56d2876009903609a2cc3fef83b415"
+ },
+ _
+ ]
+ } ->
+ %{
+ id: id,
+ jsonrpc: "2.0",
+ result: "0x0000000000000000000000000000000000000000000000000000000000000001"
+ }
+
+ %{
+ id: id,
+ method: "eth_call",
+ params: [
+ %{
+ data: "0x70a08231000000000000000000000000f712a82dd8e2ac923299193e9d6daeda2d5a32fd",
+ to: "0xe0d0b1dbbcf3dd5cac67edaf9243863fd70745da"
+ },
+ _
+ ]
+ } ->
+ %{
+ id: id,
+ jsonrpc: "2.0",
+ result: "0x00000000000000000000000000000000000000001027e72f1f12813088000000"
+ }
+
+ %{
+ id: id,
+ method: "eth_call",
+ params: [
+ %{
+ data: "0x70a08231000000000000000000000000e0d0b1dbbcf3dd5cac67edaf9243863fd70745da",
+ to: "0xe0d0b1dbbcf3dd5cac67edaf9243863fd70745da"
+ },
+ _
+ ]
+ } ->
+ %{
+ id: id,
+ jsonrpc: "2.0",
+ result: "0x00000000000000000000000000000000000000001363156bbee3016d70000000"
+ }
+ end)
+ |> Enum.shuffle()}
+ end
+ )
+ end
+
defp get_balance_from_blockchain_with_error() do
expect(
EthereumJSONRPC.Mox,
diff --git a/apps/indexer/test/indexer/transform/address_token_balances_test.exs b/apps/indexer/test/indexer/transform/address_token_balances_test.exs
index 1e22696557b1..82009ffab2f4 100644
--- a/apps/indexer/test/indexer/transform/address_token_balances_test.exs
+++ b/apps/indexer/test/indexer/transform/address_token_balances_test.exs
@@ -24,7 +24,9 @@ defmodule Indexer.Transform.AddressTokenBalancesTest do
block_number: block_number,
from_address_hash: from_address_hash,
to_address_hash: to_address_hash,
- token_contract_address_hash: token_contract_address_hash
+ token_contract_address_hash: token_contract_address_hash,
+ token_id: nil,
+ token_type: "ERC-20"
}
params_set = AddressTokenBalances.params_set(%{token_transfers_params: [token_transfer_params]})
@@ -46,7 +48,8 @@ defmodule Indexer.Transform.AddressTokenBalancesTest do
from_address_hash: from_address_hash,
to_address_hash: to_address_hash,
token_contract_address_hash: token_contract_address_hash,
- token_type: "ERC-721"
+ token_type: "ERC-721",
+ token_id: nil
}
params_set = AddressTokenBalances.params_set(%{token_transfers_params: [token_transfer_params]})
@@ -56,7 +59,9 @@ defmodule Indexer.Transform.AddressTokenBalancesTest do
%{
address_hash: "0x5b8410f67eb8040bb1cd1e8a4ff9d5f6ce678a15",
block_number: 1,
- token_contract_address_hash: "0xe18035bf8712672935fdb4e5e431b1a0183d2dfc"
+ token_contract_address_hash: "0xe18035bf8712672935fdb4e5e431b1a0183d2dfc",
+ token_id: nil,
+ token_type: "ERC-721"
}
])
end
diff --git a/apps/indexer/test/indexer/transform/token_transfers_test.exs b/apps/indexer/test/indexer/transform/token_transfers_test.exs
index 1d5f6880c3b6..425ca476966d 100644
--- a/apps/indexer/test/indexer/transform/token_transfers_test.exs
+++ b/apps/indexer/test/indexer/transform/token_transfers_test.exs
@@ -77,6 +77,7 @@ defmodule Indexer.Transform.TokenTransfersTest do
block_hash: log_3.block_hash
},
%{
+ token_id: nil,
amount: Decimal.new(17_000_000_000_000_000_000),
block_number: log_1.block_number,
log_index: log_1.index,
@@ -137,6 +138,83 @@ defmodule Indexer.Transform.TokenTransfersTest do
assert TokenTransfers.parse([log]) == expected
end
+ test "parses erc1155 token transfer" do
+ log = %{
+ address_hash: "0x58Ab73CB79c8275628E0213742a85B163fE0A9Fb",
+ block_number: 8_683_457,
+ data:
+ "0x1000000000000c520000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
+ first_topic: "0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62",
+ secon_topic: "0x0000000000000000000000009c978f4cfa1fe13406bcc05baf26a35716f881dd",
+ third_topic: "0x0000000000000000000000009c978f4cfa1fe13406bcc05baf26a35716f881dd",
+ fourth_topic: "0x0000000000000000000000009c978f4cfa1fe13406bcc05baf26a35716f881dd",
+ index: 2,
+ transaction_hash: "0x6d2dd62c178e55a13b65601f227c4ffdd8aa4e3bcb1f24731363b4f7619e92c8",
+ block_hash: "0x79594150677f083756a37eee7b97ed99ab071f502104332cb3835bac345711ca",
+ type: "mined"
+ }
+
+ assert TokenTransfers.parse([log]) == %{
+ token_transfers: [
+ %{
+ amount: 1,
+ block_hash: "0x79594150677f083756a37eee7b97ed99ab071f502104332cb3835bac345711ca",
+ block_number: 8_683_457,
+ from_address_hash: "0x9c978f4cfa1fe13406bcc05baf26a35716f881dd",
+ log_index: 2,
+ to_address_hash: "0x9c978f4cfa1fe13406bcc05baf26a35716f881dd",
+ token_contract_address_hash: "0x58Ab73CB79c8275628E0213742a85B163fE0A9Fb",
+ token_id:
+ 7_237_005_577_332_282_011_952_059_972_634_123_378_909_214_838_582_411_639_295_170_840_059_424_276_480,
+ token_type: "ERC-1155",
+ transaction_hash: "0x6d2dd62c178e55a13b65601f227c4ffdd8aa4e3bcb1f24731363b4f7619e92c8"
+ }
+ ],
+ tokens: [
+ %{
+ contract_address_hash: "0x58Ab73CB79c8275628E0213742a85B163fE0A9Fb",
+ type: "ERC-1155"
+ }
+ ]
+ }
+ end
+
+ test "parses erc1155 batch token transfer" do
+ log = %{
+ address_hash: "0x58Ab73CB79c8275628E0213742a85B163fE0A9Fb",
+ block_number: 8_683_457,
+ data:
+ "0x000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000001388",
+ first_topic: "0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb",
+ secon_topic: "0x0000000000000000000000006c943470780461b00783ad530a53913bd2c104d3",
+ third_topic: "0x0000000000000000000000006c943470780461b00783ad530a53913bd2c104d3",
+ fourth_topic: "0x0000000000000000000000006c943470780461b00783ad530a53913bd2c104d3",
+ index: 2,
+ transaction_hash: "0x6d2dd62c178e55a13b65601f227c4ffdd8aa4e3bcb1f24731363b4f7619e92c8",
+ block_hash: "0x79594150677f083756a37eee7b97ed99ab071f502104332cb3835bac345711ca",
+ type: "mined"
+ }
+
+ assert TokenTransfers.parse([log]) == %{
+ token_transfers: [
+ %{
+ block_hash: "0x79594150677f083756a37eee7b97ed99ab071f502104332cb3835bac345711ca",
+ block_number: 8_683_457,
+ from_address_hash: "0x6c943470780461b00783ad530a53913bd2c104d3",
+ log_index: 2,
+ to_address_hash: "0x6c943470780461b00783ad530a53913bd2c104d3",
+ token_contract_address_hash: "0x58Ab73CB79c8275628E0213742a85B163fE0A9Fb",
+ token_id: nil,
+ token_ids: [680_564_733_841_876_926_926_749_214_863_536_422_912],
+ token_type: "ERC-1155",
+ transaction_hash: "0x6d2dd62c178e55a13b65601f227c4ffdd8aa4e3bcb1f24731363b4f7619e92c8",
+ amounts: [5000]
+ }
+ ],
+ tokens: [%{contract_address_hash: "0x58Ab73CB79c8275628E0213742a85B163fE0A9Fb", type: "ERC-1155"}]
+ }
+ end
+
test "logs error with unrecognized token transfer format" do
log = %{
address_hash: "0x58Ab73CB79c8275628E0213742a85B163fE0A9Fb",
diff --git a/cloudbuild.yaml b/cloudbuild.yaml
index f0461cd0d6fa..d0ee6932a1c8 100644
--- a/cloudbuild.yaml
+++ b/cloudbuild.yaml
@@ -20,5 +20,5 @@ steps:
"--context", "dir://."]
waitFor: ["-"]
options:
- machineType: 'N1_HIGHCPU_8'
+ machineType: 'N1_HIGHCPU_32'
timeout: 1200s
diff --git a/config/config.exs b/config/config.exs
index b9964ce0c626..912e7d738991 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -31,7 +31,8 @@ config :logger,
{LoggerFileBackend, :indexer_token_balances},
{LoggerFileBackend, :token_instances},
{LoggerFileBackend, :reading_token_functions},
- {LoggerFileBackend, :pending_transactions_to_refetch}
+ {LoggerFileBackend, :pending_transactions_to_refetch},
+ {LoggerFileBackend, :empty_blocks_to_refetch}
]
config :logger_json, :console,
diff --git a/docker/Makefile b/docker/Makefile
index d229e99d8be2..b3643c8deef0 100644
--- a/docker/Makefile
+++ b/docker/Makefile
@@ -321,6 +321,9 @@ endif
ifdef CHAIN_ID
BLOCKSCOUT_CONTAINER_PARAMS += -e 'CHAIN_ID=$(CHAIN_ID)'
endif
+ifdef JSON_RPC
+ BLOCKSCOUT_CONTAINER_PARAMS += -e 'JSON_RPC=$(JSON_RPC)'
+endif
ifdef MAX_SIZE_UNLESS_HIDE_ARRAY
BLOCKSCOUT_CONTAINER_PARAMS += -e 'MAX_SIZE_UNLESS_HIDE_ARRAY=$(MAX_SIZE_UNLESS_HIDE_ARRAY)'
endif
@@ -331,8 +334,6 @@ ifdef DISPLAY_TOKEN_ICONS
BLOCKSCOUT_CONTAINER_PARAMS += -e 'DISPLAY_TOKEN_ICONS=$(DISPLAY_TOKEN_ICONS)'
endif
-BLOCKSCOUT_CONTAINNER_PARAMS += -e 'ECTO_USE_SSL=false'
-
HAS_BLOCKSCOUT_IMAGE := $(shell docker images | grep -sw ${DOCKER_IMAGE})
build:
diff --git a/mix.lock b/mix.lock
index fad4bc905526..28b129046186 100644
--- a/mix.lock
+++ b/mix.lock
@@ -35,7 +35,7 @@
"ecto_sql": {:hex, :ecto_sql, "3.7.0", "2fcaad4ab0c8d76a5afbef078162806adbe709c04160aca58400d5cbbe8eeac6", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.4.0 or ~> 0.5.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a26135dfa1d99bf87a928c464cfa25bba6535a4fe761eefa56077a4febc60f70"},
"elixir_make": {:hex, :elixir_make, "0.6.2", "7dffacd77dec4c37b39af867cedaabb0b59f6a871f89722c25b28fcd4bd70530", [:mix], [], "hexpm", "03e49eadda22526a7e5279d53321d1cced6552f344ba4e03e619063de75348d9"},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
- "ex_abi": {:hex, :ex_abi, "0.5.5", "678d69f8a74406cd8eaee1890cd35333c3fbcdbcb0ad50565fd0a4a82fd4ffa3", [:mix], [{:ex_keccak, "~> 0.2.0", [hex: :ex_keccak, repo: "hexpm", optional: false]}], "hexpm", "9a90d2b8c80f38dfcda110120d27fd2673ed79a8bb1c823019ecb8aa970884e3"},
+ "ex_abi": {:hex, :ex_abi, "0.5.7", "bb9695c1776a84ee4760794207f3c67c0412c28ba7c556790a6e887569d0667c", [:mix], [{:ex_keccak, "~> 0.2.2", [hex: :ex_keccak, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "64be72bcbab2e8d2c2d40df19c38d2bf4c29a3b2813e61dbf3d73e3f39004beb"},
"ex_cldr": {:hex, :ex_cldr, "2.19.1", "6bd81c826202d08420ebf7174306d277a8c4093c3c32c188ac9a636927f27c7e", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:cldr_utils, "~> 2.12", [hex: :cldr_utils, repo: "hexpm", optional: false]}, {:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:gettext, "~> 0.13", [hex: :gettext, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "5541261dd2915b7c9fb1408b1cfe9075657515e4b348ccb921e45e149dea6b11"},
"ex_cldr_currencies": {:hex, :ex_cldr_currencies, "2.8.0", "b2ecc94e9fa4b8ec07614830f4d6e811e5df5e7679c6d2be92f4fe4f31184913", [:mix], [{:ex_cldr, "~> 2.18", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "a39780667b73bfd3d2bd08e61981bca23a97912b86f3236042850ecb062f48eb"},
"ex_cldr_lists": {:hex, :ex_cldr_lists, "2.7.0", "86264f509ada404afc9d469faf58ad78a9cbba4b728776eb218ee1bf9a9396a2", [:mix], [{:ex_cldr_numbers, "~> 2.16", [hex: :ex_cldr_numbers, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "ec9f55af45aa628930900e84d09bab50ee61841ad974aeb8fd51f627a9685353"},
@@ -43,7 +43,7 @@
"ex_cldr_units": {:hex, :ex_cldr_units, "2.8.1", "f3fc6da7ff9795f70df16a9c8616df801dbc91831e2c4b994973d7199139b721", [:mix], [{:cldr_utils, "~> 2.6", [hex: :cldr_utils, repo: "hexpm", optional: false]}, {:ex_cldr, "~> 2.13", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:ex_cldr_lists, "~> 2.2", [hex: :ex_cldr_lists, repo: "hexpm", optional: false]}, {:ex_cldr_numbers, "~> 2.12", [hex: :ex_cldr_numbers, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "e2e0f5cebdd94dc75142a8cc2fcaec3ad6de5c1501adfe55369caeb4115e7b75"},
"ex_doc": {:hex, :ex_doc, "0.25.3", "3edf6a0d70a39d2eafde030b8895501b1c93692effcbd21347296c18e47618ce", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "9ebebc2169ec732a38e9e779fd0418c9189b3ca93f4a676c961be6c1527913f5"},
"ex_json_schema": {:hex, :ex_json_schema, "0.6.2", "de23d80478215987469c81688208fe0ff440ee0e0e6ae2268fcadbb2ff35df9d", [:mix], [], "hexpm", "2f25c57e919ffc5d6b02f2f130548284342dd6c3e99555ee0beeb9f2d2366a96"},
- "ex_keccak": {:hex, :ex_keccak, "0.2.0", "ca31d7fdae818380aa07952e6941c5de6b689cb8a68189fcce1e25d7bb0c4574", [:mix], [{:rustler, "~> 0.22.0", [hex: :rustler, repo: "hexpm", optional: false]}], "hexpm", "4bef13ce4dda45b15e2958e0ce5ff77502279778fb7e32cfa887dccb036f3bb2"},
+ "ex_keccak": {:hex, :ex_keccak, "0.2.2", "2ec7e8839de0938837b74fcbd5d7ef8ad97fa560d185de7ea2521d15ba47f1c3", [:mix], [{:rustler, "~> 0.22.0", [hex: :rustler, repo: "hexpm", optional: false]}], "hexpm", "c2f877d22cec98616a91b35ea198c3a45d0eefcd41d093e2485dc2b76ba3f6d3"},
"ex_machina": {:hex, :ex_machina, "2.7.0", "b792cc3127fd0680fecdb6299235b4727a4944a09ff0fa904cc639272cd92dc7", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "419aa7a39bde11894c87a615c4ecaa52d8f107bbdd81d810465186f783245bf8"},
"ex_rlp": {:hex, :ex_rlp, "0.5.3", "9055bddade545ee3e734aaad62c4b4d08211834da3beb43ae269b75785909e5e", [:mix], [], "hexpm", "a755a5f8f9f66079f3ecbe021536b949077fac0df963d9e59a20321bab28722d"},
"ex_utils": {:hex, :ex_utils, "0.1.7", "2c133e0bcdc49a858cf8dacf893308ebc05bc5fba501dc3d2935e65365ec0bf3", [:mix], [], "hexpm", "66d4fe75285948f2d1e69c2a5ddd651c398c813574f8d36a9eef11dc20356ef6"},
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 000000000000..48e341a0954d
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,3 @@
+{
+ "lockfileVersion": 1
+}