+ <% address_name -> %>
+ <%= if contract?(@address) do %>
+
+
+ <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html",
+ text: gettext("The name found in the source code of the Contract.") %>
+ <%= gettext("Contract Name") %>
+
+
+ <%= short_contract_name(address_name, 30) %>
+
+
+ <% else %>
+
+
+ <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html",
+ text: gettext("The name of the validator.") %>
+ <%= gettext("Validator Name") %>
+
+
+ <%= short_contract_name(address_name, 30) %>
+
+
+ <% end %>
+ <% true -> %>
+ <% end %>
+
+ <% from_address_hash = from_address_hash(@address) %>
+ <%= if contract?(@address) do %>
+
+
+ <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html",
+ text: gettext("Transactions and address of creation.") %>
+ <%= gettext("Creator") %>
+
+ <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html",
+ text: gettext("Number of transactions related to this address.") %>
+ <%= gettext("Transactions") %>
+
+ <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html",
+ text: gettext("Gas used by the address.") %>
+ <%= gettext("Gas Used") %>
+
+
+
+ <%= if @address.gas_used do %>
+ <%= Number.Delimit.number_to_delimited(@address.gas_used, precision: 0) %>
+ <% else %>
+ <%= render BlockScoutWeb.CommonComponentsView, "_loading_spinner.html", loading_text: gettext("Fetching gas used...") %>
+ <% end %>
-
-
- <% from_address_hash = from_address_hash(@address) %>
-
- <%= if contract?(@address) do %>
-
- <%= if from_address_hash do %>
- <%= gettext "Created by" %> <%= link(
- trimmed_hash(from_address_hash(@address)),
- to: address_path(@conn, :show, from_address_hash(@address))
- ) %>
-
- <%= gettext "at" %>
-
+
+
+
+ <%= if @address.fetched_coin_balance_block_number do %>
+
+
+ <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html",
+ text: gettext("Block number in which the address was updated.") %>
+ <%= gettext("Last Balance Update") %>
+
"
title="If using a flat file for contract verification, you can use the POA Solidity flattener or the Truffle flattener">
- 2. Verification through Sourcify.
+ 2. Verification using Standard input JSON file.
+ 3. Verification through Sourcify.
a) if smart-contract already verified on Sourcify, it will automatically fetch the data from the repo
- b) otherwise you will be asked to upload source files and JSON metadata file(s).
+ b) otherwise you will be asked to upload source files and JSON metadata file(s).
+ 4. Verification of Vyper contract.
+
The compiler version is specified in pragma solidity X.X.X. Use the compiler version rather than the nightly build. If using the Solidity compiler, run solc —version to check.
+
+
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_constructor_args.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_constructor_args.html.eex
new file mode 100644
index 000000000000..5fa778585ddd
--- /dev/null
+++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_constructor_args.html.eex
@@ -0,0 +1,10 @@
+
+
+ <%= label @f, :constructor_arguments, gettext("ABI-encoded Constructor Arguments (if required by the contract)") %>
+
Add arguments in ABI hex encoded form. Constructor arguments are written right to left, and will be found at the end of the input created bytecode. They may also be parsed here.
+
+
\ No newline at end of file
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_contract_address_field.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_contract_address_field.html.eex
new file mode 100644
index 000000000000..61775e6488d8
--- /dev/null
+++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_contract_address_field.html.eex
@@ -0,0 +1,9 @@
+
+
+
+
+ readonly="">
+
+
The 0x address supplied on contract creation.
+
+
\ No newline at end of file
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_contract_name_field.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_contract_name_field.html.eex
new file mode 100644
index 000000000000..924c008df352
--- /dev/null
+++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_contract_name_field.html.eex
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_fetch_constructor_args.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_fetch_constructor_args.html.eex
new file mode 100644
index 000000000000..5702cadb6314
--- /dev/null
+++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_fetch_constructor_args.html.eex
@@ -0,0 +1,20 @@
+
\ No newline at end of file
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_include_nightly_builds_field.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_include_nightly_builds_field.html.eex
new file mode 100644
index 000000000000..275de35cbec9
--- /dev/null
+++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_include_nightly_builds_field.html.eex
@@ -0,0 +1,21 @@
+
Must match the name specified in the code. For example, in contract MyContract {..}MyContract is the contract name.
-
-
+ <%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_contract_name_field.html", f: f, tooltip: "Must match the name specified in the code. For example, in contract MyContract {..}MyContract is the contract name.", contract_name_value: contract_name_value %>
-
The compiler version is specified in pragma solidity X.X.X. Use the compiler version rather than the nightly build. If using the Solidity compiler, run solc —version to check.
Add arguments in ABI hex encoded form. Constructor arguments are written right to left, and will be found at the end of the input created bytecode. They may also be parsed here.
Add arguments in ABI hex encoded form. Constructor arguments are written right to left, and will be found at the end of the input created bytecode. They may also be parsed here.
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_token/overview.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_token/overview.html.eex
new file mode 100644
index 000000000000..11da76cfc8df
--- /dev/null
+++ b/apps/block_scout_web/lib/block_scout_web/templates/address_token/overview.html.eex
@@ -0,0 +1,73 @@
+<% native_coin = gettext("ETH") %>
+
+<% wei_value = if @address.fetched_coin_balance, do: @address.fetched_coin_balance.value %>
+<% raw_usd_value =
+ case @exchange_rate.usd_value do
+ %Decimal{} ->
+ if wei_value do
+ %Wei{value: Decimal.new(wei_value)}
+ |> Wei.to(:ether)
+ |> Decimal.mult(@exchange_rate.usd_value)
+ else
+ Decimal.new(0)
+ end
+ _ -> Decimal.new(0)
+ end
+%>
+<% data_usd_exchange_rate =
+ unless AddressView.empty_exchange_rate?(@exchange_rate) do
+ "data-usd-exchange-rate='#{@exchange_rate.usd_value}' data-raw-usd-value='#{raw_usd_value}'"
+ end
+%>
+<% native_coin_balance_token = AddressView.balance(@address)
+%>
+<% native_coin_balance_usd =
+ if AddressView.empty_exchange_rate?(@exchange_rate) do
+ nil
+ else
+ "
+ "
+ end
+%>
+<% native_coin_balance =
+ if native_coin_balance_usd do
+ native_coin_balance_usd <> " | " <> native_coin_balance_token
+ else
+ native_coin_balance_token
+ end
+%>
+
+ <%= render BlockScoutWeb.AddressTokenView, "overview_item.html",
+ title: gettext("Net Worth"),
+ tooltip: gettext("Shows total assets held in the address"),
+ value: "N/A",
+ data_test: "address-tokens-panel-net-worth",
+ classes: ["fs-14"]
+ %>
+ <%= render BlockScoutWeb.AddressTokenView, "overview_item.html",
+ title: "#{native_coin} #{gettext("Balance")}",
+ tooltip: "#{gettext("Shows the current")} #{native_coin} #{gettext("balance of the address")}",
+ value: raw(native_coin_balance),
+ data_test: "address-tokens-panel-native-worth",
+ classes: ["fs-14"]
+ %>
+ <%= render BlockScoutWeb.AddressTokenView, "overview_item.html",
+ title: gettext("Tokens"),
+ tooltip: gettext("Shows the tokens held in the address (includes ERC-20, ERC-721 and ERC-1155)."),
+ value: "N/A",
+ data_test: "address-tokens-panel-tokens-worth",
+ classes: ["fs-14"]
+ %>
+ <%= render BlockScoutWeb.AddressTokenView, "overview_item.html",
+ title: gettext("CRC Worth"),
+ tooltip: gettext("Shows the total CRC balance in the address."),
+ value: "0 CRC",
+ data_test: "address-tokens-panel-crc-total-worth",
+ data_test_container: "address-tokens-panel-crc-total-worth-container",
+ classes: ["fs-14"],
+ container_classes: ["d-none"]
+ %>
+
\ No newline at end of file
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_token/overview_item.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_token/overview_item.html.eex
new file mode 100644
index 000000000000..c0382545d898
--- /dev/null
+++ b/apps/block_scout_web/lib/block_scout_web/templates/address_token/overview_item.html.eex
@@ -0,0 +1,14 @@
+
" data-test="<%= if assigns[:data_test_container], do: @data_test_container %>" style="padding: 10px;">
+
\ No newline at end of file
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex
index 34f721df1521..c3d6794c57f0 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex
@@ -1,57 +1,78 @@
-<%= if Decimal.cmp(@circles_total_balance, 0) == :gt do %>
-
- <%= for {token_balance, bridged_token} <- sort_by_usd_value_and_name(@token_balances) do %>
+ <%= for {token_balance, bridged_token, token} <- sort_by_usd_value_and_name(@token_balances) do %>
@@ -171,7 +177,7 @@
text: gettext("The total gas amount used in the block and its percentage of gas filled in the block.") %>
<%= gettext("Gas Used") %>
-
<%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html",
- text: gettext("xDai burned from transactions included in the block (Base fee (per unit of gas) * Gas Used).") %>
+ text: gettext("Ether") <> " " <> gettext("burned from transactions included in the block (Base fee (per unit of gas) * Gas Used).") %>
<%= gettext("Burnt Fees") %>
<% end %>
<%= if BlockScoutWeb.BlockView.show_reward?(@block.rewards) do %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/chain/show.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/chain/show.html.eex
index 36dc7a4a4f79..4b29c1e77e34 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/chain/show.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/chain/show.html.eex
@@ -37,9 +37,9 @@
<%= if Enum.member?(market_chart_config, :price) do %>
+ <%= render BlockScoutWeb.CommonComponentsView, "_info.html" %>
+ <%= gettext("Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.") %>
+
\ No newline at end of file
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_channel_disconnected_message.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_channel_disconnected_message.html.eex
new file mode 100644
index 000000000000..4dd8e27d5140
--- /dev/null
+++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_channel_disconnected_message.html.eex
@@ -0,0 +1,5 @@
+
+
+ <%= @text %>
+
+
\ No newline at end of file
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_csv_export_button.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_csv_export_button.html.eex
new file mode 100644
index 000000000000..9fd19339cfbe
--- /dev/null
+++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_csv_export_button.html.eex
@@ -0,0 +1,7 @@
+
\ No newline at end of file
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_loading_spinner.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_loading_spinner.html.eex
new file mode 100644
index 000000000000..d501012d1bb1
--- /dev/null
+++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_loading_spinner.html.eex
@@ -0,0 +1,6 @@
+
+
+
+
+<%= if assigns[:loading_text], do: @loading_text %>
+
\ No newline at end of file
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_rap_pagination_container.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_rap_pagination_container.html.eex
new file mode 100644
index 000000000000..efd1ce63d485
--- /dev/null
+++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_rap_pagination_container.html.eex
@@ -0,0 +1,15 @@
+
position-top<% end %> <%= if assigns[:position] == "bottom" do %>position-bottom<% end %>'
+data-pagination-container>
+
+
+
+
+
<%= gettext("Go to")%>
+
+
+
+<%= if assigns[:showing_limit] do %>
+
(<%= gettext("Only the first")%> <%= assigns[:showing_limit] |> BlockScoutWeb.Cldr.Number.to_string! %> <%= gettext("elements are displayed")%>)
+
+<% end %>
\ No newline at end of file
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_table-loader.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_table-loader.html.eex
index 339567f4809a..ecaf52d6906e 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_table-loader.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_table-loader.html.eex
@@ -1,170 +1,9 @@
+<%= for _r <- 1..5 do %>
-
-
-
-
-
-
-
-
-
- <%= if assigns[:total_supply] do %>
- <%= if balance_percentage_enabled?(@total_supply) do %>
-
-
-
- <% end %>
- <% end %>
-
-
-
- <%= if assigns[:is_eth_bridged_tokens_table] || assigns[:is_tokens_table] do %>
-
-
-
-
-
-
- <% end %>
- <%= if assigns[:is_bsc_bridged_tokens_table] do %>
-
-
-
- <% end %>
-
-
-
-
-
-
-
-
-
-
-
- <%= if assigns[:total_supply] do %>
- <%= if balance_percentage_enabled?(@total_supply) do %>
-
-
-
- <% end %>
- <% end %>
-
-
-
- <%= if assigns[:is_eth_bridged_tokens_table] || assigns[:is_tokens_table] do %>
-
-
-
-
-
-
- <% end %>
- <%= if assigns[:is_bsc_bridged_tokens_table] do %>
-
-
-
- <% end %>
-
-
-
-
-
-
-
-
-
-
-
- <%= if assigns[:total_supply] do %>
- <%= if balance_percentage_enabled?(@total_supply) do %>
-
-
-
- <% end %>
- <% end %>
-
-
-
- <%= if assigns[:is_eth_bridged_tokens_table] || assigns[:is_tokens_table] do %>
-
-
-
-
-
-
- <% end %>
- <%= if assigns[:is_bsc_bridged_tokens_table] do %>
-
-
-
- <% end %>
-
-
-
-
-
-
-
-
-
-
-
- <%= if assigns[:total_supply] do %>
- <%= if balance_percentage_enabled?(@total_supply) do %>
-
-
-
- <% end %>
- <% end %>
-
-
-
- <%= if assigns[:is_eth_bridged_tokens_table] || assigns[:is_tokens_table] do %>
-
-
-
-
-
-
- <% end %>
- <%= if assigns[:is_bsc_bridged_tokens_table] do %>
-
-
-
- <% end %>
-
-
-
-
-
-
-
-
-
-
-
- <%= if assigns[:total_supply] do %>
- <%= if balance_percentage_enabled?(@total_supply) do %>
-
-
-
- <% end %>
- <% end %>
-
-
-
- <%= if assigns[:is_eth_bridged_tokens_table] || assigns[:is_tokens_table] do %>
-
-
-
-
-
-
- <% end %>
- <%= if assigns[:is_bsc_bridged_tokens_table] do %>
+ <%= for _c <- 1..@columns_num do %>
<% end %>
+<% end %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex
new file mode 100644
index 000000000000..d79aff67ed0a
--- /dev/null
+++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex
@@ -0,0 +1,12 @@
+<%= case @type do %>
+ <% :token_burning -> %>
+ <%= gettext("Token Burning") %>
+ <% :token_minting -> %>
+ <%= gettext("Token Minting") %>
+ <% :token_spawning -> %>
+ <%= gettext("Token Creation") %>
+ <% :token_transfer -> %>
+ <%= gettext("Token Transfer") %>
+ <% _ -> %>
+ <%= gettext("Token Transfer") %>
+<% end %>
\ No newline at end of file
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/csv_export/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/csv_export/index.html.eex
new file mode 100644
index 000000000000..0060e01d31c2
--- /dev/null
+++ b/apps/block_scout_web/lib/block_scout_web/templates/csv_export/index.html.eex
@@ -0,0 +1,41 @@
+<%=
+ for status <- ["error", "warning", "success", "question"] do
+ render BlockScoutWeb.CommonComponentsView, "_modal_status.html", status: status
+ end
+ %>
+">
+
+
+
+
<%= gettext "Export Data" %>
+
+
+
Export <%= type_display_name(@type) %> for address <%= link(
+ @address_hash_string,
+ to: address_path(@conn, :show, @address_hash_string)
+ ) %> to CSV file
+
+
+
+ from to
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/internal_transaction/_tile.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/internal_transaction/_tile.html.eex
index 2350284a37cc..b0e1c31d9958 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/internal_transaction/_tile.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/internal_transaction/_tile.html.eex
@@ -1,4 +1,5 @@
-
<%= link to: webapp_url(@conn), class: "navbar-brand", "data-test": "header_logo" do %>
-
+ <%= if logo() do %>
+
+ <% end %>
<%= if logo_text() do %>
- <%= logo_text() %>
+ "> <%= logo_text() %>
<% end %>
<% end %>
@@ -178,10 +184,7 @@
<% end %>
<%= if not Explorer.Chain.finished_indexing?() do %>
-
-
-
-
+ <%= render BlockScoutWeb.CommonComponentsView, "_loading_spinner.html" %>
<%= gettext("- We're indexing this chain right now. Some of the counts may be inaccurate.") %>
\ No newline at end of file
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/search/_name_td.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/search/_name_td.html.eex
new file mode 100644
index 000000000000..7a98114236c0
--- /dev/null
+++ b/apps/block_scout_web/lib/block_scout_web/templates/search/_name_td.html.eex
@@ -0,0 +1,7 @@
+
\ 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 @@
[<%= 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/_functions.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex
index 24ac14818781..ee6675a928a5 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex
@@ -1,17 +1,12 @@
<% minimal_proxy_template = Chain.get_minimal_proxy_template(@address.hash) %>
<% metadata_for_verification = minimal_proxy_template || Chain.get_address_verified_twin_contract(@address.hash).verified_contract %>
-<%= unless BlockScoutWeb.AddressView.smart_contract_verified?(@address) do %>
+<% smart_contract_verified = BlockScoutWeb.AddressView.smart_contract_verified?(@address) %>
+<%= unless smart_contract_verified do %>
<%= if metadata_for_verification do %>
<%= if minimal_proxy_template do %>
<%= render BlockScoutWeb.CommonComponentsView, "_minimal_proxy_pattern.html", address_hash: metadata_for_verification.address_hash, conn: @conn %>
<% else %>
- <% path =
- if Application.get_env(:explorer, Explorer.ThirdPartyIntegrations.Sourcify)[:enabled] do
- address_verify_contract_path(@conn, :new, @address.hash)
- else
- address_verify_contract_via_flattened_code_path(@conn, :new, @address.hash)
- end
- %>
+ <% path = address_verify_contract_path(@conn, :new, @address.hash) %>
<%= render BlockScoutWeb.CommonComponentsView, "_info.html" %> <%= gettext("Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB") %> <%= link(
metadata_for_verification.address_hash,
@@ -23,6 +18,9 @@
<% end %>
<% end %>
<% end %>
+<%= if smart_contract_verified && @address.smart_contract.is_changed_bytecode do %>
+ <%= render BlockScoutWeb.CommonComponentsView, "_changed_bytecode_warning.html" %>
+<% end %>
<%= if @action == "write" or (@read_functions_required_wallet && @read_functions_required_wallet != []) do %>
<%= render BlockScoutWeb.SmartContractView, "_connect_container.html" %>
<% end %>
@@ -63,26 +61,26 @@
<%= if queryable?(function["inputs"]) do %>
<%= for input <- function["inputs"] do %>
-
+
<%= if int?(input["type"]) do %>
- px;"/>
-
+
-
+
106
108
1018
-
10
+
10
<% else %>
- " />
<% end %>
@@ -90,15 +88,18 @@
<% end %>
<%= if Helper.payable?(function) do %>
-
-<% end %>
\ No newline at end of file
+<% end %>
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 @@
-
-
\ No newline at end of file
+
+
\ 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 e0a331136129..0e16239094eb 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
@@ -5,16 +5,20 @@
<%= @index %>
-
+
<%= if System.get_env("DISPLAY_TOKEN_ICONS") === "true" do %>
<% 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,
to: token_path(BlockScoutWeb.Endpoint, :show, @token.contract_address_hash),
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..9b5df1bc16a5
--- /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) %>
+" alt="" onerror="this.style.visibility='hidden'"/>
\ No newline at end of file
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/read_contract/_metatags.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/contract/_metatags.html.eex
similarity index 100%
rename from apps/block_scout_web/lib/block_scout_web/templates/tokens/read_contract/_metatags.html.eex
rename to apps/block_scout_web/lib/block_scout_web/templates/tokens/contract/_metatags.html.eex
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/contract/index.html.eex
similarity index 71%
rename from apps/block_scout_web/lib/block_scout_web/templates/tokens/read_contract/index.html.eex
rename to apps/block_scout_web/lib/block_scout_web/templates/tokens/contract/index.html.eex
index 811035aac2d0..fde314766433 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/contract/index.html.eex
@@ -13,14 +13,10 @@
+ <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html",
+ text: gettext("Number of digits that come after the decimal place when displaying token value") %>
+ <%= gettext("Decimals") %>
+
+
+ <%= @token.decimals %>
+
+
+ <% end %>
+
+
+ <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html",
+ text: gettext("Type of the token standard") %>
+ <%= gettext("Token type") %>
+
<%= link(
gettext("Token Transfers"),
@@ -9,22 +11,43 @@
gettext("Token Holders"),
class: "card-tab #{tab_status("token-holders", @conn.request_path)}",
"data-test": "token_holders_tab",
- to: AccessHelpers.get_path(@conn, :token_holder_path, :index, Address.checksum(@token.contract_address_hash))
+ to: AccessHelpers.get_path(@conn, :token_holder_path, :index, address_hash)
)
%>
<%= if display_inventory?(@token) do %>
<%= link(
gettext("Inventory"),
class: "card-tab #{tab_status("inventory", @conn.request_path)}",
- to: AccessHelpers.get_path(@conn, :token_inventory_path, :index, Address.checksum(@token.contract_address_hash))
+ to: AccessHelpers.get_path(@conn, :token_inventory_path, :index, address_hash)
)
%>
<% end %>
<%= if smart_contract_with_read_only_functions?(@token) do %>
<%= link(
gettext("Read Contract"),
- to: AccessHelpers.get_path(@conn, :token_read_contract_path, :index, Address.checksum(@token.contract_address_hash)),
+ to: AccessHelpers.get_path(@conn, :token_read_contract_path, :index, address_hash),
class: "card-tab #{tab_status("read-contract", @conn.request_path)}")
%>
<% end %>
+ <%= if smart_contract_with_write_functions?(@token) do %>
+ <%= link(
+ gettext("Write Contract"),
+ to: AccessHelpers.get_path(@conn, :token_write_contract_path, :index, address_hash),
+ class: "card-tab #{tab_status("write-contract", @conn.request_path)}")
+ %>
+ <% end %>
+ <%= if is_proxy do %>
+ <%= link(
+ gettext("Read Proxy"),
+ to: AccessHelpers.get_path(@conn, :token_read_proxy_path, :index, address_hash),
+ class: "card-tab #{tab_status("read-proxy", @conn.request_path)}")
+ %>
+ <% end %>
+ <%= if smart_contract_with_write_functions?(@token) && is_proxy do %>
+ <%= link(
+ gettext("Write Proxy"),
+ to: AccessHelpers.get_path(@conn, :token_write_proxy_path, :index, address_hash),
+ class: "card-tab #{tab_status("write-proxy", @conn.request_path)}")
+ %>
+ <% end %>
\ No newline at end of file
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 dc2e48c01467..d1d151ffe2b5 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 @@
" 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 %>
@@ -25,12 +27,12 @@
<%= name %>
<%= type %>
- <%= case BlockScoutWeb.ABIEncodedValueView.value_html(type, value) do %>
+ <%= case BlockScoutWeb.ABIEncodedValueView.value_html(type, value, true) do %>
<% :error -> %>
<%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html",
- text: gettext("Amount of xDai burned for this transaction. Equals Block Base Fee per Gas * Gas Used.") %>
+ text: gettext("Amount of") <> " " <> gettext("Ether") <> " " <> gettext("burned for this transaction. Equals Block Base Fee per Gas * Gas Used.") %>
<%= gettext "Transaction Burnt Fee" %>
diff --git a/apps/block_scout_web/lib/block_scout_web/views/abi_encoded_value_view.ex b/apps/block_scout_web/lib/block_scout_web/views/abi_encoded_value_view.ex
index aad184898aef..0f09ce2302fc 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/abi_encoded_value_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/abi_encoded_value_view.ex
@@ -12,10 +12,12 @@ defmodule BlockScoutWeb.ABIEncodedValueView do
require Logger
- def value_html(type, value) do
+ def value_html(type, value, no_links \\ false)
+
+ def value_html(type, value, no_links) do
decoded_type = FunctionSelector.decode_type(type)
- do_value_html(decoded_type, value)
+ do_value_html(decoded_type, value, no_links)
rescue
exception ->
Logger.warn(fn ->
@@ -78,20 +80,20 @@ defmodule BlockScoutWeb.ABIEncodedValueView do
to_string(value)
end
- defp do_value_html(type, value, depth \\ 0)
+ defp do_value_html(type, value, no_links, depth \\ 0)
- defp do_value_html({:bytes, _}, value, depth) do
- do_value_html(:bytes, value, depth)
+ defp do_value_html({:bytes, _}, value, no_links, depth) do
+ do_value_html(:bytes, value, no_links, depth)
end
- defp do_value_html({:array, type, _}, value, depth) do
- do_value_html({:array, type}, value, depth)
+ defp do_value_html({:array, type, _}, value, no_links, depth) do
+ do_value_html({:array, type}, value, no_links, depth)
end
- defp do_value_html({:array, type}, value, depth) do
+ defp do_value_html({:array, type}, value, no_links, depth) do
values =
Enum.map(value, fn inner_value ->
- do_value_html(type, inner_value, depth + 1)
+ do_value_html(type, inner_value, no_links, depth + 1)
end)
spacing = String.duplicate(" ", depth * 2)
@@ -100,44 +102,48 @@ defmodule BlockScoutWeb.ABIEncodedValueView do
~E|<%= spacing %>[<%= "\n" %><%= delimited %><%= "\n" %><%= spacing %>]|
end
- defp do_value_html({:tuple, types}, values, _) do
+ defp do_value_html({:tuple, types}, values, no_links, _) do
values_list =
values
|> Tuple.to_list()
|> Enum.with_index()
|> Enum.map(fn {value, i} ->
- do_value_html(Enum.at(types, i), value)
+ do_value_html(Enum.at(types, i), value, no_links)
end)
delimited = Enum.intersperse(values_list, ",")
~E|(<%= delimited %>)|
end
- defp do_value_html(type, value, depth) do
+ defp do_value_html(type, value, no_links, depth) do
spacing = String.duplicate(" ", depth * 2)
- ~E|<%= spacing %><%=base_value_html(type, value)%>|
- [spacing, base_value_html(type, value)]
+ ~E|<%= spacing %><%=base_value_html(type, value, no_links)%>|
+ [spacing, base_value_html(type, value, no_links)]
end
- defp base_value_html(_, {:dynamic, value}) do
+ defp base_value_html(_, {:dynamic, value}, _no_links) do
~E|<%= hex(value) %>|
end
- defp base_value_html(:address, value) do
- address = hex(value)
+ defp base_value_html(:address, value, no_links) do
+ if no_links do
+ base_value_html(:address_text, value, no_links)
+ else
+ address = hex(value)
- ~E|<%= address %>|
+ ~E|<%= address %>|
+ end
end
- defp base_value_html(:address_text, value) do
+ defp base_value_html(:address_text, value, _no_links) do
~E|<%= hex(value) %>|
end
- defp base_value_html(:bytes, value) do
+ defp base_value_html(:bytes, value, _no_links) do
~E|<%= hex(value) %>|
end
- defp base_value_html(_, value), do: HTML.html_escape(value)
+ defp base_value_html(_, value, _no_links), do: HTML.html_escape(value)
defp hex(value), do: "0x" <> Base.encode16(value, case: :lower)
end
diff --git a/apps/block_scout_web/lib/block_scout_web/views/access_helpers.ex b/apps/block_scout_web/lib/block_scout_web/views/access_helpers.ex
index 86202a3da750..c89ac104353d 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/access_helpers.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/access_helpers.ex
@@ -3,17 +3,14 @@ defmodule BlockScoutWeb.AccessHelpers do
Helpers to restrict access to some pages filtering by address
"""
+ import Phoenix.Controller
+
+ alias BlockScoutWeb.API.APILogger
+ alias BlockScoutWeb.API.RPC.RPCView
alias BlockScoutWeb.WebRouter.Helpers
alias Plug.Conn
- defp get_restricted_key(%Phoenix.Socket{}) do
- nil
- end
-
- defp get_restricted_key(conn) do
- conn_with_params = Conn.fetch_query_params(conn)
- conn_with_params.query_params["key"]
- end
+ alias RemoteIp
def restricted_access?(address_hash, params) do
restricted_list_var = Application.get_env(:block_scout_web, :restricted_list)
@@ -59,4 +56,95 @@ defmodule BlockScoutWeb.AccessHelpers do
apply(Helpers, path, full_args)
end
+
+ def handle_rate_limit_deny(conn) do
+ APILogger.message("API rate limit reached")
+
+ conn
+ |> Conn.put_status(429)
+ |> put_view(RPCView)
+ |> render(:error, %{error: "429 Too Many Requests"})
+ |> Conn.halt()
+ end
+
+ def check_rate_limit(conn) do
+ if Mix.env() == :test do
+ :ok
+ else
+ global_api_rate_limit = Application.get_env(:block_scout_web, :api_rate_limit)[:global_limit]
+ api_rate_limit_by_key = Application.get_env(:block_scout_web, :api_rate_limit)[:api_rate_limit_by_key]
+ api_rate_limit_by_ip = Application.get_env(:block_scout_web, :api_rate_limit)[:limit_by_ip]
+ static_api_key = Application.get_env(:block_scout_web, :api_rate_limit)[:static_api_key]
+
+ remote_ip = conn.remote_ip
+ remote_ip_from_headers = RemoteIp.from(conn.resp_headers)
+ ip = remote_ip_from_headers || remote_ip
+ ip_string = to_string(:inet_parse.ntoa(ip))
+
+ cond do
+ conn.query_params && Map.has_key?(conn.query_params, "apikey") &&
+ Map.get(conn.query_params, "apikey") == static_api_key ->
+ rate_limit_by_key(static_api_key, api_rate_limit_by_key)
+
+ Enum.member?(api_rate_limit_whitelisted_ips(), ip_string) ->
+ rate_limit_by_ip(ip_string, api_rate_limit_by_ip)
+
+ true ->
+ global_rate_limit(global_api_rate_limit)
+ end
+ end
+ end
+
+ defp rate_limit_by_key(api_key, api_rate_limit_by_key) do
+ case Hammer.check_rate("api-#{api_key}", 1_000, api_rate_limit_by_key) do
+ {:allow, _count} ->
+ :ok
+
+ {:deny, _limit} ->
+ :rate_limit_reached
+ end
+ end
+
+ defp rate_limit_by_ip(ip_string, api_rate_limit_by_ip) do
+ case Hammer.check_rate("api-#{ip_string}", 1_000, api_rate_limit_by_ip) do
+ {:allow, _count} ->
+ :ok
+
+ {:deny, _limit} ->
+ :rate_limit_reached
+ end
+ end
+
+ defp global_rate_limit(global_api_rate_limit) do
+ case Hammer.check_rate("api", 1_000, global_api_rate_limit) do
+ {:allow, _count} ->
+ :ok
+
+ {:deny, _limit} ->
+ :rate_limit_reached
+ end
+ end
+
+ defp get_restricted_key(%Phoenix.Socket{}) do
+ nil
+ end
+
+ defp get_restricted_key(conn) do
+ conn_with_params = Conn.fetch_query_params(conn)
+ conn_with_params.query_params["key"]
+ end
+
+ defp api_rate_limit_whitelisted_ips do
+ with api_rate_limit_object <-
+ :block_scout_web
+ |> Application.get_env(:api_rate_limit),
+ {:ok, whitelisted_ips_string} <-
+ api_rate_limit_object &&
+ api_rate_limit_object
+ |> Keyword.fetch(:whitelisted_ips) do
+ if whitelisted_ips_string, do: String.split(whitelisted_ips_string, ","), else: []
+ else
+ _ -> []
+ end
+ end
end
diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_common_fields_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_common_fields_view.ex
new file mode 100644
index 000000000000..933980752dfb
--- /dev/null
+++ b/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_common_fields_view.ex
@@ -0,0 +1,3 @@
+defmodule BlockScoutWeb.AddressContractVerificationCommonFieldsView do
+ use BlockScoutWeb, :view
+end
diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_via_standard_json_input_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_via_standard_json_input_view.ex
new file mode 100644
index 000000000000..5e794923f2e7
--- /dev/null
+++ b/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_via_standard_json_input_view.ex
@@ -0,0 +1,4 @@
+defmodule BlockScoutWeb.AddressContractVerificationViaStandardJsonInputView do
+ use BlockScoutWeb, :view
+ alias Explorer.Chain
+end
diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_vyper_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_vyper_view.ex
new file mode 100644
index 000000000000..ea99d78b7183
--- /dev/null
+++ b/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_vyper_view.ex
@@ -0,0 +1,5 @@
+defmodule BlockScoutWeb.AddressContractVerificationVyperView do
+ use BlockScoutWeb, :view
+
+ alias Explorer.Chain
+end
diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_internal_transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_internal_transaction_view.ex
index 7ce8667b6db5..092e22363dc9 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/address_internal_transaction_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/address_internal_transaction_view.ex
@@ -2,6 +2,7 @@ defmodule BlockScoutWeb.AddressInternalTransactionView do
use BlockScoutWeb, :view
alias BlockScoutWeb.AccessHelpers
+ alias Explorer.Chain.Address
def format_current_filter(filter) do
case filter do
diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_logs_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_logs_view.ex
index 3cdfaf1f9b31..577661ab8161 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/address_logs_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/address_logs_view.ex
@@ -1,7 +1,7 @@
defmodule BlockScoutWeb.AddressLogsView do
use BlockScoutWeb, :view
- alias Explorer.Chain.Log
+ alias Explorer.Chain.{Address, Log}
def decode(log, transaction) do
Log.decode(log, transaction)
diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_token_balance_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_token_balance_view.ex
index 46518454e653..252a9fb82ace 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/address_token_balance_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/address_token_balance_view.ex
@@ -1,7 +1,9 @@
defmodule BlockScoutWeb.AddressTokenBalanceView do
use BlockScoutWeb, :view
+ alias BlockScoutWeb.AccessHelpers
alias Explorer.Chain
+ alias Explorer.Chain.Address
alias Explorer.Counters.AddressTokenUsdSum
def tokens_count_title(token_balances) do
@@ -9,7 +11,7 @@ defmodule BlockScoutWeb.AddressTokenBalanceView do
end
def filter_by_type(token_balances, type) do
- Enum.filter(token_balances, fn {token_balance, _} -> token_balance.token.type == type end)
+ Enum.filter(token_balances, fn {token_balance, _, _} -> token_balance.token.type == type end)
end
@doc """
@@ -27,12 +29,12 @@ defmodule BlockScoutWeb.AddressTokenBalanceView do
"""
def sort_by_usd_value_and_name(token_balances) do
token_balances
- |> Enum.sort(fn {token_balance1, _}, {token_balance2, _} ->
+ |> Enum.sort(fn {token_balance1, _, token1}, {token_balance2, _, token2} ->
usd_value1 = token_balance1.token.usd_value
usd_value2 = token_balance2.token.usd_value
- token_name1 = token_balance1.token.name
- token_name2 = token_balance2.token.name
+ token_name1 = token1.name
+ token_name2 = token2.name
sort_by_name = sort_2_tokens_by_name(token_name1, token_name2)
diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_token_transfer_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_token_transfer_view.ex
index 965feca80fdf..ca82caa32d65 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/address_token_transfer_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/address_token_transfer_view.ex
@@ -2,6 +2,7 @@ defmodule BlockScoutWeb.AddressTokenTransferView do
use BlockScoutWeb, :view
alias BlockScoutWeb.AccessHelpers
+ alias Explorer.Chain.Address
def format_current_filter(filter) do
case filter do
diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_token_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_token_view.ex
index bf41494b3842..83cdf79a7182 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/address_token_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/address_token_view.ex
@@ -1,3 +1,7 @@
defmodule BlockScoutWeb.AddressTokenView do
use BlockScoutWeb, :view
+
+ alias BlockScoutWeb.{AddressView, ChainView}
+ alias Explorer.Chain
+ alias Explorer.Chain.{Address, Wei}
end
diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_transaction_view.ex
index 2985d42b2c50..64ba591cec3a 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/address_transaction_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/address_transaction_view.ex
@@ -2,6 +2,7 @@ defmodule BlockScoutWeb.AddressTransactionView do
use BlockScoutWeb, :view
alias BlockScoutWeb.AccessHelpers
+ alias Explorer.Chain.Address
def format_current_filter(filter) do
case filter do
diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/eth_rpc_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/eth_rpc/view.ex
similarity index 67%
rename from apps/block_scout_web/lib/block_scout_web/views/api/rpc/eth_rpc_view.ex
rename to apps/block_scout_web/lib/block_scout_web/views/api/eth_rpc/view.ex
index f9fae4a36af0..16f85b303ff2 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/eth_rpc_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/api/eth_rpc/view.ex
@@ -1,4 +1,7 @@
-defmodule BlockScoutWeb.API.RPC.EthRPCView do
+defmodule BlockScoutWeb.API.EthRPC.View do
+ @moduledoc """
+ Views for /eth-rpc API endpoints
+ """
use BlockScoutWeb, :view
defstruct [:result, :id, :error]
@@ -47,8 +50,8 @@ defmodule BlockScoutWeb.API.RPC.EthRPCView do
end)
end
- defimpl Poison.Encoder, for: BlockScoutWeb.API.RPC.EthRPCView do
- def encode(%BlockScoutWeb.API.RPC.EthRPCView{result: result, id: id, error: error}, _options) when is_nil(error) do
+ defimpl Poison.Encoder, for: BlockScoutWeb.API.EthRPC.View do
+ def encode(%BlockScoutWeb.API.EthRPC.View{result: result, id: id, error: error}, _options) when is_nil(error) do
result = Poison.encode!(result)
"""
@@ -56,15 +59,15 @@ defmodule BlockScoutWeb.API.RPC.EthRPCView do
"""
end
- def encode(%BlockScoutWeb.API.RPC.EthRPCView{id: id, error: error}, _options) do
+ def encode(%BlockScoutWeb.API.EthRPC.View{id: id, error: error}, _options) do
"""
{"jsonrpc":"2.0","error": "#{error}","id": #{id}}
"""
end
end
- defimpl Jason.Encoder, for: BlockScoutWeb.API.RPC.EthRPCView do
- def encode(%BlockScoutWeb.API.RPC.EthRPCView{result: result, id: id, error: error}, _options) when is_nil(error) do
+ defimpl Jason.Encoder, for: BlockScoutWeb.API.EthRPC.View do
+ def encode(%BlockScoutWeb.API.EthRPC.View{result: result, id: id, error: error}, _options) when is_nil(error) do
result = Jason.encode!(result)
"""
@@ -72,7 +75,7 @@ defmodule BlockScoutWeb.API.RPC.EthRPCView do
"""
end
- def encode(%BlockScoutWeb.API.RPC.EthRPCView{id: id, error: error}, _options) do
+ def encode(%BlockScoutWeb.API.EthRPC.View{id: id, error: error}, _options) do
"""
{"jsonrpc":"2.0","error": "#{error}","id": #{id}}
"""
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 30f9803259e9..03743acc3225 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
@@ -1,7 +1,8 @@
defmodule BlockScoutWeb.API.RPC.AddressView do
use BlockScoutWeb, :view
- alias BlockScoutWeb.API.RPC.{EthRPCView, RPCView}
+ alias BlockScoutWeb.API.EthRPC.View, as: EthRPCView
+ alias BlockScoutWeb.API.RPC.RPCView
def render("listaccounts.json", %{accounts: accounts}) do
accounts = Enum.map(accounts, &prepare_account/1)
@@ -172,6 +173,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/api/rpc/block_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/block_view.ex
index ce2f04fb49ab..70d5f0bd0441 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/block_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/block_view.ex
@@ -1,7 +1,8 @@
defmodule BlockScoutWeb.API.RPC.BlockView do
use BlockScoutWeb, :view
- alias BlockScoutWeb.API.RPC.{EthRPCView, RPCView}
+ alias BlockScoutWeb.API.EthRPC.View, as: EthRPCView
+ alias BlockScoutWeb.API.RPC.RPCView
alias Explorer.Chain.{Hash, Wei}
alias Explorer.EthRPC, as: EthRPC
diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex
index 1ffa68a8bf5a..91ae8f7a482f 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex
@@ -30,6 +30,10 @@ defmodule BlockScoutWeb.API.RPC.ContractView do
RPCView.render("show.json", data: prepare_source_code_contract(contract))
end
+ def render("show.json", %{result: result}) do
+ RPCView.render("show.json", data: result)
+ end
+
defp prepare_source_code_contract(nil) do
%{
"Address" => "",
diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/eth_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/eth_view.ex
deleted file mode 100644
index 739f3ac8a7ba..000000000000
--- a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/eth_view.ex
+++ /dev/null
@@ -1,13 +0,0 @@
-defmodule BlockScoutWeb.API.RPC.EthView do
- use BlockScoutWeb, :view
-
- alias BlockScoutWeb.API.RPC.EthRPCView
-
- def render("responses.json", %{responses: responses}) do
- EthRPCView.render("responses.json", %{responses: responses})
- end
-
- def render("response.json", %{response: response}) do
- EthRPCView.render("response.json", %{response: response})
- end
-end
diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/token_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/token_view.ex
index 9ccab7c9d163..449759827cf9 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/token_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/token_view.ex
@@ -2,6 +2,7 @@ defmodule BlockScoutWeb.API.RPC.TokenView do
use BlockScoutWeb, :view
alias BlockScoutWeb.API.RPC.RPCView
+ alias BlockScoutWeb.BridgedTokensView
def render("gettoken.json", %{token: token}) do
RPCView.render("show.json", data: prepare_token(token))
@@ -12,6 +13,11 @@ defmodule BlockScoutWeb.API.RPC.TokenView do
RPCView.render("show.json", data: data)
end
+ def render("bridgedtokenlist.json", %{bridged_tokens: bridged_tokens}) do
+ data = Enum.map(bridged_tokens, &prepare_bridged_token/1)
+ RPCView.render("show.json", data: data)
+ end
+
def render("error.json", assigns) do
RPCView.render("error.json", assigns)
end
@@ -34,4 +40,21 @@ defmodule BlockScoutWeb.API.RPC.TokenView do
"value" => token_holder.value
}
end
+
+ defp prepare_bridged_token([token, bridged_token]) do
+ total_supply = divide_decimals(token.total_supply, token.decimals)
+ usd_value = BridgedTokensView.bridged_token_usd_cap(bridged_token, token)
+
+ %{
+ "foreignChainId" => bridged_token.foreign_chain_id,
+ "foreignTokenContractAddressHash" => bridged_token.foreign_token_contract_address_hash,
+ "homeContractAddressHash" => token.contract_address_hash,
+ "homeDecimals" => token.decimals,
+ "homeHolderCount" => if(token.holder_count, do: to_string(token.holder_count), else: "0"),
+ "homeName" => token.name,
+ "homeSymbol" => token.symbol,
+ "homeTotalSupply" => total_supply,
+ "homeUsdValue" => usd_value
+ }
+ end
end
diff --git a/apps/block_scout_web/lib/block_scout_web/views/block_view.ex b/apps/block_scout_web/lib/block_scout_web/views/block_view.ex
index ca0b69964ebd..dc1aaa949242 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/block_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/block_view.ex
@@ -18,7 +18,7 @@ defmodule BlockScoutWeb.BlockView do
|> Kernel.||(0)
|> BlockScoutWeb.Cldr.Number.to_string!()
- unit_text = gettext("MicroEvmos")
+ unit_text = gettext("ntevmos")
"#{average} #{unit_text}"
end
diff --git a/apps/block_scout_web/lib/block_scout_web/views/bridged_tokens_view.ex b/apps/block_scout_web/lib/block_scout_web/views/bridged_tokens_view.ex
index 2753e101d5f3..f59617ff410c 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/bridged_tokens_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/bridged_tokens_view.ex
@@ -3,7 +3,7 @@ defmodule BlockScoutWeb.BridgedTokensView do
alias BlockScoutWeb.ChainView
alias Explorer.Chain
- alias Explorer.Chain.{Address, Token}
+ alias Explorer.Chain.{Address, BridgedToken, Token}
@owl_token_amb "0x0905Ab807F8FD040255F0cF8fa14756c1D824931"
@owl_token_omni "0x750eCf8c11867Ce5Dbc556592c5bb1E0C6d16538"
@@ -31,6 +31,10 @@ defmodule BlockScoutWeb.BridgedTokensView do
"
OWL token bridged through OmniBridge without support of burnOWL method. It is not recommended to use.
"
end
+ @doc """
+ Calculates capitalization of the bridged token in USD.
+ """
+ @spec bridged_token_usd_cap(%BridgedToken{}, %Token{}) :: any()
def bridged_token_usd_cap(bridged_token, token) do
if bridged_token.custom_cap do
bridged_token.custom_cap
diff --git a/apps/block_scout_web/lib/block_scout_web/views/captcha_view.ex b/apps/block_scout_web/lib/block_scout_web/views/captcha_view.ex
new file mode 100644
index 000000000000..8da20e685f13
--- /dev/null
+++ b/apps/block_scout_web/lib/block_scout_web/views/captcha_view.ex
@@ -0,0 +1,3 @@
+defmodule BlockScoutWeb.CaptchaView do
+ use BlockScoutWeb, :view
+end
diff --git a/apps/block_scout_web/lib/block_scout_web/views/chain_view.ex b/apps/block_scout_web/lib/block_scout_web/views/chain_view.ex
index a0c1b797207a..0f747e9e5fb9 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/chain_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/chain_view.ex
@@ -1,7 +1,8 @@
defmodule BlockScoutWeb.ChainView do
use BlockScoutWeb, :view
- import Number.Currency, only: [number_to_currency: 1, number_to_currency: 2]
+ require Decimal
+ import Number.Currency, only: [number_to_currency: 2]
alias BlockScoutWeb.LayoutView
alias Explorer.Chain.Supply.TokenBridge
@@ -35,29 +36,52 @@ defmodule BlockScoutWeb.ChainView do
if System.get_env("SUPPLY_MODULE") === "TokenBridge", do: true, else: false
end
+ def format_usd_value(nil), do: ""
+
def format_usd_value(value) do
- "#{format_currency_value(value)} USD"
+ if Decimal.is_decimal(value) do
+ "#{format_currency_value(Decimal.to_float(value))} USD"
+ else
+ "#{format_currency_value(value)} USD"
+ end
+ end
+
+ def format_currency_value(value, symbol \\ "$")
+
+ def format_currency_value(nil, _symbol), do: ""
+
+ def format_currency_value(%Decimal{} = value, symbol) do
+ value
+ |> Decimal.to_float()
+ |> format_currency_value(symbol)
end
- defp format_currency_value(value, symbol \\ "$")
+ def format_currency_value(value, _symbol) when not is_float(value) do
+ "N/A"
+ end
- defp format_currency_value(value, symbol) when value < 0 do
- "#{symbol}0.000000"
+ def format_currency_value(value, symbol) when is_float(value) and value < 0 do
+ "#{symbol}0.00"
end
- defp format_currency_value(value, symbol) when value < 0.000001 do
+ def format_currency_value(value, symbol) when is_float(value) and value < 0.000001 do
"Less than #{symbol}0.000001"
end
- defp format_currency_value(value, _symbol) when value < 1 do
- "#{number_to_currency(value, precision: 6)}"
+ def format_currency_value(value, symbol) when is_float(value) and value < 1 do
+ "#{number_to_currency(value, unit: symbol, precision: 6)}"
+ end
+
+ def format_currency_value(value, symbol) when is_float(value) and value < 100_000 do
+ "#{number_to_currency(value, unit: symbol)}"
end
- defp format_currency_value(value, _symbol) when value < 100_000 do
- "#{number_to_currency(value)}"
+ def format_currency_value(value, _symbol) when value >= 1_000_000 and value <= 999_000_000 do
+ {:ok, value} = Cldr.Number.to_string(value, format: :short, currency: :USD, fractional_digits: 2)
+ value
end
- defp format_currency_value(value, _symbol) do
- "#{number_to_currency(value, precision: 0)}"
+ def format_currency_value(value, symbol) when is_float(value) do
+ "#{number_to_currency(value, unit: symbol, precision: 0)}"
end
end
diff --git a/apps/block_scout_web/lib/block_scout_web/views/csv_export.ex b/apps/block_scout_web/lib/block_scout_web/views/csv_export.ex
new file mode 100644
index 000000000000..07b9564594ad
--- /dev/null
+++ b/apps/block_scout_web/lib/block_scout_web/views/csv_export.ex
@@ -0,0 +1,44 @@
+defmodule BlockScoutWeb.CsvExportView do
+ use BlockScoutWeb, :view
+
+ alias Explorer.Chain
+ alias Explorer.Chain.Address
+
+ defp type_display_name(type) do
+ case type do
+ "internal-transactions" -> "internal transactions"
+ "transactions" -> "transactions"
+ "token-transfers" -> "token transfers"
+ "logs" -> "logs"
+ _ -> ""
+ end
+ end
+
+ defp type_download_path(type) do
+ case type do
+ "internal-transactions" -> :internal_transactions_csv
+ "transactions" -> :transactions_csv
+ "token-transfers" -> :token_transfers_csv
+ "logs" -> :logs_csv
+ _ -> ""
+ end
+ end
+
+ defp address_checksum(address_hash_string) do
+ with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string) do
+ address_hash
+ |> Address.checksum()
+ end
+ end
+
+ defp default_period_start do
+ DateTime.utc_now()
+ |> Timex.shift(months: -1)
+ |> Timex.format!("{YYYY}-{0M}-{0D}")
+ end
+
+ defp default_period_end do
+ DateTime.utc_now()
+ |> Timex.format!("{YYYY}-{0M}-{0D}")
+ end
+end
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..e847ec967329 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, 24) == :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/layout_view.ex b/apps/block_scout_web/lib/block_scout_web/views/layout_view.ex
index 07ddc1214d57..f8920b724fb3 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/layout_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/layout_view.ex
@@ -19,7 +19,7 @@ defmodule BlockScoutWeb.LayoutView do
test_net?: true
},
%{
- title: "xDai",
+ title: "Gnosis Chain",
url: "https://blockscout.com/xdai/mainnet"
},
%{
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..67524a2d0671 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,10 +2,12 @@ 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
+ @tab " "
+
def queryable?(inputs) when not is_nil(inputs), do: Enum.any?(inputs)
def queryable?(inputs) when is_nil(inputs), do: false
@@ -26,6 +28,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 +124,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
@@ -127,11 +138,22 @@ defmodule BlockScoutWeb.SmartContractView do
name
end
- def values_only(value, type, components) when is_list(value) do
+ def values_only(value, type, components, nested_index \\ 1)
+
+ def values_only(value, type, components, nested_index) when is_list(value) do
max_size = Enum.at(Tuple.to_list(Application.get_env(:block_scout_web, :max_size_to_show_array_as_is)), 0)
is_too_long = length(value) > max_size
cond do
+ String.ends_with?(type, "[][]") ->
+ values =
+ value
+ |> Enum.map(&values_only(&1, String.slice(type, 0..-3), components, nested_index + 1))
+ |> Enum.map(&(String.duplicate(@tab, nested_index) <> &1))
+ |> Enum.join(",")
+
+ wrap_output(render_nested_array_value(values, nested_index - 1), is_too_long)
+
String.starts_with?(type, "tuple") ->
tuple_types =
type
@@ -170,7 +192,7 @@ defmodule BlockScoutWeb.SmartContractView do
end
end
- def values_only(value, type, _components) when is_tuple(value) do
+ def values_only(value, type, _components, _nested_index) when is_tuple(value) do
values =
value
|> tuple_to_array(type)
@@ -181,22 +203,22 @@ defmodule BlockScoutWeb.SmartContractView do
wrap_output(values, tuple_size(value) > max_size)
end
- def values_only(value, type, _components) when type in [:address, "address", "address payable"] do
+ def values_only(value, type, _components, _nested_index) when type in [:address, "address", "address payable"] do
{:ok, address} = HashAddress.cast(value)
wrap_output(to_string(address))
end
- def values_only(value, "string", _components), do: wrap_output(value)
+ def values_only(value, "string", _components, _nested_index), do: wrap_output(value)
- def values_only(value, :string, _components), do: wrap_output(value)
+ def values_only(value, :string, _components, _nested_index), do: wrap_output(value)
- def values_only(value, :bytes, _components), do: wrap_output(value)
+ def values_only(value, :bytes, _components, _nested_index), do: wrap_output(value)
- def values_only(value, "bool", _components), do: wrap_output(to_string(value))
+ def values_only(value, "bool", _components, _nested_index), do: wrap_output(to_string(value))
- def values_only(value, :bool, _components), do: wrap_output(to_string(value))
+ def values_only(value, :bool, _components, _nested_index), do: wrap_output(to_string(value))
- def values_only(value, _type, _components) do
+ def values_only(value, _type, _components, _nested_index) do
wrap_output(binary_to_utf_string(value))
end
@@ -204,7 +226,7 @@ defmodule BlockScoutWeb.SmartContractView do
if is_too_long do
"Click to view#{value}"
else
- "
#{value}
"
+ "#{value}"
end
end
@@ -304,11 +326,11 @@ defmodule BlockScoutWeb.SmartContractView do
end
defp render_type_value(type, value, type) do
- "
(#{type}) : #{value}
"
+ "
(#{type}) : #{value}
"
end
defp render_type_value(type, value, name) do
- "
#{name} (#{type}) : #{value}
"
+ "
#{name} (#{type}) : #{value}
"
end
defp render_array_type_value(type, values, name) do
@@ -317,6 +339,12 @@ defmodule BlockScoutWeb.SmartContractView do
render_type_value(type, value_to_display, name)
end
+ defp render_nested_array_value(values, nested_index) do
+ value_to_display = "[" <> values <> "" <> String.duplicate(@tab, nested_index) <> "]"
+
+ value_to_display
+ end
+
defp render_array_value(values) do
value_to_display = "[" <> values <> "]"
@@ -337,4 +365,25 @@ 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
+
+ def not_last_element?(length, index), do: length > 1 and index < length - 1
end
diff --git a/apps/block_scout_web/lib/block_scout_web/views/stakes_view.ex b/apps/block_scout_web/lib/block_scout_web/views/stakes_view.ex
index e8441515455e..d11c854a472a 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/stakes_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/stakes_view.ex
@@ -2,8 +2,4 @@ defmodule BlockScoutWeb.StakesView do
use BlockScoutWeb, :view
import BlockScoutWeb.StakesHelpers
alias Explorer.Chain
-
- def render("styles.html", _) do
- ~E()
- end
end
diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/read_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/contract_view.ex
similarity index 67%
rename from apps/block_scout_web/lib/block_scout_web/views/tokens/read_contract_view.ex
rename to apps/block_scout_web/lib/block_scout_web/views/tokens/contract_view.ex
index d8ab6f288dac..0b90df1ba981 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/tokens/read_contract_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/contract_view.ex
@@ -1,4 +1,4 @@
-defmodule BlockScoutWeb.Tokens.ReadContractView do
+defmodule BlockScoutWeb.Tokens.ContractView do
use BlockScoutWeb, :view
alias BlockScoutWeb.Tokens.OverviewView
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 7652fa14ad1e..7b8d3276d63a 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..4d61ff571bf1 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
@@ -11,6 +12,7 @@ defmodule BlockScoutWeb.Tokens.Instance.OverviewView do
import BlockScoutWeb.APIDocsView, only: [blockscout_url: 1, blockscout_url: 2]
@tabs ["token-transfers", "metadata"]
+ @stub_image "/images/controller.svg"
def token_name?(%Token{name: nil}), do: false
def token_name?(%Token{name: _}), do: true
@@ -21,7 +23,7 @@ defmodule BlockScoutWeb.Tokens.Instance.OverviewView do
def total_supply?(%Token{total_supply: nil}), do: false
def total_supply?(%Token{total_supply: _}), do: true
- def media_src(nil), do: "/images/controller.svg"
+ def media_src(nil), do: @stub_image
def media_src(instance) do
result =
@@ -149,10 +151,17 @@ defmodule BlockScoutWeb.Tokens.Instance.OverviewView do
|> tab_name()
end
+ defp retrieve_image(image) when is_nil(image), do: @stub_image
+
defp retrieve_image(image) when is_map(image) do
image["description"]
end
+ defp retrieve_image(image) when is_list(image) do
+ image_url = image |> Enum.at(0)
+ retrieve_image(image_url)
+ end
+
defp retrieve_image(image_url) do
image_url
|> URI.encode()
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..254fb7a7fd44 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
@@ -3,7 +3,7 @@ defmodule BlockScoutWeb.Tokens.OverviewView do
alias Explorer.{Chain, CustomContractsHelpers}
alias Explorer.Chain.{Address, SmartContract, Token}
- alias Explorer.SmartContract.Helper
+ alias Explorer.SmartContract.{Helper, Writer}
alias BlockScoutWeb.{AccessHelpers, CurrencyHelpers, LayoutView}
@@ -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?(
@@ -54,11 +55,28 @@ defmodule BlockScoutWeb.Tokens.OverviewView do
def smart_contract_with_read_only_functions?(%Token{contract_address: %Address{smart_contract: nil}}), do: false
+ def smart_contract_is_proxy?(%Token{contract_address: %Address{smart_contract: %SmartContract{}} = address}) do
+ Chain.proxy_contract?(address.hash, address.smart_contract.abi)
+ end
+
+ def smart_contract_is_proxy?(%Token{contract_address: %Address{smart_contract: nil}}), do: false
+
+ def smart_contract_with_write_functions?(%Token{
+ contract_address: %Address{smart_contract: %SmartContract{}} = address
+ }) do
+ Enum.any?(
+ address.smart_contract.abi,
+ &Writer.write_function?(&1)
+ )
+ end
+
+ def smart_contract_with_write_functions?(%Token{contract_address: %Address{smart_contract: nil}}), do: false
+
@doc """
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 84856e8af9d0..ab2be5a31e04 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.
"""
@@ -553,7 +550,7 @@ defmodule BlockScoutWeb.TransactionView do
case Integer.parse(string_value) do
{integer, ""} -> integer
- _ -> 0
+ _ -> 2040
end
end
@@ -568,4 +565,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/views/wei_helpers.ex b/apps/block_scout_web/lib/block_scout_web/views/wei_helpers.ex
index fc0d434470d6..05d228295213 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/wei_helpers.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/wei_helpers.ex
@@ -29,13 +29,13 @@ defmodule BlockScoutWeb.WeiHelpers do
## Examples
iex> format_wei_value(%Wei{value: Decimal.new(1)}, :wei)
- "1 PicoEvmos"
+ "1 atevmos"
iex> format_wei_value(%Wei{value: Decimal.new(1, 10, 12)}, :gwei)
- "10,000 MicroEvmos"
+ "10,000 ntevmos"
iex> format_wei_value(%Wei{value: Decimal.new(1, 10, 21)}, :ether)
- "10,000 Evmos"
+ "10,000 tevmos"
# With formatting options
@@ -73,7 +73,7 @@ defmodule BlockScoutWeb.WeiHelpers do
end
end
- defp display_unit(:wei), do: gettext("PicoEvmos")
- defp display_unit(:gwei), do: gettext("MicroEvmos")
- defp display_unit(:ether), do: gettext("Evmos")
+ defp display_unit(:wei), do: gettext("atevmos")
+ defp display_unit(:gwei), do: gettext("ntevmos")
+ defp display_unit(:ether), do: gettext("tevmos")
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 2f199f2de8bb..401432c10b43 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
@@ -132,22 +132,6 @@ defmodule BlockScoutWeb.WebRouter do
as: :verify_contract
)
- # if Application.get_env(:explorer, Explorer.ThirdPartyIntegrations.Sourcify)[:enabled] do
- # resources(
- # "/contract_verifications",
- # AddressContractVerificationController,
- # only: [:new],
- # as: :verify_contract
- # )
- # else
- # resources(
- # "/contract_verifications",
- # AddressContractVerificationViaFlattenedCodeController,
- # only: [:new],
- # as: :verify_contract
- # )
- # end
-
resources(
"/verify-via-flattened-code",
AddressContractVerificationViaFlattenedCodeController,
@@ -162,6 +146,20 @@ defmodule BlockScoutWeb.WebRouter do
as: :verify_contract_via_json
)
+ resources(
+ "/verify-via-standard-json-input",
+ AddressContractVerificationViaStandardJsonInputController,
+ only: [:new],
+ as: :verify_contract_via_standard_json_input
+ )
+
+ resources(
+ "/verify-vyper-contract",
+ AddressContractVerificationVyperController,
+ only: [:new],
+ as: :verify_vyper_contract
+ )
+
resources(
"/read-contract",
AddressReadContractController,
@@ -238,11 +236,32 @@ defmodule BlockScoutWeb.WebRouter do
resources(
"/read-contract",
- Tokens.ReadContractController,
+ Tokens.ContractController,
only: [:index],
as: :read_contract
)
+ resources(
+ "/write-contract",
+ Tokens.ContractController,
+ only: [:index],
+ as: :write_contract
+ )
+
+ resources(
+ "/read-proxy",
+ Tokens.ContractController,
+ only: [:index],
+ as: :read_proxy
+ )
+
+ resources(
+ "/write-proxy",
+ Tokens.ContractController,
+ only: [:index],
+ as: :write_proxy
+ )
+
resources(
"/token-holders",
Tokens.HolderController,
@@ -276,6 +295,13 @@ defmodule BlockScoutWeb.WebRouter do
only: [:index],
as: :metadata
)
+
+ resources(
+ "/token-holders",
+ Tokens.Instance.HolderController,
+ only: [:index],
+ as: :holder
+ )
end
end
@@ -289,11 +315,32 @@ defmodule BlockScoutWeb.WebRouter do
resources(
"/read-contract",
- Tokens.ReadContractController,
+ Tokens.ContractController,
only: [:index],
as: :read_contract
)
+ resources(
+ "/write-contract",
+ Tokens.ContractController,
+ only: [:index],
+ as: :write_contract
+ )
+
+ resources(
+ "/read-proxy",
+ Tokens.ContractController,
+ only: [:index],
+ as: :read_proxy
+ )
+
+ resources(
+ "/write-proxy",
+ Tokens.ContractController,
+ only: [:index],
+ as: :write_proxy
+ )
+
resources(
"/token-holders",
Tokens.HolderController,
@@ -327,6 +374,13 @@ defmodule BlockScoutWeb.WebRouter do
only: [:index],
as: :metadata
)
+
+ resources(
+ "/token-holders",
+ Tokens.Instance.HolderController,
+ only: [:index],
+ as: :holder
+ )
end
end
@@ -345,8 +399,20 @@ defmodule BlockScoutWeb.WebRouter do
get("/search-results", SearchController, :search_results)
+ get("/csv-export", CsvExportController, :index)
+
+ post("/captcha", CaptchaController, :index)
+
+ get("/transactions-csv", AddressTransactionController, :transactions_csv)
+
get("/token-autocomplete", ChainController, :token_autocomplete)
+ get("/token-transfers-csv", AddressTransactionController, :token_transfers_csv)
+
+ get("/internal-transactions-csv", AddressTransactionController, :internal_transactions_csv)
+
+ get("/logs-csv", AddressTransactionController, :logs_csv)
+
get("/chain-blocks", ChainController, :chain_blocks, as: :chain_blocks)
get("/token-counters", Tokens.TokenController, :token_counters)
diff --git a/apps/block_scout_web/mix.exs b/apps/block_scout_web/mix.exs
index 4a379609a4ae..0bc11d48f871 100644
--- a/apps/block_scout_web/mix.exs
+++ b/apps/block_scout_web/mix.exs
@@ -84,10 +84,11 @@ defmodule BlockScoutWeb.Mixfile do
{:floki, "~> 0.31"},
{:flow, "~> 0.12"},
{:gettext, "~> 0.18.2"},
+ {:hammer, "~> 6.0"},
{:httpoison, "~> 1.6"},
{:indexer, in_umbrella: true, runtime: false},
# JSON parser and generator
- {:jason, "~> 1.2.2"},
+ {:jason, "~> 1.3"},
{:junit_formatter, ">= 0.0.0", only: [:test], runtime: false},
# Log errors and application output to separate files
{:logger_file_backend, "~> 0.0.10"},
@@ -113,6 +114,7 @@ defmodule BlockScoutWeb.Mixfile do
{:prometheus_plugs, "~> 1.1"},
# OS process metrics for Prometheus
{:prometheus_process_collector, "~> 1.3"},
+ {:remote_ip, "~> 1.0"},
{:qrcode, "~> 0.1.0"},
{:sobelow, ">= 0.7.0", only: [:dev, :test], runtime: false},
# Tracing
diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot
index 0169ac6ce587..7de56d5465bd 100644
--- a/apps/block_scout_web/priv/gettext/default.pot
+++ b/apps/block_scout_web/priv/gettext/default.pot
@@ -6,7 +6,7 @@ msgstr[0] ""
msgstr[1] ""
#, elixir-format
-#: lib/block_scout_web/views/address_token_balance_view.ex:8
+#: lib/block_scout_web/views/address_token_balance_view.ex:10
msgid "%{count} token"
msgid_plural "%{count} tokens"
msgstr[0] ""
@@ -45,22 +45,22 @@ msgid "%{block_type} Details"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:49
+#: lib/block_scout_web/templates/block/overview.html.eex:55
msgid "%{block_type} Height"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/index.html.eex:11
+#: lib/block_scout_web/templates/block/index.html.eex:7
msgid "%{block_type}s"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:79
+#: lib/block_scout_web/templates/block/overview.html.eex:85
msgid "%{count} Transaction"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:81
+#: lib/block_scout_web/templates/block/overview.html.eex:87
#: lib/block_scout_web/templates/chain/_block.html.eex:11
msgid "%{count} Transactions"
msgstr ""
@@ -81,7 +81,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 ""
@@ -99,17 +99,27 @@ msgid "(query)"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/app.html.eex:186
+#: lib/block_scout_web/templates/layout/app.html.eex:189
msgid "- We're indexing this chain right now. Some of the counts may be inaccurate."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:189
+#: lib/block_scout_web/templates/block/overview.html.eex:195
msgid "64-bit hash of value verifying proof-of-work (note: null for POA chains)."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:91
+#: lib/block_scout_web/templates/stakes/_stakes_modal_withdraw.html.eex:76
+msgid "
Pending stake (stake placed on a candidate pool or placed during the current staking epoch) may be withdrawn now.
\n
Active stake (stake available after the current epoch) can be ordered for withdrawal from the pool, and will be available to claim after the current staking epoch is complete.
\n
If you have already ordered (and the staking window is still open), you may increase your current order by entering a positive value, or decrease your current order by entering a negative value in the box and clicking 'Order Withdrawal'. You must either keep the minimum stake amount in the pool, or order your entire stake for withdrawal.
To become a candidate, your staking address must be funded with %{tokenSymbol} tokens and %{coinSymbol} coins, and your OpenEthereum node must be active and configured with the mining address you specify here.
\n
To become a delegator, close this window and select an address from the list of pools you would like to place stake on. Click the Stake button next to the address to begin the process.
"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/block/overview.html.eex:97
msgid "A block producer who successfully included the block onto the blockchain."
msgstr ""
@@ -124,7 +134,8 @@ msgid "A string with the name of the module to be invoked."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:156
+#: lib/block_scout_web/templates/address_contract_verification_common_fields/_constructor_args.html.eex:3
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:62
msgid "ABI-encoded Constructor Arguments (if required by the contract)"
msgstr ""
@@ -144,7 +155,7 @@ msgid "API for the %{subnetwork} - BlockScout"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:122
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:124
msgid "APIs"
msgstr ""
@@ -164,47 +175,62 @@ msgid "Action"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:404
+#: lib/block_scout_web/templates/transaction/overview.html.eex:425
msgid "Actual gas amount used by the transaction."
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/layout/_footer.html.eex:44
+msgid "Add"
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:16
-#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:26 lib/block_scout_web/views/address_view.ex:104
+#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:20 lib/block_scout_web/views/address_view.ex:104
msgid "Address"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:192
+#: 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:172
+#: lib/block_scout_web/templates/transaction/overview.html.eex:193
msgid "Address (external or contract) sending the transaction."
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/address/overview.html.eex:166
+msgid "Address balance in xDAI (doesn't include ERC20, ERC721, ERC1155 tokens)."
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:65
+msgid "Address of the token contract"
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/address/index.html.eex:5
msgid "Addresses"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:30
-#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:28 lib/block_scout_web/templates/address_transaction/index.html.eex:26
-#: lib/block_scout_web/templates/layout/_network_selector.html.eex:21 lib/block_scout_web/templates/layout/_topnav.html.eex:71
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:103 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
+#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:26
+#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:28 lib/block_scout_web/templates/address_transaction/index.html.eex:22
+#: lib/block_scout_web/templates/layout/_network_selector.html.eex:21 lib/block_scout_web/templates/layout/_topnav.html.eex:73
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:105 lib/block_scout_web/views/address_internal_transaction_view.ex:11
+#: lib/block_scout_web/views/address_token_transfer_view.ex:11 lib/block_scout_web/views/address_transaction_view.ex:11
msgid "All"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:18
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:13
msgid "All functions displayed below are from ABI of that contract. In order to verify current contract, proceed with"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:24
+#: lib/block_scout_web/templates/address_contract/index.html.eex:27
msgid "All metadata displayed below is from that contract. In order to verify current contract, click"
msgstr ""
@@ -213,6 +239,11 @@ msgstr ""
msgid "All pool participant addresses. The top address belongs to the %{pool_type}."
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/address/overview.html.eex:191
+msgid "All tokens in the account and total value."
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/stakes/_stakes_modal_withdraw.html.eex:27
msgid "Already Ordered:"
@@ -225,18 +256,18 @@ msgid "Amount"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:41
-msgid "Amount of %{symbol} placed by an address."
+#: lib/block_scout_web/templates/transaction/overview.html.eex:411
+msgid "Amount of"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:230
-msgid "Amount of distributed reward. Miners receive a static block reward + Tx fees + uncle fees."
+#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:41
+msgid "Amount of %{symbol} placed by an address."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:390
-msgid "Amount of xDai burned for this transaction. Equals Block Base Fee per Gas * Gas Used."
+#: lib/block_scout_web/templates/block/overview.html.eex:236
+msgid "Amount of distributed reward. Miners receive a static block reward + Tx fees + uncle fees."
msgstr ""
#, elixir-format
@@ -255,7 +286,7 @@ msgid "Approximate Current Annual Percentage Yield. If you see N/A, please wait
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:150
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:152
msgid "Apps"
msgstr ""
@@ -276,12 +307,13 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_balance_card.html.eex:4
+#: lib/block_scout_web/templates/address/overview.html.eex:167 lib/block_scout_web/templates/address_token/overview.html.eex:51
#: lib/block_scout_web/templates/stakes/_stakes_stats_item_account.html.eex:42 lib/block_scout_web/templates/stakes/_stakes_stats_item_account.html.eex:43
msgid "Balance"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:18
+#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:14
msgid "Balances"
msgstr ""
@@ -296,7 +328,7 @@ msgid "Banned until block #%{banned_until} (%{estimated_unban_day})"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:201
+#: lib/block_scout_web/templates/block/overview.html.eex:207
msgid "Base Fee per Gas"
msgstr ""
@@ -314,19 +346,19 @@ msgid "Become a Candidate"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:425
+#: lib/block_scout_web/templates/transaction/overview.html.eex:446
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:131
+#: lib/block_scout_web/templates/block/overview.html.eex:29 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:32 lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:43
msgid "Block #%{number}"
msgstr ""
@@ -341,12 +373,12 @@ msgid "Block Details"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:47
+#: lib/block_scout_web/templates/block/overview.html.eex:53
msgid "Block Height"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/app.html.eex:40
+#: lib/block_scout_web/templates/layout/app.html.eex:46
msgid "Block Mined, awaiting import..."
msgstr ""
@@ -356,7 +388,7 @@ msgid "Block Pending"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:152
+#: lib/block_scout_web/templates/block/overview.html.eex:158
msgid "Block difficulty for miner, used to calibrate block generation time (Note: constant in POA based networks)."
msgstr ""
@@ -371,10 +403,15 @@ msgid "Block number"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:130
+#: 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:274
+msgid "Block number in which the address was updated."
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/chain/_metatags.html.eex:4
msgid "BlockScout provides analytics data, API, and Smart Contract tools for the %{subnetwork}"
@@ -382,23 +419,24 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:176
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:22 lib/block_scout_web/templates/layout/_topnav.html.eex:26
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:24 lib/block_scout_web/templates/layout/_topnav.html.eex:28
msgid "Blocks"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/app.html.eex:39
+#: lib/block_scout_web/templates/layout/app.html.eex:45
msgid "Blocks Indexed"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:48
-#: lib/block_scout_web/templates/address_validation/index.html.eex:15 lib/block_scout_web/views/address_view.ex:356
+#: lib/block_scout_web/templates/address/overview.html.eex:291 lib/block_scout_web/templates/address_validation/index.html.eex:11
+#: lib/block_scout_web/views/address_view.ex:356
msgid "Blocks Validated"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_footer.html.eex:20
+#: lib/block_scout_web/templates/layout/_footer.html.eex:22
msgid "Blockscout is a tool for inspecting and analyzing EVM based blockchains. Blockchain explorer for Ethereum Networks."
msgstr ""
@@ -413,22 +451,33 @@ msgid "Bridged Tokens from "
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:82
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:84
msgid "Bridged from BSC"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:76
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:78
msgid "Bridged from Ethereum"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/_tile.html.eex:64
-#: lib/block_scout_web/templates/block/overview.html.eex:210
+#: lib/block_scout_web/templates/block/overview.html.eex:216
msgid "Burnt Fees"
msgstr ""
#, elixir-format
+#: lib/block_scout_web/templates/address_token/overview.html.eex:65
+msgid "CRC Worth"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/common_components/_csv_export_button.html.eex:2
+msgid "CSV"
+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 ""
@@ -439,9 +488,11 @@ msgid "Call Code"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:91
-#: 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/api_docs/_action_tile.html.eex:47
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:114
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:231
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:48
+#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:60
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:85 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 ""
@@ -462,8 +513,8 @@ msgid "Change Network"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_footer.html.eex:39
-msgid "Chat"
+#: lib/block_scout_web/templates/layout/_footer.html.eex:41
+msgid "Chat (#blockscout)"
msgstr ""
#, elixir-format
@@ -530,78 +581,89 @@ msgid "Coin Balance History"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:71
+#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:55
+msgid "Collapse"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification_common_fields/_compiler_field.html.eex:3
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:40
msgid "Compiler"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:57
+#: lib/block_scout_web/templates/address_contract/index.html.eex:65
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 ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:109
+#: lib/block_scout_web/templates/transaction/overview.html.eex:118
msgid "Confirmed by "
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:163
+#: lib/block_scout_web/templates/transaction/overview.html.eex:184
msgid "Confirmed within"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:4
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:13
-#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:4 lib/block_scout_web/templates/tokens/holder/index.html.eex:17
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:2
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:11
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:2
+#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:9
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:6 lib/block_scout_web/templates/tokens/holder/index.html.eex:15
msgid "Connection Lost"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:14
-#: lib/block_scout_web/templates/block/index.html.eex:7
+#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:12
+#: lib/block_scout_web/templates/block/index.html.eex:5
msgid "Connection Lost, click to load newer blocks"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:17
+#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:15
msgid "Connection Lost, click to load newer internal transactions"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_transaction/index.html.eex:13
-#: lib/block_scout_web/templates/pending_transaction/index.html.eex:18 lib/block_scout_web/templates/transaction/index.html.eex:24
+#: lib/block_scout_web/templates/address_transaction/index.html.eex:11
+#: lib/block_scout_web/templates/pending_transaction/index.html.eex:16 lib/block_scout_web/templates/transaction/index.html.eex:22
msgid "Connection Lost, click to load newer transactions"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_validation/index.html.eex:12
+#: lib/block_scout_web/templates/address_validation/index.html.eex:10
msgid "Connection Lost, click to load newer validations"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:82
+#: lib/block_scout_web/templates/address_contract/index.html.eex:90
msgid "Constructor Arguments"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:202
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:66
+#: 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:120
+#: lib/block_scout_web/templates/address_contract/index.html.eex:126
msgid "Contract ABI"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:18
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:27
-#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:13 lib/block_scout_web/views/address_view.ex:102
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:14
+#: lib/block_scout_web/templates/address_contract_verification_common_fields/_contract_address_field.html.eex:3
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:23
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:9
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:18 lib/block_scout_web/views/address_view.ex:102
msgid "Contract Address"
msgstr ""
@@ -612,73 +674,75 @@ 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:136
-#: lib/block_scout_web/templates/address_contract/index.html.eex:151
+#: lib/block_scout_web/templates/address_contract/index.html.eex:142
+#: lib/block_scout_web/templates/address_contract/index.html.eex:157
msgid "Contract Creation Code"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:170
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:100
msgid "Contract Libraries"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:38
+#: lib/block_scout_web/templates/address/overview.html.eex:93
+#: lib/block_scout_web/templates/address_contract_verification_common_fields/_contract_name_field.html.eex:3
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:29
msgid "Contract Name"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:22
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:16
+#: lib/block_scout_web/templates/address_contract/index.html.eex:25
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:11
msgid "Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:49
+#: lib/block_scout_web/templates/address_contract/index.html.eex:57
msgid "Contract name:"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:92
+#: lib/block_scout_web/templates/address_contract/index.html.eex:100
msgid "Contract source code"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:142
+#: lib/block_scout_web/templates/address_contract/index.html.eex:148
msgid "Contracts that self destruct in their constructors have no contract code published and cannot be verified."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_footer.html.eex:38
+#: lib/block_scout_web/templates/layout/_footer.html.eex:40
msgid "Contribute"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:122
+#: lib/block_scout_web/templates/address_contract/index.html.eex:128
msgid "Copy ABI"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:37
-#: lib/block_scout_web/templates/address/overview.html.eex:38 lib/block_scout_web/templates/block/overview.html.eex:98
-#: lib/block_scout_web/templates/block/overview.html.eex:99 lib/block_scout_web/templates/tokens/overview/_details.html.eex:50
+#: lib/block_scout_web/templates/address/overview.html.eex:38 lib/block_scout_web/templates/block/overview.html.eex:104
+#: lib/block_scout_web/templates/block/overview.html.eex:105 lib/block_scout_web/templates/tokens/overview/_details.html.eex:50
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:51
msgid "Copy Address"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:138
-#: lib/block_scout_web/templates/address_contract/index.html.eex:154
+#: lib/block_scout_web/templates/address_contract/index.html.eex:144
+#: lib/block_scout_web/templates/address_contract/index.html.eex:160
msgid "Copy Contract Creation Code"
msgstr ""
@@ -695,14 +759,14 @@ 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:182
-#: lib/block_scout_web/templates/transaction/overview.html.eex:183
+#: 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 ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:123
-#: lib/block_scout_web/templates/block/overview.html.eex:124
+#: lib/block_scout_web/templates/block/overview.html.eex:129
+#: lib/block_scout_web/templates/block/overview.html.eex:130
msgid "Copy Hash"
msgstr ""
@@ -712,8 +776,8 @@ msgid "Copy Metadata"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:143
-#: lib/block_scout_web/templates/block/overview.html.eex:144
+#: lib/block_scout_web/templates/block/overview.html.eex:149
+#: lib/block_scout_web/templates/block/overview.html.eex:150
msgid "Copy Parent Hash"
msgstr ""
@@ -723,16 +787,16 @@ msgid "Copy Raw Trace"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:94
-#: lib/block_scout_web/templates/address_contract/index.html.eex:108
+#: lib/block_scout_web/templates/address_contract/index.html.eex:102
+#: lib/block_scout_web/templates/address_contract/index.html.eex:115
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:211
-#: lib/block_scout_web/templates/transaction/overview.html.eex:212 lib/block_scout_web/templates/transaction/overview.html.eex:221
-#: lib/block_scout_web/templates/transaction/overview.html.eex:222
+#: 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 ""
@@ -743,29 +807,29 @@ msgid "Copy Token ID"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:72
+#: lib/block_scout_web/templates/transaction/overview.html.eex:81
msgid "Copy Transaction Hash"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:73
+#: lib/block_scout_web/templates/transaction/overview.html.eex:82
msgid "Copy Txn Hash"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:451
+#: lib/block_scout_web/templates/transaction/overview.html.eex:472
msgid "Copy Txn Hex Input"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:457
+#: lib/block_scout_web/templates/transaction/overview.html.eex:478
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:450
-#: lib/block_scout_web/templates/transaction/overview.html.eex:456
+#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:41 lib/block_scout_web/templates/transaction/overview.html.eex:471
+#: lib/block_scout_web/templates/transaction/overview.html.eex:477
msgid "Copy Value"
msgstr ""
@@ -780,8 +844,8 @@ msgid "Create2"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address/overview.html.eex:111
-msgid "Created by"
+#: lib/block_scout_web/templates/address/overview.html.eex:120
+msgid "Creator"
msgstr ""
#, elixir-format
@@ -812,7 +876,7 @@ msgid "Current Stake:"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:82
+#: lib/block_scout_web/templates/transaction/overview.html.eex:91
msgid "Current transaction state: Success, Failed (Error), or Pending (In Process)"
msgstr ""
@@ -822,33 +886,33 @@ msgid "DApp for Staking %{symbol} tokens"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:107
-#: 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/transaction_log/_logs.html.eex:110
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:101
+#: 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 ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:64
+#: lib/block_scout_web/templates/block/overview.html.eex:70
msgid "Date & time at which block was produced."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:149
+#: lib/block_scout_web/templates/transaction/overview.html.eex:170
msgid "Date & time of transaction inclusion, including length of time for confirmation."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:52
-#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:85
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:145
msgid "Decimals"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:38
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:44 lib/block_scout_web/templates/address_logs/_logs.html.eex:59
-#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:39 lib/block_scout_web/templates/transaction_log/_logs.html.eex:47
-#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:62
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:32
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:38 lib/block_scout_web/templates/address_logs/_logs.html.eex:53
+#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:33 lib/block_scout_web/templates/transaction_log/_logs.html.eex:41
+#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:56 lib/block_scout_web/templates/transaction_log/_logs.html.eex:72
msgid "Decoded"
msgstr ""
@@ -913,20 +977,30 @@ msgid "Details"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:153
+#: lib/block_scout_web/templates/block/overview.html.eex:159
msgid "Difficulty"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:143
+#: lib/block_scout_web/templates/address_contract/index.html.eex:149
msgid "Displaying the init data provided of the creating transaction."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:30
+#: lib/block_scout_web/templates/csv_export/index.html.eex:25
+msgid "Download"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:26
msgid "Drop sources and metadata JSON file or click here"
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:33
+msgid "Drop the standard input JSON file or click here"
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/transaction/not_found.html.eex:22
msgid "During times when the network is busy (i.e during ICOs) it can take a while for your transaction to propagate through the network and for us to index it."
@@ -938,18 +1012,24 @@ 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 ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:95
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:95 lib/block_scout_web/templates/smart_contract/_functions.html.eex:131
+#: lib/block_scout_web/templates/address_token/overview.html.eex:1
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:93 lib/block_scout_web/templates/smart_contract/_functions.html.eex:93
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:135
msgid "ETH"
msgstr ""
@@ -959,8 +1039,8 @@ msgid "ETH RPC API Documentation"
msgstr ""
#, elixir-format
-#: 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
+#: lib/block_scout_web/templates/address_contract/index.html.eex:76
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:40
msgid "EVM Version"
msgstr ""
@@ -980,10 +1060,15 @@ msgid "Emission Reward"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:124
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:82
msgid "Enter the Solidity Contract Code"
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:51
+msgid "Enter the Vyper Contract Code"
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/stakes/_stakes_top.html.eex:4
msgid "Epoch number"
@@ -995,42 +1080,51 @@ 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/internal_transaction/_tile.html.eex:11
+#: 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_card.html.eex:31
+#: 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 ""
#, elixir-format
-#: lib/block_scout_web/templates/address/overview.html.eex:124
+#: lib/block_scout_web/templates/address/overview.html.eex:137
msgid "Error: Could not determine contract creator."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:136
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:138
msgid "Eth RPC"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_balance_card.html.eex:18
-#: lib/block_scout_web/templates/address/index.html.eex:5 lib/block_scout_web/templates/internal_transaction/_tile.html.eex:20
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:87 lib/block_scout_web/templates/layout/_topnav.html.eex:108
-#: lib/block_scout_web/templates/layout/app.html.eex:45 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/views/wei_helpers.ex:78
-msgid "Evmos"
+#: lib/block_scout_web/templates/address/index.html.eex:5 lib/block_scout_web/templates/address/overview.html.eex:180
+#: lib/block_scout_web/templates/block/overview.html.eex:215 lib/block_scout_web/templates/internal_transaction/_tile.html.eex:24
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:89 lib/block_scout_web/templates/layout/_topnav.html.eex:110
+#: lib/block_scout_web/templates/layout/app.html.eex:51 lib/block_scout_web/templates/transaction/_pending_tile.html.eex:20
+#: lib/block_scout_web/templates/transaction/_tile.html.eex:43 lib/block_scout_web/templates/transaction/overview.html.eex:411
+#: lib/block_scout_web/views/wei_helpers.ex:78
+msgid "tevmos"
msgstr ""
#, elixir-format
@@ -1046,18 +1140,28 @@ msgid "Execute"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:234
+#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:55
+msgid "Expand"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/csv_export/index.html.eex:10
+msgid "Export Data"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract/index.html.eex:228
msgid "External libraries"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:39
+#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:40
msgid "Failed to decode input data."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:41
-#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:42
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:35
+#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:36
msgid "Failed to decode log data."
msgstr ""
@@ -1067,13 +1171,31 @@ msgid "Favorites"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address/_balance_card.html.eex:32
+#: lib/block_scout_web/templates/address/overview.html.eex:264
+msgid "Fetching gas used..."
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:126
+msgid "Fetching holders..."
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address/_balance_card.html.eex:28
+#: lib/block_scout_web/templates/address/_balance_dropdown.html.eex:7
msgid "Fetching tokens..."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:35
-msgid "Flattened source code"
+#: lib/block_scout_web/templates/address/overview.html.eex:211
+#: lib/block_scout_web/templates/address/overview.html.eex:219
+msgid "Fetching transactions..."
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address/overview.html.eex:238
+#: lib/block_scout_web/templates/address/overview.html.eex:246 lib/block_scout_web/templates/tokens/overview/_details.html.eex:137
+msgid "Fetching transfers..."
msgstr ""
#, elixir-format
@@ -1082,15 +1204,20 @@ msgid "For any existing contracts in the database, insert all ABI entries into t
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:32
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:34
msgid "Forked Blocks (Reorgs)"
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:173 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
+#: lib/block_scout_web/templates/layout/_footer.html.eex:42
+msgid "Forum"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:38
+#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:40 lib/block_scout_web/templates/address_transaction/index.html.eex:34
+#: 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 ""
@@ -1101,58 +1228,73 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/_tile.html.eex:67
-#: lib/block_scout_web/templates/block/overview.html.eex:181 lib/block_scout_web/templates/transaction/overview.html.eex:352
+#: lib/block_scout_web/templates/block/overview.html.eex:187 lib/block_scout_web/templates/transaction/overview.html.eex:373
msgid "Gas Limit"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:332
+#: lib/block_scout_web/templates/transaction/overview.html.eex:353
msgid "Gas Price"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/_tile.html.eex:73
-#: lib/block_scout_web/templates/block/overview.html.eex:172
+#: lib/block_scout_web/templates/address/overview.html.eex:257
+#: lib/block_scout_web/templates/block/_tile.html.eex:73 lib/block_scout_web/templates/block/overview.html.eex:178
msgid "Gas Used"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:405
+#: lib/block_scout_web/templates/transaction/overview.html.eex:426
msgid "Gas Used by Transaction"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:54
+#: lib/block_scout_web/templates/address/overview.html.eex:256
+msgid "Gas used by the address."
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/block/overview.html.eex:60
msgid "Genesis Block"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_footer.html.eex:22
+#: lib/block_scout_web/templates/layout/_footer.html.eex:24
msgid "Github"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:126
+#: lib/block_scout_web/templates/common_components/_rap_pagination_container.html.eex:8
+msgid "Go to"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:128
msgid "GraphQL"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:52
#: lib/block_scout_web/views/block_view.ex:21 lib/block_scout_web/views/wei_helpers.ex:77
-msgid "MicroEvmos"
+msgid "ntevmos"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:117
+#: lib/block_scout_web/templates/block/overview.html.eex:123
msgid "Hash"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:433
-#: lib/block_scout_web/templates/transaction/overview.html.eex:437
+#: lib/block_scout_web/templates/transaction/overview.html.eex:454
+#: lib/block_scout_web/templates/transaction/overview.html.eex:458
msgid "Hex (Default)"
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:122
+msgid "Holders"
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/stakes/_stakes_modal_pool_info.html.eex:77
msgid "How Many Times this Address has been Banned"
@@ -1169,13 +1311,13 @@ msgid "However, in general, the"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:25
+#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:19
msgid "IMPORTANT: This information is a best guess based on similar functions from other verified contracts."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:38
-#: lib/block_scout_web/templates/transaction/_tile.html.eex:80
+#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:42
+#: lib/block_scout_web/templates/transaction/_tile.html.eex:86
msgid "IN"
msgstr ""
@@ -1190,8 +1332,13 @@ msgid "If you have just submitted this transaction please wait for at least 30 s
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address/overview.html.eex:65
-msgid "Implementation:"
+#: lib/block_scout_web/templates/address/overview.html.eex:149
+msgid "Implementation"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address/overview.html.eex:148
+msgid "Implementation address of the proxy contract."
msgstr ""
#, elixir-format
@@ -1200,12 +1347,12 @@ msgid "Inactive Pool Addresses. Current validator pools are specified by a check
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:417
+#: lib/block_scout_web/templates/transaction/overview.html.eex:438
msgid "Index position of Transaction in the block."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:243
+#: lib/block_scout_web/templates/block/overview.html.eex:249
msgid "Index position(s) of referenced stale blocks."
msgstr ""
@@ -1215,7 +1362,7 @@ msgid "Indexed?"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/app.html.eex:41
+#: lib/block_scout_web/templates/layout/app.html.eex:47
msgid "Indexing Tokens"
msgstr ""
@@ -1225,20 +1372,20 @@ msgid "Input"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:194
+#: lib/block_scout_web/templates/transaction/overview.html.eex:215
msgid "Interacted With (To)"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:6
+#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:7
msgid "Internal Transaction"
msgstr ""
#, elixir-format
#: 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/address_internal_transaction/index.html.eex:17 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:346
-#: lib/block_scout_web/views/transaction_view.ex:515
+#: lib/block_scout_web/views/transaction_view.ex:512
msgid "Internal Transactions"
msgstr ""
@@ -1249,7 +1396,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/inventory/index.html.eex:15
-#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:17 lib/block_scout_web/views/tokens/overview_view.ex:44
+#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:19 lib/block_scout_web/views/tokens/overview_view.ex:44
msgid "Inventory"
msgstr ""
@@ -1269,30 +1416,30 @@ msgid "JSON RPC error"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address/overview.html.eex:96
-msgid "Last Balance Update: Block #"
+#: lib/block_scout_web/templates/address/overview.html.eex:275
+msgid "Last Balance Update"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/app.html.eex:42
+#: lib/block_scout_web/templates/layout/app.html.eex:48
msgid "Less than"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:185
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:207
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:229
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:251
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:273
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:115
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:137
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:159
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:181
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:203
msgid "Library Address"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:175
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:197
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:219
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:241
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:263
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:105
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:127
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:149
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:171
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:193
msgid "Library Name"
msgstr ""
@@ -1312,51 +1459,48 @@ msgid "Likelihood of Becoming a Validator on the Next Epoch"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:284
+#: 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:268
+#: 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:251
+#: 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:235
+#: 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:18
+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:79
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:225
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:42
+#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:54
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:79 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/contract/index.html.eex:16
msgid "Loading..."
msgstr ""
-#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:72
-#: 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
-msgid "Loading...."
-msgstr ""
-
#, elixir-format
#: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:2
msgid "Log Data"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:120
+#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:130
msgid "Log Index"
msgstr ""
@@ -1364,7 +1508,7 @@ msgstr ""
#: lib/block_scout_web/templates/address/_tabs.html.eex:41
#: 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:357
-#: lib/block_scout_web/views/transaction_view.ex:516
+#: lib/block_scout_web/views/transaction_view.ex:513
msgid "Logs"
msgstr ""
@@ -1374,7 +1518,7 @@ msgid "ME"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_footer.html.eex:47
+#: lib/block_scout_web/templates/layout/_footer.html.eex:52
msgid "Main Networks"
msgstr ""
@@ -1385,7 +1529,8 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:61
-#: lib/block_scout_web/templates/layout/app.html.eex:43 lib/block_scout_web/views/address_view.ex:142
+#: lib/block_scout_web/templates/layout/app.html.eex:49 lib/block_scout_web/templates/tokens/overview/_details.html.eex:98
+#: lib/block_scout_web/views/address_view.ex:142
msgid "Market Cap"
msgstr ""
@@ -1395,44 +1540,44 @@ msgid "Max Amount to Move:"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:361
+#: lib/block_scout_web/templates/transaction/overview.html.eex:382
msgid "Max Fee per Gas"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:371
+#: lib/block_scout_web/templates/transaction/overview.html.eex:392
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:351
+#: lib/block_scout_web/templates/transaction/overview.html.eex:372
msgid "Maximum gas amount approved for the transaction."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:360
+#: lib/block_scout_web/templates/transaction/overview.html.eex:381
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:187
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 ""
#, elixir-format
#: lib/block_scout_web/templates/block/_tile.html.eex:41
-#: lib/block_scout_web/templates/block/overview.html.eex:92 lib/block_scout_web/templates/chain/_block.html.eex:16
+#: lib/block_scout_web/templates/block/overview.html.eex:98 lib/block_scout_web/templates/chain/_block.html.eex:16
msgid "Block Proposer"
msgstr ""
@@ -1458,7 +1603,7 @@ msgid "Minimum Stake:"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:200
+#: lib/block_scout_web/templates/block/overview.html.eex:206
msgid "Minimum fee required per unit of gas. Fee adjusts based on network congestion."
msgstr ""
@@ -1483,7 +1628,7 @@ msgid "More internal transactions have come in"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_transaction/index.html.eex:50
+#: lib/block_scout_web/templates/address_transaction/index.html.eex:46
#: lib/block_scout_web/templates/chain/show.html.eex:239 lib/block_scout_web/templates/pending_transaction/index.html.eex:13
#: lib/block_scout_web/templates/transaction/index.html.eex:19
msgid "More transactions have come in"
@@ -1507,23 +1652,44 @@ msgstr ""
msgid "N/A"
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/block/overview.html.eex:116
+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 ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:9
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:18
-#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:9
+#: lib/block_scout_web/templates/address_token/overview.html.eex:44
+msgid "Net Worth"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:5
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:5
+#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:14
msgid "New Smart Contract Verification"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:75
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:14
+msgid "New Solidity Smart Contract Verification"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:9
+msgid "New Vyper Smart Contract Verification"
+msgstr ""
+
+#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:82
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:89 lib/block_scout_web/templates/address_contract_verification/new.html.eex:97
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:105
msgid "Next"
msgstr ""
@@ -1533,9 +1699,9 @@ msgid "Next epoch in"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:55
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:98
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:141 lib/block_scout_web/templates/stakes/_rows.html.eex:24
+#: lib/block_scout_web/templates/address_contract_verification_common_fields/_fetch_constructor_args.html.eex:9
+#: lib/block_scout_web/templates/address_contract_verification_common_fields/_include_nightly_builds_field.html.eex:9
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:56 lib/block_scout_web/templates/stakes/_rows.html.eex:24
msgid "No"
msgstr ""
@@ -1545,25 +1711,65 @@ msgid "No Information"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:190
-#: lib/block_scout_web/templates/transaction/overview.html.eex:415
+#: lib/block_scout_web/templates/block/overview.html.eex:196
+#: lib/block_scout_web/templates/transaction/overview.html.eex:436
msgid "Nonce"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:36
-#: lib/block_scout_web/templates/transaction/_tile.html.eex:76
+#: lib/block_scout_web/templates/tokens/inventory/_token.html.eex:11
+msgid "Not unique Token"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:121
+msgid "Number of accounts holding the token"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address/overview.html.eex:290
+msgid "Number of blocks validated by this validator."
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:144
+msgid "Number of digits that come after the decimal place when displaying token value"
+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/tokens/overview/_details.html.eex:132
+msgid "Number of transfers for the token"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address/overview.html.eex:229
+msgid "Number of transfers to/from this address."
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:40
+#: lib/block_scout_web/templates/transaction/_tile.html.eex:82
msgid "OUT"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:53
+#: lib/block_scout_web/templates/common_components/_rap_pagination_container.html.eex:13
+msgid "Only the first"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract/index.html.eex:61
msgid "Optimization enabled"
msgstr ""
#, elixir-format
-#: 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
+#: lib/block_scout_web/templates/address_contract/index.html.eex:70
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:72
msgid "Optimization runs"
msgstr ""
@@ -1579,12 +1785,12 @@ msgid "Ordered"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_footer.html.eex:71
+#: lib/block_scout_web/templates/layout/_footer.html.eex:76
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 ""
@@ -1606,14 +1812,14 @@ msgid "Parameters"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:133
+#: lib/block_scout_web/templates/block/overview.html.eex:139
msgid "Parent Hash"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:53
-#: 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/layout/_topnav.html.eex:55
+#: 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 ""
@@ -1660,12 +1866,12 @@ msgid "Pools searching is already in progress for this address"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:417
+#: lib/block_scout_web/templates/transaction/overview.html.eex:438
msgid "Position"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:248
+#: lib/block_scout_web/templates/block/overview.html.eex:254
msgid "Position %{index}"
msgstr ""
@@ -1676,7 +1882,8 @@ msgid "Potential Reward Share"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:24
+#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:18
+#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:32
msgid "Potential matches from our contract method database:"
msgstr ""
@@ -1687,18 +1894,23 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:41
-#: lib/block_scout_web/templates/layout/app.html.eex:44
+#: lib/block_scout_web/templates/layout/app.html.eex:50 lib/block_scout_web/templates/tokens/overview/_details.html.eex:109
msgid "Price"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:331
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:108
+msgid "Price per token on the exchanges"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/transaction/overview.html.eex:352
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:219
-#: lib/block_scout_web/templates/transaction/overview.html.eex:381
+#: lib/block_scout_web/templates/block/overview.html.eex:225
+#: lib/block_scout_web/templates/transaction/overview.html.eex:402
msgid "Priority Fee / Tip"
msgstr ""
@@ -1714,36 +1926,36 @@ msgid "QR Code"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:99
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:98
msgid "Query"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:131
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:133
msgid "RPC"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:426
+#: lib/block_scout_web/templates/transaction/overview.html.eex:447
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:517
+#: lib/block_scout_web/templates/transaction_raw_trace/index.html.eex:7 lib/block_scout_web/views/transaction_view.ex:514
msgid "Raw Trace"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:81
-#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:25 lib/block_scout_web/views/address_view.ex:351
+#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:27 lib/block_scout_web/views/address_view.ex:351
#: lib/block_scout_web/views/tokens/overview_view.ex:43
msgid "Read Contract"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:88
-#: lib/block_scout_web/views/address_view.ex:352
+#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:41 lib/block_scout_web/views/address_view.ex:352
msgid "Read Proxy"
msgstr ""
@@ -1778,8 +1990,10 @@ 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_via_flattened_code/new.html.eex:228
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:45
+#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:57
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:82
msgid "Reset"
msgstr ""
@@ -1796,18 +2010,18 @@ msgid "Responses"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:83
+#: lib/block_scout_web/templates/transaction/overview.html.eex:92
msgid "Result"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:120
+#: lib/block_scout_web/templates/transaction/overview.html.eex:129
msgid "Revert reason"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/_tile.html.eex:52
-#: 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 ""
@@ -1853,7 +2067,7 @@ msgid "Search network"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex:36
+#: lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex:47
msgid "Search tokens"
msgstr ""
@@ -1904,25 +2118,45 @@ msgid "Show only those I have stake in"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:108
+#: lib/block_scout_web/templates/address_token/overview.html.eex:52
+msgid "Shows the current"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_token/overview.html.eex:59
+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:66
+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"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/block/overview.html.eex:114
msgid "Size"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:107
+#: lib/block_scout_web/templates/block/overview.html.eex:113
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_internal_transaction/index.html.eex:54 lib/block_scout_web/templates/address_logs/index.html.eex:23
-#: lib/block_scout_web/templates/address_token/index.html.eex:17 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/address_coin_balance/index.html.eex:30
+#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:50 lib/block_scout_web/templates/address_logs/index.html.eex:23
+#: lib/block_scout_web/templates/address_token/index.html.eex:60 lib/block_scout_web/templates/address_token_transfer/index.html.eex:58
+#: lib/block_scout_web/templates/address_transaction/index.html.eex:50 lib/block_scout_web/templates/address_validation/index.html.eex:20
#: lib/block_scout_web/templates/block_transaction/index.html.eex:22 lib/block_scout_web/templates/chain/show.html.eex:180
-#: 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/pending_transaction/index.html.eex:18 lib/block_scout_web/templates/stakes/_table.html.eex:49
+#: lib/block_scout_web/templates/tokens/holder/index.html.eex:23 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:25
#: 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."
@@ -1944,15 +2178,10 @@ msgid "Source Pool"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:23
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:19
msgid "Sources and Metadata JSON"
msgstr ""
-#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:40
-msgid "Sourcify: Sources and metadata JSON file"
-msgstr ""
-
#, elixir-format
#: lib/block_scout_web/templates/stakes/_stakes_modal_stake.html.eex:7
msgid "Stake"
@@ -1990,7 +2219,7 @@ msgid "Staker's Address"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:152
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:154
msgid "Stakes"
msgstr ""
@@ -2001,7 +2230,7 @@ msgid "Stakes Ratio"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:158
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:160
msgid "Staking"
msgstr ""
@@ -2015,32 +2244,32 @@ msgstr ""
msgid "Staking epochs are not specified or not in the allowed range"
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:29
+msgid "Standard Input JSON"
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/views/internal_transaction_view.ex:24
msgid "Static Call"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:95
+#: lib/block_scout_web/templates/transaction/overview.html.eex:104
msgid "Status"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_footer.html.eex:37
+#: lib/block_scout_web/templates/layout/_footer.html.eex:39
msgid "Submit an Issue"
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 ""
-#, elixir-format
-#: lib/block_scout_web/templates/layout/_footer.html.eex:40
-msgid "Support"
-msgstr ""
-
#, elixir-format
#: lib/block_scout_web/templates/stakes/_stakes_top.html.eex:38
msgid "Swap STAKE on Honeyswap"
@@ -2048,7 +2277,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:21
-#: lib/block_scout_web/templates/transaction/_tile.html.eex:40
+#: lib/block_scout_web/templates/transaction/_tile.html.eex:46
msgid "TX Fee"
msgstr ""
@@ -2058,12 +2287,12 @@ msgid "Target Pool"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_footer.html.eex:28
+#: lib/block_scout_web/templates/layout/_footer.html.eex:30
msgid "Telegram"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_footer.html.eex:60
+#: lib/block_scout_web/templates/layout/_footer.html.eex:65
msgid "Test Networks"
msgstr ""
@@ -2078,7 +2307,7 @@ msgid "The Number of Delegators in the Pool"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:116
+#: lib/block_scout_web/templates/block/overview.html.eex:122
msgid "The SHA256 hash of the block."
msgstr ""
@@ -2088,12 +2317,12 @@ msgid "The amount can be claimed after the current epoch finishes."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:45
+#: lib/block_scout_web/templates/block/overview.html.eex:51
msgid "The block height of a particular block is defined as the number of blocks preceding it in the blockchain."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:43
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:41
msgid "The fallback function is executed on a call to the contract if none of the other functions match the given function signature, or if no data was supplied at all and there is no receive Ether function. The fallback function always receives data, but in order to also receive Ether it must be marked payable."
msgstr ""
@@ -2113,17 +2342,27 @@ msgid "The first amount is the validator’s own stake, the second is the total
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:132
+#: lib/block_scout_web/templates/block/overview.html.eex:138
msgid "The hash of the block from which this block was generated."
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/address/overview.html.eex:92
+msgid "The name found in the source code of the Contract."
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address/overview.html.eex:103
+msgid "The name of the validator."
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/stakes/_table.html.eex:43
msgid "The number of delegators providing stake to the pool. Click on the number to see more details."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:73
+#: lib/block_scout_web/templates/block/overview.html.eex:79
msgid "The number of transactions in the block."
msgstr ""
@@ -2133,7 +2372,7 @@ msgid "The percentage of stake in a single pool relative to the total amount sta
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:45
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:43
msgid "The receive function is executed on a call to the contract with empty calldata. This is the function that is executed on plain Ether transfers (e.g. via .send() or .transfer()). If no such function exists, but a payable fallback function exists, the fallback function will be called on a plain Ether transfer. If neither a receive Ether nor a payable fallback function is present, the contract cannot receive Ether through regular transactions and throws an exception."
msgstr ""
@@ -2143,7 +2382,7 @@ msgid "The rest addresses are delegators of its pool."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:119
+#: lib/block_scout_web/templates/transaction/overview.html.eex:128
msgid "The revert reason of the transaction."
msgstr ""
@@ -2153,7 +2392,7 @@ msgid "The staking epochs for which the reward could be claimed (read-only field
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:94
+#: lib/block_scout_web/templates/transaction/overview.html.eex:103
msgid "The status of the transaction: Confirmed or Unconfirmed."
msgstr ""
@@ -2163,27 +2402,32 @@ msgid "The table refreshed block(s) ago."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:171
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:82
+msgid "The total amount of tokens issued"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/block/overview.html.eex:177
msgid "The total gas amount used in the block and its percentage of gas filled in the block."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_validation/index.html.eex:20
+#: lib/block_scout_web/templates/address_validation/index.html.eex:16
msgid "There are no blocks validated by this address."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/index.html.eex:21
+#: lib/block_scout_web/templates/block/index.html.eex:17
msgid "There are no blocks."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/tokens/holder/index.html.eex:32
+#: lib/block_scout_web/templates/tokens/holder/index.html.eex:28
msgid "There are no holders for this Token."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:58
+#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:54
msgid "There are no internal transactions for this address."
msgstr ""
@@ -2203,7 +2447,7 @@ msgid "There are no logs for this transaction."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/pending_transaction/index.html.eex:26
+#: lib/block_scout_web/templates/pending_transaction/index.html.eex:22
msgid "There are no pending transactions."
msgstr ""
@@ -2218,7 +2462,7 @@ msgid "There are no token transfers for this transaction"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_token/index.html.eex:22
+#: lib/block_scout_web/templates/address_token/index.html.eex:65
msgid "There are no tokens for this address."
msgstr ""
@@ -2228,7 +2472,7 @@ msgid "There are no tokens."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_transaction/index.html.eex:59
+#: lib/block_scout_web/templates/address_transaction/index.html.eex:55
msgid "There are no transactions for this address."
msgstr ""
@@ -2238,18 +2482,18 @@ msgid "There are no transactions for this block."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/index.html.eex:35
+#: lib/block_scout_web/templates/transaction/index.html.eex:31
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:35
msgid "There is no coin history for this address."
msgstr ""
@@ -2264,7 +2508,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:21
#: lib/block_scout_web/templates/chain/show.html.eex:9
msgid "There was a problem loading the chart."
msgstr ""
@@ -2285,12 +2529,12 @@ msgid "This block has not been processed yet."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:33
+#: lib/block_scout_web/templates/address_contract/index.html.eex:41
msgid "This contract has been partially verified via Sourcify."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:37
+#: lib/block_scout_web/templates/address_contract/index.html.eex:45
msgid "This contract has been verified via Sourcify."
msgstr ""
@@ -2315,26 +2559,26 @@ msgid "This pool is banned until block #%{banned_until} (%{estimated_unban_day})
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:49
+#: lib/block_scout_web/templates/transaction/overview.html.eex:58
msgid "This transaction is pending confirmation."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:65
-#: lib/block_scout_web/templates/transaction/overview.html.eex:150
+#: lib/block_scout_web/templates/block/overview.html.eex:71
+#: 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:196 lib/block_scout_web/views/address_internal_transaction_view.ex:8
-#: lib/block_scout_web/views/address_token_transfer_view.ex:8 lib/block_scout_web/views/address_transaction_view.ex:8
+#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:32
+#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:34 lib/block_scout_web/templates/address_transaction/index.html.eex:28
+#: 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 ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:26
+#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:20
msgid "To have guaranteed accuracy, use the link above to verify the contract's source code."
msgstr ""
@@ -2345,18 +2589,22 @@ msgid "To see accurate decoded input data, the contract must be verified."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:11
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:13
msgid "Toggle navigation"
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/address/overview.html.eex:73
+msgid "Token"
+msgstr ""
+
+#, elixir-format
+#: 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 ""
@@ -2367,65 +2615,76 @@ msgid "Token Details"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/tokens/holder/index.html.eex:20
-#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:9 lib/block_scout_web/views/tokens/overview_view.ex:42
+#: lib/block_scout_web/templates/tokens/holder/index.html.eex:16
+#: 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:11 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 ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:13
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:19 lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex:3
-#: 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/instance/transfer/index.html.eex:16 lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:5
#: 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:348
-#: 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:514
+#: lib/block_scout_web/views/tokens/instance/overview_view.ex:186 lib/block_scout_web/views/tokens/overview_view.ex:41
+#: lib/block_scout_web/views/transaction_view.ex:511
msgid "Token Transfers"
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/address/overview.html.eex:72
+msgid "Token name and symbol."
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:156
+msgid "Token type"
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:21
-#: lib/block_scout_web/templates/address_token/index.html.eex:10 lib/block_scout_web/templates/address_token_transfer/index.html.eex:13
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:67 lib/block_scout_web/templates/layout/_topnav.html.eex:99
-#: lib/block_scout_web/templates/tokens/index.html.eex:10 lib/block_scout_web/views/address_view.ex:345
+#: lib/block_scout_web/templates/address/overview.html.eex:192 lib/block_scout_web/templates/address_token/overview.html.eex:58
+#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:13 lib/block_scout_web/templates/layout/_topnav.html.eex:69
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:101 lib/block_scout_web/templates/tokens/index.html.eex:10
+#: lib/block_scout_web/views/address_view.ex:345
msgid "Tokens"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:269
+#: 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:285
+#: 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:252
+#: 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:236
+#: lib/block_scout_web/templates/transaction/overview.html.eex:257
msgid "Tokens Transferred"
msgstr ""
@@ -2440,19 +2699,19 @@ msgid "Topic"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:77
-#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:80
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:71
+#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:90
msgid "Topics"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:163
+#: lib/block_scout_web/templates/block/overview.html.eex:169
msgid "Total Difficulty"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:97
-msgid "Total Supply"
+msgid "Total Supply * Price"
msgstr ""
#, elixir-format
@@ -2461,17 +2720,22 @@ msgid "Total blocks"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:162
+#: lib/block_scout_web/templates/block/overview.html.eex:168
msgid "Total difficulty of the chain until this block."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:180
+#: lib/block_scout_web/templates/block/overview.html.eex:186
msgid "Total gas limit provided by all transactions in the block."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:316
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:83
+msgid "Total supply"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/transaction/overview.html.eex:337
msgid "Total transaction fee."
msgstr ""
@@ -2482,12 +2746,12 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/stakes/_stakes_top.html.eex:52
-msgid "Trade STAKE on BitMax"
+msgid "Trade STAKE on AscendEX"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:25
-#: lib/block_scout_web/views/transaction_view.ex:463
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:19
+#: lib/block_scout_web/views/transaction_view.ex:460
msgid "Transaction"
msgstr ""
@@ -2502,61 +2766,70 @@ msgid "Transaction %{transaction}, %{subnetwork} %{transaction}"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:391
+#: lib/block_scout_web/templates/transaction/overview.html.eex:412
msgid "Transaction Burnt Fee"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:40
+#: lib/block_scout_web/templates/transaction/overview.html.eex:49
msgid "Transaction Details"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:317
+#: lib/block_scout_web/templates/transaction/overview.html.eex:338
msgid "Transaction Fee"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:65
+#: lib/block_scout_web/templates/transaction/overview.html.eex:74
msgid "Transaction Hash"
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:341
+#: lib/block_scout_web/templates/transaction/overview.html.eex:362
msgid "Transaction Type"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:414
+#: lib/block_scout_web/templates/transaction/overview.html.eex:435
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:340
+#: lib/block_scout_web/templates/transaction/overview.html.eex:361
msgid "Transaction type, introduced in EIP-2718."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:7
-#: lib/block_scout_web/templates/address_transaction/index.html.eex:17 lib/block_scout_web/templates/block/overview.html.eex:74
-#: lib/block_scout_web/templates/block_transaction/index.html.eex:10 lib/block_scout_web/templates/chain/show.html.eex:236
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:41 lib/block_scout_web/views/address_view.ex:347
+#: lib/block_scout_web/templates/address/overview.html.eex:203 lib/block_scout_web/templates/address/overview.html.eex:209
+#: lib/block_scout_web/templates/address/overview.html.eex:217 lib/block_scout_web/templates/address_transaction/index.html.eex:13
+#: lib/block_scout_web/templates/block/overview.html.eex:80 lib/block_scout_web/templates/block_transaction/index.html.eex:10
+#: lib/block_scout_web/templates/chain/show.html.eex:236 lib/block_scout_web/templates/layout/_topnav.html.eex:43
+#: lib/block_scout_web/views/address_view.ex:347
msgid "Transactions"
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/address/overview.html.eex:119
+msgid "Transactions and address of creation."
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/address/_tile.html.eex:31
msgid "Transactions sent"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:50
+#: lib/block_scout_web/templates/address/overview.html.eex:230
+#: lib/block_scout_web/templates/address/overview.html.eex:236 lib/block_scout_web/templates/address/overview.html.eex:244
+#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:50 lib/block_scout_web/templates/tokens/overview/_details.html.eex:133
msgid "Transfers"
msgstr ""
@@ -2567,7 +2840,7 @@ msgid "Try it out"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_footer.html.eex:25
+#: lib/block_scout_web/templates/layout/_footer.html.eex:27
msgid "Twitter"
msgstr ""
@@ -2578,18 +2851,23 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:92
-#: lib/block_scout_web/templates/layout/app.html.eex:46
+#: lib/block_scout_web/templates/layout/app.html.eex:52
msgid "Tx/day"
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:440
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:155
+msgid "Type of the token standard"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/transaction/overview.html.eex:461
msgid "UTF-8"
msgstr ""
@@ -2604,23 +2882,23 @@ msgid "Uncle Reward"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:244
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:29
+#: lib/block_scout_web/templates/block/overview.html.eex:250
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:31
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 ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:64
+#: lib/block_scout_web/templates/transaction/overview.html.eex:73
msgid "Unique character string (TxID) assigned to every verified transaction."
msgstr ""
@@ -2647,22 +2925,22 @@ 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:370
+#: lib/block_scout_web/templates/transaction/overview.html.eex:391
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:380
+#: lib/block_scout_web/templates/transaction/overview.html.eex:401
msgid "User-defined tip sent to validator for transaction priority/inclusion."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:218
+#: lib/block_scout_web/templates/block/overview.html.eex:224
msgid "User-defined tips sent to validator for transaction priority/inclusion."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:45
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:47
msgid "Validated"
msgstr ""
@@ -2686,6 +2964,11 @@ msgstr ""
msgid "Validator Info"
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/address/overview.html.eex:104
+msgid "Validator Name"
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/stakes/_table.html.eex:8
msgid "Validator Pool Addresses."
@@ -2697,45 +2980,62 @@ 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:302
+#: lib/block_scout_web/templates/transaction/overview.html.eex:323
msgid "Value"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:301
+#: lib/block_scout_web/templates/transaction/overview.html.eex:322
msgid "Value sent in the native token (and USD) if applicable."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:74
+#: lib/block_scout_web/templates/address_contract/index.html.eex:82
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:158 lib/block_scout_web/templates/address_contract/index.html.eex:170
-#: lib/block_scout_web/templates/address_contract/index.html.eex:201 lib/block_scout_web/templates/address_contract/index.html.eex:213
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:19
+#: lib/block_scout_web/templates/address_contract/index.html.eex:27
+#: lib/block_scout_web/templates/address_contract/index.html.eex:29 lib/block_scout_web/templates/address_contract/index.html.eex:164
+#: lib/block_scout_web/templates/address_contract/index.html.eex:170 lib/block_scout_web/templates/address_contract/index.html.eex:201
+#: lib/block_scout_web/templates/address_contract/index.html.eex:207 lib/block_scout_web/templates/smart_contract/_functions.html.eex:14
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_via_flattened_code/new.html.eex:227
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:44
+#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:56
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:81
msgid "Verify & publish"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:16
-#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:18 lib/block_scout_web/templates/transaction_log/_logs.html.eex:16
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:10
+#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:12 lib/block_scout_web/templates/transaction_log/_logs.html.eex:10
msgid "Verify the contract "
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_footer.html.eex:86
+#: lib/block_scout_web/templates/layout/_footer.html.eex:91
msgid "Version"
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:42
+msgid "Via Sourcify: Sources and metadata JSON file"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:36
+msgid "Via Standard Input JSON"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:31
+msgid "Via flattened source code"
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:175
msgid "View All Blocks"
@@ -2748,20 +3048,30 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:16
-#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:20 lib/block_scout_web/templates/tokens/overview/_details.html.eex:77
+#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:20
msgid "View Contract"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/_tile.html.eex:61
+#: lib/block_scout_web/templates/transaction/_tile.html.eex:67
msgid "View Less Transfers"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/_tile.html.eex:60
+#: lib/block_scout_web/templates/transaction/_tile.html.eex:66
msgid "View More Transfers"
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/block/overview.html.eex:39
+msgid "View next block"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/block/overview.html.eex:23
+msgid "View previous block"
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/address/_metatags.html.eex:9
msgid "View the account balance, transactions, and other data for %{address} on the %{network}"
@@ -2778,12 +3088,12 @@ msgid "View transaction %{transaction} on %{subnetwork}"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:130
-msgid "EVMOS"
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:48
+msgid "Vyper contract"
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 ""
@@ -2792,14 +3102,19 @@ msgstr ""
msgid "Wallet addresses"
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/common_components/_changed_bytecode_warning.html.eex:3
+msgid "Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky."
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/stakes/_stakes_modal_claim_reward_content.html.eex:2
msgid "We found the following pools you can claim reward from:"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/views/wei_helpers.ex:76
-msgid "Evmos"
+#: lib/block_scout_web/views/wei_helpers.ex:76 lib/block_scout_web/templates/smart_contract/_functions.html.eex:134
+msgid "atevmos"
msgstr ""
#, elixir-format
@@ -2828,26 +3143,26 @@ msgid "Working Stake Amount"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:99
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:98
msgid "Write"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:95
-#: lib/block_scout_web/views/address_view.ex:353
+#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:34 lib/block_scout_web/views/address_view.ex:353
msgid "Write Contract"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:102
-#: lib/block_scout_web/views/address_view.ex:354
+#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:48 lib/block_scout_web/views/address_view.ex:354
msgid "Write Proxy"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:60
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:103
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:146 lib/block_scout_web/templates/stakes/_rows.html.eex:24
+#: lib/block_scout_web/templates/address_contract_verification_common_fields/_fetch_constructor_args.html.eex:14
+#: lib/block_scout_web/templates/address_contract_verification_common_fields/_include_nightly_builds_field.html.eex:14
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:61 lib/block_scout_web/templates/stakes/_rows.html.eex:24
msgid "Yes"
msgstr ""
@@ -2918,12 +3233,27 @@ msgid "all epochs"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address/overview.html.eex:116
+#: lib/block_scout_web/templates/address/overview.html.eex:129
msgid "at"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:24
+#: lib/block_scout_web/templates/address_token/overview.html.eex:52
+msgid "balance of the address"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/transaction/overview.html.eex:411
+msgid "burned for this transaction. Equals Block Base Fee per Gas * Gas Used."
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/block/overview.html.eex:215
+msgid "burned from transactions included in the block (Base fee (per unit of gas) * Gas Used)."
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract/index.html.eex:27
msgid "button"
msgstr ""
@@ -2933,7 +3263,7 @@ msgid "candidate"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:207
+#: lib/block_scout_web/templates/transaction/overview.html.eex:228
msgid "created"
msgstr ""
@@ -2943,7 +3273,12 @@ msgid "custom RPC"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:43
+#: lib/block_scout_web/templates/common_components/_rap_pagination_container.html.eex:13
+msgid "elements are displayed"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:41
msgid "fallback"
msgstr ""
@@ -2953,8 +3288,8 @@ msgid "false"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:16
-#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:18 lib/block_scout_web/templates/transaction_log/_logs.html.eex:16
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:10
+#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:12 lib/block_scout_web/templates/transaction_log/_logs.html.eex:10
msgid "here"
msgstr ""
@@ -2968,13 +3303,18 @@ 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"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:21
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:16
msgid "page"
msgstr ""
@@ -2984,7 +3324,7 @@ msgid "pool owner"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:45
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:43
msgid "receive"
msgstr ""
@@ -3017,26 +3357,6 @@ msgid "validator"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:209
-msgid "xDai burned from transactions included in the block (Base fee (per unit of gas) * Gas Used)."
-msgstr ""
-
-#, elixir-format
-#: lib/block_scout_web/templates/stakes/_stakes_modal_withdraw.html.eex:76
-msgid "
Pending stake (stake placed on a candidate pool or placed during the current staking epoch) may be withdrawn now.
\n
Active stake (stake available after the current epoch) can be ordered for withdrawal from the pool, and will be available to claim after the current staking epoch is complete.
\n
If you have already ordered (and the staking window is still open), you may increase your current order by entering a positive value, or decrease your current order by entering a negative value in the box and clicking 'Order Withdrawal'. You must either keep the minimum stake amount in the pool, or order your entire stake for withdrawal.
To become a candidate, your staking address must be funded with %{tokenSymbol} tokens and %{coinSymbol} coins, and your OpenEthereum node must be active and configured with the mining address you specify here.
\n
To become a delegator, close this window and select an address from the list of pools you would like to place stake on. Click the Stake button next to the address to begin the process.
Pending stake (stake placed on a candidate pool or placed during the current staking epoch) may be withdrawn now.
\n
Active stake (stake available after the current epoch) can be ordered for withdrawal from the pool, and will be available to claim after the current staking epoch is complete.
\n
If you have already ordered (and the staking window is still open), you may increase your current order by entering a positive value, or decrease your current order by entering a negative value in the box and clicking 'Order Withdrawal'. You must either keep the minimum stake amount in the pool, or order your entire stake for withdrawal.
To become a candidate, your staking address must be funded with %{tokenSymbol} tokens and %{coinSymbol} coins, and your OpenEthereum node must be active and configured with the mining address you specify here.
\n
To become a delegator, close this window and select an address from the list of pools you would like to place stake on. Click the Stake button next to the address to begin the process.
"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/block/overview.html.eex:97
msgid "A block producer who successfully included the block onto the blockchain."
msgstr ""
@@ -124,7 +134,8 @@ msgid "A string with the name of the module to be invoked."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:156
+#: lib/block_scout_web/templates/address_contract_verification_common_fields/_constructor_args.html.eex:3
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:62
msgid "ABI-encoded Constructor Arguments (if required by the contract)"
msgstr ""
@@ -144,7 +155,7 @@ msgid "API for the %{subnetwork} - BlockScout"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:122
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:124
msgid "APIs"
msgstr ""
@@ -164,47 +175,62 @@ msgid "Action"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:404
+#: lib/block_scout_web/templates/transaction/overview.html.eex:425
msgid "Actual gas amount used by the transaction."
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/layout/_footer.html.eex:44
+msgid "Add"
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:16
-#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:26 lib/block_scout_web/views/address_view.ex:104
+#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:20 lib/block_scout_web/views/address_view.ex:104
msgid "Address"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:192
+#: 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:172
+#: lib/block_scout_web/templates/transaction/overview.html.eex:193
msgid "Address (external or contract) sending the transaction."
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/address/overview.html.eex:166
+msgid "Address balance in xDAI (doesn't include ERC20, ERC721, ERC1155 tokens)."
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:65
+msgid "Address of the token contract"
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/address/index.html.eex:5
msgid "Addresses"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:30
-#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:28 lib/block_scout_web/templates/address_transaction/index.html.eex:26
-#: lib/block_scout_web/templates/layout/_network_selector.html.eex:21 lib/block_scout_web/templates/layout/_topnav.html.eex:71
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:103 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
+#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:26
+#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:28 lib/block_scout_web/templates/address_transaction/index.html.eex:22
+#: lib/block_scout_web/templates/layout/_network_selector.html.eex:21 lib/block_scout_web/templates/layout/_topnav.html.eex:73
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:105 lib/block_scout_web/views/address_internal_transaction_view.ex:11
+#: lib/block_scout_web/views/address_token_transfer_view.ex:11 lib/block_scout_web/views/address_transaction_view.ex:11
msgid "All"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:18
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:13
msgid "All functions displayed below are from ABI of that contract. In order to verify current contract, proceed with"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:24
+#: lib/block_scout_web/templates/address_contract/index.html.eex:27
msgid "All metadata displayed below is from that contract. In order to verify current contract, click"
msgstr ""
@@ -213,6 +239,11 @@ msgstr ""
msgid "All pool participant addresses. The top address belongs to the %{pool_type}."
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/address/overview.html.eex:191
+msgid "All tokens in the account and total value."
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/stakes/_stakes_modal_withdraw.html.eex:27
msgid "Already Ordered:"
@@ -225,18 +256,18 @@ msgid "Amount"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:41
-msgid "Amount of %{symbol} placed by an address."
+#: lib/block_scout_web/templates/transaction/overview.html.eex:411
+msgid "Amount of"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:230
-msgid "Amount of distributed reward. Miners receive a static block reward + Tx fees + uncle fees."
+#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:41
+msgid "Amount of %{symbol} placed by an address."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:390
-msgid "Amount of xDai burned for this transaction. Equals Block Base Fee per Gas * Gas Used."
+#: lib/block_scout_web/templates/block/overview.html.eex:236
+msgid "Amount of distributed reward. Miners receive a static block reward + Tx fees + uncle fees."
msgstr ""
#, elixir-format
@@ -255,7 +286,7 @@ msgid "Approximate Current Annual Percentage Yield. If you see N/A, please wait
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:150
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:152
msgid "Apps"
msgstr ""
@@ -276,12 +307,13 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_balance_card.html.eex:4
+#: lib/block_scout_web/templates/address/overview.html.eex:167 lib/block_scout_web/templates/address_token/overview.html.eex:51
#: lib/block_scout_web/templates/stakes/_stakes_stats_item_account.html.eex:42 lib/block_scout_web/templates/stakes/_stakes_stats_item_account.html.eex:43
msgid "Balance"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:18
+#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:14
msgid "Balances"
msgstr ""
@@ -296,7 +328,7 @@ msgid "Banned until block #%{banned_until} (%{estimated_unban_day})"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:201
+#: lib/block_scout_web/templates/block/overview.html.eex:207
msgid "Base Fee per Gas"
msgstr ""
@@ -314,19 +346,19 @@ msgid "Become a Candidate"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:425
+#: lib/block_scout_web/templates/transaction/overview.html.eex:446
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:131
+#: lib/block_scout_web/templates/block/overview.html.eex:29 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:32 lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:43
msgid "Block #%{number}"
msgstr ""
@@ -341,12 +373,12 @@ msgid "Block Details"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:47
+#: lib/block_scout_web/templates/block/overview.html.eex:53
msgid "Block Height"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/app.html.eex:40
+#: lib/block_scout_web/templates/layout/app.html.eex:46
msgid "Block Mined, awaiting import..."
msgstr ""
@@ -356,7 +388,7 @@ msgid "Block Pending"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:152
+#: lib/block_scout_web/templates/block/overview.html.eex:158
msgid "Block difficulty for miner, used to calibrate block generation time (Note: constant in POA based networks)."
msgstr ""
@@ -371,10 +403,15 @@ msgid "Block number"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:130
+#: 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:274
+msgid "Block number in which the address was updated."
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/chain/_metatags.html.eex:4
msgid "BlockScout provides analytics data, API, and Smart Contract tools for the %{subnetwork}"
@@ -382,23 +419,24 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:176
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:22 lib/block_scout_web/templates/layout/_topnav.html.eex:26
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:24 lib/block_scout_web/templates/layout/_topnav.html.eex:28
msgid "Blocks"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/app.html.eex:39
+#: lib/block_scout_web/templates/layout/app.html.eex:45
msgid "Blocks Indexed"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:48
-#: lib/block_scout_web/templates/address_validation/index.html.eex:15 lib/block_scout_web/views/address_view.ex:356
+#: lib/block_scout_web/templates/address/overview.html.eex:291 lib/block_scout_web/templates/address_validation/index.html.eex:11
+#: lib/block_scout_web/views/address_view.ex:356
msgid "Blocks Validated"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_footer.html.eex:20
+#: lib/block_scout_web/templates/layout/_footer.html.eex:22
msgid "Blockscout is a tool for inspecting and analyzing EVM based blockchains. Blockchain explorer for Ethereum Networks."
msgstr ""
@@ -413,22 +451,33 @@ msgid "Bridged Tokens from "
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:82
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:84
msgid "Bridged from BSC"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:76
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:78
msgid "Bridged from Ethereum"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/_tile.html.eex:64
-#: lib/block_scout_web/templates/block/overview.html.eex:210
+#: lib/block_scout_web/templates/block/overview.html.eex:216
msgid "Burnt Fees"
msgstr ""
#, elixir-format
+#: lib/block_scout_web/templates/address_token/overview.html.eex:65
+msgid "CRC Worth"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/common_components/_csv_export_button.html.eex:2
+msgid "CSV"
+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 ""
@@ -439,9 +488,11 @@ msgid "Call Code"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:91
-#: 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/api_docs/_action_tile.html.eex:47
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:114
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:231
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:48
+#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:60
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:85 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 ""
@@ -462,8 +513,8 @@ msgid "Change Network"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_footer.html.eex:39
-msgid "Chat"
+#: lib/block_scout_web/templates/layout/_footer.html.eex:41
+msgid "Chat (#blockscout)"
msgstr ""
#, elixir-format
@@ -530,78 +581,89 @@ msgid "Coin Balance History"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:71
+#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:55
+msgid "Collapse"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification_common_fields/_compiler_field.html.eex:3
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:40
msgid "Compiler"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:57
+#: lib/block_scout_web/templates/address_contract/index.html.eex:65
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 ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:109
+#: lib/block_scout_web/templates/transaction/overview.html.eex:118
msgid "Confirmed by "
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:163
+#: lib/block_scout_web/templates/transaction/overview.html.eex:184
msgid "Confirmed within"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:4
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:13
-#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:4 lib/block_scout_web/templates/tokens/holder/index.html.eex:17
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:2
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:11
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:2
+#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:9
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:6 lib/block_scout_web/templates/tokens/holder/index.html.eex:15
msgid "Connection Lost"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:14
-#: lib/block_scout_web/templates/block/index.html.eex:7
+#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:12
+#: lib/block_scout_web/templates/block/index.html.eex:5
msgid "Connection Lost, click to load newer blocks"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:17
+#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:15
msgid "Connection Lost, click to load newer internal transactions"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_transaction/index.html.eex:13
-#: lib/block_scout_web/templates/pending_transaction/index.html.eex:18 lib/block_scout_web/templates/transaction/index.html.eex:24
+#: lib/block_scout_web/templates/address_transaction/index.html.eex:11
+#: lib/block_scout_web/templates/pending_transaction/index.html.eex:16 lib/block_scout_web/templates/transaction/index.html.eex:22
msgid "Connection Lost, click to load newer transactions"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_validation/index.html.eex:12
+#: lib/block_scout_web/templates/address_validation/index.html.eex:10
msgid "Connection Lost, click to load newer validations"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:82
+#: lib/block_scout_web/templates/address_contract/index.html.eex:90
msgid "Constructor Arguments"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:202
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:66
+#: 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:120
+#: lib/block_scout_web/templates/address_contract/index.html.eex:126
msgid "Contract ABI"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:18
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:27
-#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:13 lib/block_scout_web/views/address_view.ex:102
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:14
+#: lib/block_scout_web/templates/address_contract_verification_common_fields/_contract_address_field.html.eex:3
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:23
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:9
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:18 lib/block_scout_web/views/address_view.ex:102
msgid "Contract Address"
msgstr ""
@@ -612,73 +674,75 @@ 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:136
-#: lib/block_scout_web/templates/address_contract/index.html.eex:151
+#: lib/block_scout_web/templates/address_contract/index.html.eex:142
+#: lib/block_scout_web/templates/address_contract/index.html.eex:157
msgid "Contract Creation Code"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:170
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:100
msgid "Contract Libraries"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:38
+#: lib/block_scout_web/templates/address/overview.html.eex:93
+#: lib/block_scout_web/templates/address_contract_verification_common_fields/_contract_name_field.html.eex:3
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:29
msgid "Contract Name"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:22
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:16
+#: lib/block_scout_web/templates/address_contract/index.html.eex:25
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:11
msgid "Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:49
+#: lib/block_scout_web/templates/address_contract/index.html.eex:57
msgid "Contract name:"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:92
+#: lib/block_scout_web/templates/address_contract/index.html.eex:100
msgid "Contract source code"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:142
+#: lib/block_scout_web/templates/address_contract/index.html.eex:148
msgid "Contracts that self destruct in their constructors have no contract code published and cannot be verified."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_footer.html.eex:38
+#: lib/block_scout_web/templates/layout/_footer.html.eex:40
msgid "Contribute"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:122
+#: lib/block_scout_web/templates/address_contract/index.html.eex:128
msgid "Copy ABI"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:37
-#: lib/block_scout_web/templates/address/overview.html.eex:38 lib/block_scout_web/templates/block/overview.html.eex:98
-#: lib/block_scout_web/templates/block/overview.html.eex:99 lib/block_scout_web/templates/tokens/overview/_details.html.eex:50
+#: lib/block_scout_web/templates/address/overview.html.eex:38 lib/block_scout_web/templates/block/overview.html.eex:104
+#: lib/block_scout_web/templates/block/overview.html.eex:105 lib/block_scout_web/templates/tokens/overview/_details.html.eex:50
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:51
msgid "Copy Address"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:138
-#: lib/block_scout_web/templates/address_contract/index.html.eex:154
+#: lib/block_scout_web/templates/address_contract/index.html.eex:144
+#: lib/block_scout_web/templates/address_contract/index.html.eex:160
msgid "Copy Contract Creation Code"
msgstr ""
@@ -695,14 +759,14 @@ 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:182
-#: lib/block_scout_web/templates/transaction/overview.html.eex:183
+#: 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 ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:123
-#: lib/block_scout_web/templates/block/overview.html.eex:124
+#: lib/block_scout_web/templates/block/overview.html.eex:129
+#: lib/block_scout_web/templates/block/overview.html.eex:130
msgid "Copy Hash"
msgstr ""
@@ -712,8 +776,8 @@ msgid "Copy Metadata"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:143
-#: lib/block_scout_web/templates/block/overview.html.eex:144
+#: lib/block_scout_web/templates/block/overview.html.eex:149
+#: lib/block_scout_web/templates/block/overview.html.eex:150
msgid "Copy Parent Hash"
msgstr ""
@@ -723,16 +787,16 @@ msgid "Copy Raw Trace"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:94
-#: lib/block_scout_web/templates/address_contract/index.html.eex:108
+#: lib/block_scout_web/templates/address_contract/index.html.eex:102
+#: lib/block_scout_web/templates/address_contract/index.html.eex:115
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:211
-#: lib/block_scout_web/templates/transaction/overview.html.eex:212 lib/block_scout_web/templates/transaction/overview.html.eex:221
-#: lib/block_scout_web/templates/transaction/overview.html.eex:222
+#: 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 ""
@@ -743,29 +807,29 @@ msgid "Copy Token ID"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:72
+#: lib/block_scout_web/templates/transaction/overview.html.eex:81
msgid "Copy Transaction Hash"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:73
+#: lib/block_scout_web/templates/transaction/overview.html.eex:82
msgid "Copy Txn Hash"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:451
+#: lib/block_scout_web/templates/transaction/overview.html.eex:472
msgid "Copy Txn Hex Input"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:457
+#: lib/block_scout_web/templates/transaction/overview.html.eex:478
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:450
-#: lib/block_scout_web/templates/transaction/overview.html.eex:456
+#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:41 lib/block_scout_web/templates/transaction/overview.html.eex:471
+#: lib/block_scout_web/templates/transaction/overview.html.eex:477
msgid "Copy Value"
msgstr ""
@@ -780,8 +844,8 @@ msgid "Create2"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address/overview.html.eex:111
-msgid "Created by"
+#: lib/block_scout_web/templates/address/overview.html.eex:120
+msgid "Creator"
msgstr ""
#, elixir-format
@@ -812,7 +876,7 @@ msgid "Current Stake:"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:82
+#: lib/block_scout_web/templates/transaction/overview.html.eex:91
msgid "Current transaction state: Success, Failed (Error), or Pending (In Process)"
msgstr ""
@@ -822,33 +886,33 @@ msgid "DApp for Staking %{symbol} tokens"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:107
-#: 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/transaction_log/_logs.html.eex:110
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:101
+#: 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 ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:64
+#: lib/block_scout_web/templates/block/overview.html.eex:70
msgid "Date & time at which block was produced."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:149
+#: lib/block_scout_web/templates/transaction/overview.html.eex:170
msgid "Date & time of transaction inclusion, including length of time for confirmation."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:52
-#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:85
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:145
msgid "Decimals"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:38
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:44 lib/block_scout_web/templates/address_logs/_logs.html.eex:59
-#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:39 lib/block_scout_web/templates/transaction_log/_logs.html.eex:47
-#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:62
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:32
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:38 lib/block_scout_web/templates/address_logs/_logs.html.eex:53
+#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:33 lib/block_scout_web/templates/transaction_log/_logs.html.eex:41
+#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:56 lib/block_scout_web/templates/transaction_log/_logs.html.eex:72
msgid "Decoded"
msgstr ""
@@ -913,20 +977,30 @@ msgid "Details"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:153
+#: lib/block_scout_web/templates/block/overview.html.eex:159
msgid "Difficulty"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:143
+#: lib/block_scout_web/templates/address_contract/index.html.eex:149
msgid "Displaying the init data provided of the creating transaction."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:30
+#: lib/block_scout_web/templates/csv_export/index.html.eex:25
+msgid "Download"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:26
msgid "Drop sources and metadata JSON file or click here"
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:33
+msgid "Drop the standard input JSON file or click here"
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/transaction/not_found.html.eex:22
msgid "During times when the network is busy (i.e during ICOs) it can take a while for your transaction to propagate through the network and for us to index it."
@@ -938,18 +1012,24 @@ 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 ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:95
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:95 lib/block_scout_web/templates/smart_contract/_functions.html.eex:131
+#: lib/block_scout_web/templates/address_token/overview.html.eex:1
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:93 lib/block_scout_web/templates/smart_contract/_functions.html.eex:93
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:135
msgid "ETH"
msgstr ""
@@ -959,8 +1039,8 @@ msgid "ETH RPC API Documentation"
msgstr ""
#, elixir-format
-#: 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
+#: lib/block_scout_web/templates/address_contract/index.html.eex:76
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:40
msgid "EVM Version"
msgstr ""
@@ -980,10 +1060,15 @@ msgid "Emission Reward"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:124
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:82
msgid "Enter the Solidity Contract Code"
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:51
+msgid "Enter the Vyper Contract Code"
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/stakes/_stakes_top.html.eex:4
msgid "Epoch number"
@@ -995,42 +1080,51 @@ 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/internal_transaction/_tile.html.eex:11
+#: 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_card.html.eex:31
+#: 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 ""
#, elixir-format
-#: lib/block_scout_web/templates/address/overview.html.eex:124
+#: lib/block_scout_web/templates/address/overview.html.eex:137
msgid "Error: Could not determine contract creator."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:136
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:138
msgid "Eth RPC"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_balance_card.html.eex:18
-#: lib/block_scout_web/templates/address/index.html.eex:5 lib/block_scout_web/templates/internal_transaction/_tile.html.eex:20
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:87 lib/block_scout_web/templates/layout/_topnav.html.eex:108
-#: lib/block_scout_web/templates/layout/app.html.eex:45 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/views/wei_helpers.ex:78
-msgid "Evmos"
+#: lib/block_scout_web/templates/address/index.html.eex:5 lib/block_scout_web/templates/address/overview.html.eex:180
+#: lib/block_scout_web/templates/block/overview.html.eex:215 lib/block_scout_web/templates/internal_transaction/_tile.html.eex:24
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:89 lib/block_scout_web/templates/layout/_topnav.html.eex:110
+#: lib/block_scout_web/templates/layout/app.html.eex:51 lib/block_scout_web/templates/transaction/_pending_tile.html.eex:20
+#: lib/block_scout_web/templates/transaction/_tile.html.eex:43 lib/block_scout_web/templates/transaction/overview.html.eex:411
+#: lib/block_scout_web/views/wei_helpers.ex:78
+msgid "tevmos"
msgstr ""
#, elixir-format
@@ -1046,18 +1140,28 @@ msgid "Execute"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:234
+#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:55
+msgid "Expand"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/csv_export/index.html.eex:10
+msgid "Export Data"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract/index.html.eex:228
msgid "External libraries"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:39
+#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:40
msgid "Failed to decode input data."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:41
-#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:42
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:35
+#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:36
msgid "Failed to decode log data."
msgstr ""
@@ -1067,13 +1171,31 @@ msgid "Favorites"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address/_balance_card.html.eex:32
+#: lib/block_scout_web/templates/address/overview.html.eex:264
+msgid "Fetching gas used..."
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:126
+msgid "Fetching holders..."
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address/_balance_card.html.eex:28
+#: lib/block_scout_web/templates/address/_balance_dropdown.html.eex:7
msgid "Fetching tokens..."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:35
-msgid "Flattened source code"
+#: lib/block_scout_web/templates/address/overview.html.eex:211
+#: lib/block_scout_web/templates/address/overview.html.eex:219
+msgid "Fetching transactions..."
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address/overview.html.eex:238
+#: lib/block_scout_web/templates/address/overview.html.eex:246 lib/block_scout_web/templates/tokens/overview/_details.html.eex:137
+msgid "Fetching transfers..."
msgstr ""
#, elixir-format
@@ -1082,15 +1204,20 @@ msgid "For any existing contracts in the database, insert all ABI entries into t
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:32
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:34
msgid "Forked Blocks (Reorgs)"
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:173 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
+#: lib/block_scout_web/templates/layout/_footer.html.eex:42
+msgid "Forum"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:38
+#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:40 lib/block_scout_web/templates/address_transaction/index.html.eex:34
+#: 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 ""
@@ -1101,58 +1228,73 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/_tile.html.eex:67
-#: lib/block_scout_web/templates/block/overview.html.eex:181 lib/block_scout_web/templates/transaction/overview.html.eex:352
+#: lib/block_scout_web/templates/block/overview.html.eex:187 lib/block_scout_web/templates/transaction/overview.html.eex:373
msgid "Gas Limit"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:332
+#: lib/block_scout_web/templates/transaction/overview.html.eex:353
msgid "Gas Price"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/_tile.html.eex:73
-#: lib/block_scout_web/templates/block/overview.html.eex:172
+#: lib/block_scout_web/templates/address/overview.html.eex:257
+#: lib/block_scout_web/templates/block/_tile.html.eex:73 lib/block_scout_web/templates/block/overview.html.eex:178
msgid "Gas Used"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:405
+#: lib/block_scout_web/templates/transaction/overview.html.eex:426
msgid "Gas Used by Transaction"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:54
+#: lib/block_scout_web/templates/address/overview.html.eex:256
+msgid "Gas used by the address."
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/block/overview.html.eex:60
msgid "Genesis Block"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_footer.html.eex:22
+#: lib/block_scout_web/templates/layout/_footer.html.eex:24
msgid "Github"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:126
+#: lib/block_scout_web/templates/common_components/_rap_pagination_container.html.eex:8
+msgid "Go to"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:128
msgid "GraphQL"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:52
#: lib/block_scout_web/views/block_view.ex:21 lib/block_scout_web/views/wei_helpers.ex:77
-msgid "MicroEvmos"
+msgid "ntevmos"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:117
+#: lib/block_scout_web/templates/block/overview.html.eex:123
msgid "Hash"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:433
-#: lib/block_scout_web/templates/transaction/overview.html.eex:437
+#: lib/block_scout_web/templates/transaction/overview.html.eex:454
+#: lib/block_scout_web/templates/transaction/overview.html.eex:458
msgid "Hex (Default)"
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:122
+msgid "Holders"
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/stakes/_stakes_modal_pool_info.html.eex:77
msgid "How Many Times this Address has been Banned"
@@ -1169,13 +1311,13 @@ msgid "However, in general, the"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:25
+#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:19
msgid "IMPORTANT: This information is a best guess based on similar functions from other verified contracts."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:38
-#: lib/block_scout_web/templates/transaction/_tile.html.eex:80
+#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:42
+#: lib/block_scout_web/templates/transaction/_tile.html.eex:86
msgid "IN"
msgstr ""
@@ -1190,8 +1332,13 @@ msgid "If you have just submitted this transaction please wait for at least 30 s
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address/overview.html.eex:65
-msgid "Implementation:"
+#: lib/block_scout_web/templates/address/overview.html.eex:149
+msgid "Implementation"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address/overview.html.eex:148
+msgid "Implementation address of the proxy contract."
msgstr ""
#, elixir-format
@@ -1200,12 +1347,12 @@ msgid "Inactive Pool Addresses. Current validator pools are specified by a check
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:417
+#: lib/block_scout_web/templates/transaction/overview.html.eex:438
msgid "Index position of Transaction in the block."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:243
+#: lib/block_scout_web/templates/block/overview.html.eex:249
msgid "Index position(s) of referenced stale blocks."
msgstr ""
@@ -1215,7 +1362,7 @@ msgid "Indexed?"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/app.html.eex:41
+#: lib/block_scout_web/templates/layout/app.html.eex:47
msgid "Indexing Tokens"
msgstr ""
@@ -1225,20 +1372,20 @@ msgid "Input"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:194
+#: lib/block_scout_web/templates/transaction/overview.html.eex:215
msgid "Interacted With (To)"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:6
+#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:7
msgid "Internal Transaction"
msgstr ""
#, elixir-format
#: 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/address_internal_transaction/index.html.eex:17 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:346
-#: lib/block_scout_web/views/transaction_view.ex:515
+#: lib/block_scout_web/views/transaction_view.ex:512
msgid "Internal Transactions"
msgstr ""
@@ -1249,7 +1396,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/inventory/index.html.eex:15
-#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:17 lib/block_scout_web/views/tokens/overview_view.ex:44
+#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:19 lib/block_scout_web/views/tokens/overview_view.ex:44
msgid "Inventory"
msgstr ""
@@ -1269,30 +1416,30 @@ msgid "JSON RPC error"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address/overview.html.eex:96
-msgid "Last Balance Update: Block #"
+#: lib/block_scout_web/templates/address/overview.html.eex:275
+msgid "Last Balance Update"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/app.html.eex:42
+#: lib/block_scout_web/templates/layout/app.html.eex:48
msgid "Less than"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:185
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:207
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:229
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:251
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:273
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:115
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:137
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:159
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:181
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:203
msgid "Library Address"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:175
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:197
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:219
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:241
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:263
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:105
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:127
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:149
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:171
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:193
msgid "Library Name"
msgstr ""
@@ -1312,51 +1459,48 @@ msgid "Likelihood of Becoming a Validator on the Next Epoch"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:284
+#: 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:268
+#: 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:251
+#: 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:235
+#: 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:18
+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:79
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:225
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:42
+#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:54
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:79 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/contract/index.html.eex:16
msgid "Loading..."
msgstr ""
-#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:72
-#: 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
-msgid "Loading...."
-msgstr ""
-
#, elixir-format
#: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:2
msgid "Log Data"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:120
+#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:130
msgid "Log Index"
msgstr ""
@@ -1364,7 +1508,7 @@ msgstr ""
#: lib/block_scout_web/templates/address/_tabs.html.eex:41
#: 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:357
-#: lib/block_scout_web/views/transaction_view.ex:516
+#: lib/block_scout_web/views/transaction_view.ex:513
msgid "Logs"
msgstr ""
@@ -1374,7 +1518,7 @@ msgid "ME"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_footer.html.eex:47
+#: lib/block_scout_web/templates/layout/_footer.html.eex:52
msgid "Main Networks"
msgstr ""
@@ -1385,7 +1529,8 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:61
-#: lib/block_scout_web/templates/layout/app.html.eex:43 lib/block_scout_web/views/address_view.ex:142
+#: lib/block_scout_web/templates/layout/app.html.eex:49 lib/block_scout_web/templates/tokens/overview/_details.html.eex:98
+#: lib/block_scout_web/views/address_view.ex:142
msgid "Market Cap"
msgstr ""
@@ -1395,44 +1540,44 @@ msgid "Max Amount to Move:"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:361
+#: lib/block_scout_web/templates/transaction/overview.html.eex:382
msgid "Max Fee per Gas"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:371
+#: lib/block_scout_web/templates/transaction/overview.html.eex:392
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:351
+#: lib/block_scout_web/templates/transaction/overview.html.eex:372
msgid "Maximum gas amount approved for the transaction."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:360
+#: lib/block_scout_web/templates/transaction/overview.html.eex:381
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:187
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 ""
#, elixir-format
#: lib/block_scout_web/templates/block/_tile.html.eex:41
-#: lib/block_scout_web/templates/block/overview.html.eex:92 lib/block_scout_web/templates/chain/_block.html.eex:16
+#: lib/block_scout_web/templates/block/overview.html.eex:98 lib/block_scout_web/templates/chain/_block.html.eex:16
msgid "Block Proposer"
msgstr ""
@@ -1458,7 +1603,7 @@ msgid "Minimum Stake:"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:200
+#: lib/block_scout_web/templates/block/overview.html.eex:206
msgid "Minimum fee required per unit of gas. Fee adjusts based on network congestion."
msgstr ""
@@ -1483,7 +1628,7 @@ msgid "More internal transactions have come in"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_transaction/index.html.eex:50
+#: lib/block_scout_web/templates/address_transaction/index.html.eex:46
#: lib/block_scout_web/templates/chain/show.html.eex:239 lib/block_scout_web/templates/pending_transaction/index.html.eex:13
#: lib/block_scout_web/templates/transaction/index.html.eex:19
msgid "More transactions have come in"
@@ -1507,23 +1652,44 @@ msgstr ""
msgid "N/A"
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/block/overview.html.eex:116
+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 ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:9
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:18
-#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:9
+#: lib/block_scout_web/templates/address_token/overview.html.eex:44
+msgid "Net Worth"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:5
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:5
+#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:14
msgid "New Smart Contract Verification"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:75
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:14
+msgid "New Solidity Smart Contract Verification"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:9
+msgid "New Vyper Smart Contract Verification"
+msgstr ""
+
+#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:82
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:89 lib/block_scout_web/templates/address_contract_verification/new.html.eex:97
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:105
msgid "Next"
msgstr ""
@@ -1533,9 +1699,9 @@ msgid "Next epoch in"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:55
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:98
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:141 lib/block_scout_web/templates/stakes/_rows.html.eex:24
+#: lib/block_scout_web/templates/address_contract_verification_common_fields/_fetch_constructor_args.html.eex:9
+#: lib/block_scout_web/templates/address_contract_verification_common_fields/_include_nightly_builds_field.html.eex:9
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:56 lib/block_scout_web/templates/stakes/_rows.html.eex:24
msgid "No"
msgstr ""
@@ -1545,25 +1711,65 @@ msgid "No Information"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:190
-#: lib/block_scout_web/templates/transaction/overview.html.eex:415
+#: lib/block_scout_web/templates/block/overview.html.eex:196
+#: lib/block_scout_web/templates/transaction/overview.html.eex:436
msgid "Nonce"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:36
-#: lib/block_scout_web/templates/transaction/_tile.html.eex:76
+#: lib/block_scout_web/templates/tokens/inventory/_token.html.eex:11
+msgid "Not unique Token"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:121
+msgid "Number of accounts holding the token"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address/overview.html.eex:290
+msgid "Number of blocks validated by this validator."
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:144
+msgid "Number of digits that come after the decimal place when displaying token value"
+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/tokens/overview/_details.html.eex:132
+msgid "Number of transfers for the token"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address/overview.html.eex:229
+msgid "Number of transfers to/from this address."
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:40
+#: lib/block_scout_web/templates/transaction/_tile.html.eex:82
msgid "OUT"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:53
+#: lib/block_scout_web/templates/common_components/_rap_pagination_container.html.eex:13
+msgid "Only the first"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract/index.html.eex:61
msgid "Optimization enabled"
msgstr ""
#, elixir-format
-#: 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
+#: lib/block_scout_web/templates/address_contract/index.html.eex:70
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:72
msgid "Optimization runs"
msgstr ""
@@ -1579,12 +1785,12 @@ msgid "Ordered"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_footer.html.eex:71
+#: lib/block_scout_web/templates/layout/_footer.html.eex:76
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 ""
@@ -1606,14 +1812,14 @@ msgid "Parameters"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:133
+#: lib/block_scout_web/templates/block/overview.html.eex:139
msgid "Parent Hash"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:53
-#: 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/layout/_topnav.html.eex:55
+#: 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 ""
@@ -1660,12 +1866,12 @@ msgid "Pools searching is already in progress for this address"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:417
+#: lib/block_scout_web/templates/transaction/overview.html.eex:438
msgid "Position"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:248
+#: lib/block_scout_web/templates/block/overview.html.eex:254
msgid "Position %{index}"
msgstr ""
@@ -1676,7 +1882,8 @@ msgid "Potential Reward Share"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:24
+#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:18
+#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:32
msgid "Potential matches from our contract method database:"
msgstr ""
@@ -1687,18 +1894,23 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:41
-#: lib/block_scout_web/templates/layout/app.html.eex:44
+#: lib/block_scout_web/templates/layout/app.html.eex:50 lib/block_scout_web/templates/tokens/overview/_details.html.eex:109
msgid "Price"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:331
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:108
+msgid "Price per token on the exchanges"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/transaction/overview.html.eex:352
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:219
-#: lib/block_scout_web/templates/transaction/overview.html.eex:381
+#: lib/block_scout_web/templates/block/overview.html.eex:225
+#: lib/block_scout_web/templates/transaction/overview.html.eex:402
msgid "Priority Fee / Tip"
msgstr ""
@@ -1714,36 +1926,36 @@ msgid "QR Code"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:99
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:98
msgid "Query"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:131
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:133
msgid "RPC"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:426
+#: lib/block_scout_web/templates/transaction/overview.html.eex:447
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:517
+#: lib/block_scout_web/templates/transaction_raw_trace/index.html.eex:7 lib/block_scout_web/views/transaction_view.ex:514
msgid "Raw Trace"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:81
-#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:25 lib/block_scout_web/views/address_view.ex:351
+#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:27 lib/block_scout_web/views/address_view.ex:351
#: lib/block_scout_web/views/tokens/overview_view.ex:43
msgid "Read Contract"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:88
-#: lib/block_scout_web/views/address_view.ex:352
+#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:41 lib/block_scout_web/views/address_view.ex:352
msgid "Read Proxy"
msgstr ""
@@ -1778,8 +1990,10 @@ 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_via_flattened_code/new.html.eex:228
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:45
+#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:57
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:82
msgid "Reset"
msgstr ""
@@ -1796,18 +2010,18 @@ msgid "Responses"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:83
+#: lib/block_scout_web/templates/transaction/overview.html.eex:92
msgid "Result"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:120
+#: lib/block_scout_web/templates/transaction/overview.html.eex:129
msgid "Revert reason"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/_tile.html.eex:52
-#: 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 ""
@@ -1853,7 +2067,7 @@ msgid "Search network"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex:36
+#: lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex:47
msgid "Search tokens"
msgstr ""
@@ -1904,25 +2118,45 @@ msgid "Show only those I have stake in"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:108
+#: lib/block_scout_web/templates/address_token/overview.html.eex:52
+msgid "Shows the current"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_token/overview.html.eex:59
+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:66
+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"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/block/overview.html.eex:114
msgid "Size"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:107
+#: lib/block_scout_web/templates/block/overview.html.eex:113
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_internal_transaction/index.html.eex:54 lib/block_scout_web/templates/address_logs/index.html.eex:23
-#: lib/block_scout_web/templates/address_token/index.html.eex:17 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/address_coin_balance/index.html.eex:30
+#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:50 lib/block_scout_web/templates/address_logs/index.html.eex:23
+#: lib/block_scout_web/templates/address_token/index.html.eex:60 lib/block_scout_web/templates/address_token_transfer/index.html.eex:58
+#: lib/block_scout_web/templates/address_transaction/index.html.eex:50 lib/block_scout_web/templates/address_validation/index.html.eex:20
#: lib/block_scout_web/templates/block_transaction/index.html.eex:22 lib/block_scout_web/templates/chain/show.html.eex:180
-#: 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/pending_transaction/index.html.eex:18 lib/block_scout_web/templates/stakes/_table.html.eex:49
+#: lib/block_scout_web/templates/tokens/holder/index.html.eex:23 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:25
#: 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."
@@ -1944,15 +2178,10 @@ msgid "Source Pool"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:23
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:19
msgid "Sources and Metadata JSON"
msgstr ""
-#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:40
-msgid "Sourcify: Sources and metadata JSON file"
-msgstr ""
-
#, elixir-format
#: lib/block_scout_web/templates/stakes/_stakes_modal_stake.html.eex:7
msgid "Stake"
@@ -1990,7 +2219,7 @@ msgid "Staker's Address"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:152
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:154
msgid "Stakes"
msgstr ""
@@ -2001,7 +2230,7 @@ msgid "Stakes Ratio"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:158
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:160
msgid "Staking"
msgstr ""
@@ -2015,32 +2244,32 @@ msgstr ""
msgid "Staking epochs are not specified or not in the allowed range"
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:29
+msgid "Standard Input JSON"
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/views/internal_transaction_view.ex:24
msgid "Static Call"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:95
+#: lib/block_scout_web/templates/transaction/overview.html.eex:104
msgid "Status"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_footer.html.eex:37
+#: lib/block_scout_web/templates/layout/_footer.html.eex:39
msgid "Submit an Issue"
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 ""
-#, elixir-format
-#: lib/block_scout_web/templates/layout/_footer.html.eex:40
-msgid "Support"
-msgstr ""
-
#, elixir-format
#: lib/block_scout_web/templates/stakes/_stakes_top.html.eex:38
msgid "Swap STAKE on Honeyswap"
@@ -2048,7 +2277,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:21
-#: lib/block_scout_web/templates/transaction/_tile.html.eex:40
+#: lib/block_scout_web/templates/transaction/_tile.html.eex:46
msgid "TX Fee"
msgstr ""
@@ -2058,12 +2287,12 @@ msgid "Target Pool"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_footer.html.eex:28
+#: lib/block_scout_web/templates/layout/_footer.html.eex:30
msgid "Telegram"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_footer.html.eex:60
+#: lib/block_scout_web/templates/layout/_footer.html.eex:65
msgid "Test Networks"
msgstr ""
@@ -2078,7 +2307,7 @@ msgid "The Number of Delegators in the Pool"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:116
+#: lib/block_scout_web/templates/block/overview.html.eex:122
msgid "The SHA256 hash of the block."
msgstr ""
@@ -2088,12 +2317,12 @@ msgid "The amount can be claimed after the current epoch finishes."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:45
+#: lib/block_scout_web/templates/block/overview.html.eex:51
msgid "The block height of a particular block is defined as the number of blocks preceding it in the blockchain."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:43
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:41
msgid "The fallback function is executed on a call to the contract if none of the other functions match the given function signature, or if no data was supplied at all and there is no receive Ether function. The fallback function always receives data, but in order to also receive Ether it must be marked payable."
msgstr ""
@@ -2113,17 +2342,27 @@ msgid "The first amount is the validator’s own stake, the second is the total
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:132
+#: lib/block_scout_web/templates/block/overview.html.eex:138
msgid "The hash of the block from which this block was generated."
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/address/overview.html.eex:92
+msgid "The name found in the source code of the Contract."
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address/overview.html.eex:103
+msgid "The name of the validator."
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/stakes/_table.html.eex:43
msgid "The number of delegators providing stake to the pool. Click on the number to see more details."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:73
+#: lib/block_scout_web/templates/block/overview.html.eex:79
msgid "The number of transactions in the block."
msgstr ""
@@ -2133,7 +2372,7 @@ msgid "The percentage of stake in a single pool relative to the total amount sta
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:45
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:43
msgid "The receive function is executed on a call to the contract with empty calldata. This is the function that is executed on plain Ether transfers (e.g. via .send() or .transfer()). If no such function exists, but a payable fallback function exists, the fallback function will be called on a plain Ether transfer. If neither a receive Ether nor a payable fallback function is present, the contract cannot receive Ether through regular transactions and throws an exception."
msgstr ""
@@ -2143,7 +2382,7 @@ msgid "The rest addresses are delegators of its pool."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:119
+#: lib/block_scout_web/templates/transaction/overview.html.eex:128
msgid "The revert reason of the transaction."
msgstr ""
@@ -2153,7 +2392,7 @@ msgid "The staking epochs for which the reward could be claimed (read-only field
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:94
+#: lib/block_scout_web/templates/transaction/overview.html.eex:103
msgid "The status of the transaction: Confirmed or Unconfirmed."
msgstr ""
@@ -2163,27 +2402,32 @@ msgid "The table refreshed block(s) ago."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:171
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:82
+msgid "The total amount of tokens issued"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/block/overview.html.eex:177
msgid "The total gas amount used in the block and its percentage of gas filled in the block."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_validation/index.html.eex:20
+#: lib/block_scout_web/templates/address_validation/index.html.eex:16
msgid "There are no blocks validated by this address."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/index.html.eex:21
+#: lib/block_scout_web/templates/block/index.html.eex:17
msgid "There are no blocks."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/tokens/holder/index.html.eex:32
+#: lib/block_scout_web/templates/tokens/holder/index.html.eex:28
msgid "There are no holders for this Token."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:58
+#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:54
msgid "There are no internal transactions for this address."
msgstr ""
@@ -2203,7 +2447,7 @@ msgid "There are no logs for this transaction."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/pending_transaction/index.html.eex:26
+#: lib/block_scout_web/templates/pending_transaction/index.html.eex:22
msgid "There are no pending transactions."
msgstr ""
@@ -2218,7 +2462,7 @@ msgid "There are no token transfers for this transaction"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_token/index.html.eex:22
+#: lib/block_scout_web/templates/address_token/index.html.eex:65
msgid "There are no tokens for this address."
msgstr ""
@@ -2228,7 +2472,7 @@ msgid "There are no tokens."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_transaction/index.html.eex:59
+#: lib/block_scout_web/templates/address_transaction/index.html.eex:55
msgid "There are no transactions for this address."
msgstr ""
@@ -2238,18 +2482,18 @@ msgid "There are no transactions for this block."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/index.html.eex:35
+#: lib/block_scout_web/templates/transaction/index.html.eex:31
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:35
msgid "There is no coin history for this address."
msgstr ""
@@ -2264,7 +2508,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:21
#: lib/block_scout_web/templates/chain/show.html.eex:9
msgid "There was a problem loading the chart."
msgstr ""
@@ -2285,12 +2529,12 @@ msgid "This block has not been processed yet."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:33
+#: lib/block_scout_web/templates/address_contract/index.html.eex:41
msgid "This contract has been partially verified via Sourcify."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:37
+#: lib/block_scout_web/templates/address_contract/index.html.eex:45
msgid "This contract has been verified via Sourcify."
msgstr ""
@@ -2315,26 +2559,26 @@ msgid "This pool is banned until block #%{banned_until} (%{estimated_unban_day})
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:49
+#: lib/block_scout_web/templates/transaction/overview.html.eex:58
msgid "This transaction is pending confirmation."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:65
-#: lib/block_scout_web/templates/transaction/overview.html.eex:150
+#: lib/block_scout_web/templates/block/overview.html.eex:71
+#: 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:196 lib/block_scout_web/views/address_internal_transaction_view.ex:8
-#: lib/block_scout_web/views/address_token_transfer_view.ex:8 lib/block_scout_web/views/address_transaction_view.ex:8
+#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:32
+#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:34 lib/block_scout_web/templates/address_transaction/index.html.eex:28
+#: 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 ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:26
+#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:20
msgid "To have guaranteed accuracy, use the link above to verify the contract's source code."
msgstr ""
@@ -2345,18 +2589,22 @@ msgid "To see accurate decoded input data, the contract must be verified."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:11
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:13
msgid "Toggle navigation"
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/address/overview.html.eex:73
+msgid "Token"
+msgstr ""
+
+#, elixir-format
+#: 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 ""
@@ -2367,65 +2615,76 @@ msgid "Token Details"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/tokens/holder/index.html.eex:20
-#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:9 lib/block_scout_web/views/tokens/overview_view.ex:42
+#: lib/block_scout_web/templates/tokens/holder/index.html.eex:16
+#: 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:11 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 ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:13
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:19 lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex:3
-#: 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/instance/transfer/index.html.eex:16 lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:5
#: 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:348
-#: 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:514
+#: lib/block_scout_web/views/tokens/instance/overview_view.ex:186 lib/block_scout_web/views/tokens/overview_view.ex:41
+#: lib/block_scout_web/views/transaction_view.ex:511
msgid "Token Transfers"
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/address/overview.html.eex:72
+msgid "Token name and symbol."
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:156
+msgid "Token type"
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:21
-#: lib/block_scout_web/templates/address_token/index.html.eex:10 lib/block_scout_web/templates/address_token_transfer/index.html.eex:13
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:67 lib/block_scout_web/templates/layout/_topnav.html.eex:99
-#: lib/block_scout_web/templates/tokens/index.html.eex:10 lib/block_scout_web/views/address_view.ex:345
+#: lib/block_scout_web/templates/address/overview.html.eex:192 lib/block_scout_web/templates/address_token/overview.html.eex:58
+#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:13 lib/block_scout_web/templates/layout/_topnav.html.eex:69
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:101 lib/block_scout_web/templates/tokens/index.html.eex:10
+#: lib/block_scout_web/views/address_view.ex:345
msgid "Tokens"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:269
+#: 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:285
+#: 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:252
+#: 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:236
+#: lib/block_scout_web/templates/transaction/overview.html.eex:257
msgid "Tokens Transferred"
msgstr ""
@@ -2440,19 +2699,19 @@ msgid "Topic"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:77
-#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:80
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:71
+#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:90
msgid "Topics"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:163
+#: lib/block_scout_web/templates/block/overview.html.eex:169
msgid "Total Difficulty"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:97
-msgid "Total Supply"
+msgid "Total Supply * Price"
msgstr ""
#, elixir-format
@@ -2461,17 +2720,22 @@ msgid "Total blocks"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:162
+#: lib/block_scout_web/templates/block/overview.html.eex:168
msgid "Total difficulty of the chain until this block."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:180
+#: lib/block_scout_web/templates/block/overview.html.eex:186
msgid "Total gas limit provided by all transactions in the block."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:316
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:83
+msgid "Total supply"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/transaction/overview.html.eex:337
msgid "Total transaction fee."
msgstr ""
@@ -2482,12 +2746,12 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/stakes/_stakes_top.html.eex:52
-msgid "Trade STAKE on BitMax"
+msgid "Trade STAKE on AscendEX"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:25
-#: lib/block_scout_web/views/transaction_view.ex:463
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:19
+#: lib/block_scout_web/views/transaction_view.ex:460
msgid "Transaction"
msgstr ""
@@ -2502,61 +2766,70 @@ msgid "Transaction %{transaction}, %{subnetwork} %{transaction}"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:391
+#: lib/block_scout_web/templates/transaction/overview.html.eex:412
msgid "Transaction Burnt Fee"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:40
+#: lib/block_scout_web/templates/transaction/overview.html.eex:49
msgid "Transaction Details"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:317
+#: lib/block_scout_web/templates/transaction/overview.html.eex:338
msgid "Transaction Fee"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:65
+#: lib/block_scout_web/templates/transaction/overview.html.eex:74
msgid "Transaction Hash"
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:341
+#: lib/block_scout_web/templates/transaction/overview.html.eex:362
msgid "Transaction Type"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:414
+#: lib/block_scout_web/templates/transaction/overview.html.eex:435
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:340
+#: lib/block_scout_web/templates/transaction/overview.html.eex:361
msgid "Transaction type, introduced in EIP-2718."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:7
-#: lib/block_scout_web/templates/address_transaction/index.html.eex:17 lib/block_scout_web/templates/block/overview.html.eex:74
-#: lib/block_scout_web/templates/block_transaction/index.html.eex:10 lib/block_scout_web/templates/chain/show.html.eex:236
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:41 lib/block_scout_web/views/address_view.ex:347
+#: lib/block_scout_web/templates/address/overview.html.eex:203 lib/block_scout_web/templates/address/overview.html.eex:209
+#: lib/block_scout_web/templates/address/overview.html.eex:217 lib/block_scout_web/templates/address_transaction/index.html.eex:13
+#: lib/block_scout_web/templates/block/overview.html.eex:80 lib/block_scout_web/templates/block_transaction/index.html.eex:10
+#: lib/block_scout_web/templates/chain/show.html.eex:236 lib/block_scout_web/templates/layout/_topnav.html.eex:43
+#: lib/block_scout_web/views/address_view.ex:347
msgid "Transactions"
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/address/overview.html.eex:119
+msgid "Transactions and address of creation."
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/address/_tile.html.eex:31
msgid "Transactions sent"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:50
+#: lib/block_scout_web/templates/address/overview.html.eex:230
+#: lib/block_scout_web/templates/address/overview.html.eex:236 lib/block_scout_web/templates/address/overview.html.eex:244
+#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:50 lib/block_scout_web/templates/tokens/overview/_details.html.eex:133
msgid "Transfers"
msgstr ""
@@ -2567,7 +2840,7 @@ msgid "Try it out"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_footer.html.eex:25
+#: lib/block_scout_web/templates/layout/_footer.html.eex:27
msgid "Twitter"
msgstr ""
@@ -2578,18 +2851,23 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:92
-#: lib/block_scout_web/templates/layout/app.html.eex:46
+#: lib/block_scout_web/templates/layout/app.html.eex:52
msgid "Tx/day"
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:440
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:155
+msgid "Type of the token standard"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/transaction/overview.html.eex:461
msgid "UTF-8"
msgstr ""
@@ -2604,23 +2882,23 @@ msgid "Uncle Reward"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:244
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:29
+#: lib/block_scout_web/templates/block/overview.html.eex:250
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:31
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 ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:64
+#: lib/block_scout_web/templates/transaction/overview.html.eex:73
msgid "Unique character string (TxID) assigned to every verified transaction."
msgstr ""
@@ -2647,22 +2925,22 @@ 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:370
+#: lib/block_scout_web/templates/transaction/overview.html.eex:391
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:380
+#: lib/block_scout_web/templates/transaction/overview.html.eex:401
msgid "User-defined tip sent to validator for transaction priority/inclusion."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:218
+#: lib/block_scout_web/templates/block/overview.html.eex:224
msgid "User-defined tips sent to validator for transaction priority/inclusion."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:45
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:47
msgid "Validated"
msgstr ""
@@ -2686,6 +2964,11 @@ msgstr ""
msgid "Validator Info"
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/address/overview.html.eex:104
+msgid "Validator Name"
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/stakes/_table.html.eex:8
msgid "Validator Pool Addresses."
@@ -2697,45 +2980,62 @@ 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:302
+#: lib/block_scout_web/templates/transaction/overview.html.eex:323
msgid "Value"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:301
+#: lib/block_scout_web/templates/transaction/overview.html.eex:322
msgid "Value sent in the native token (and USD) if applicable."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:74
+#: lib/block_scout_web/templates/address_contract/index.html.eex:82
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:158 lib/block_scout_web/templates/address_contract/index.html.eex:170
-#: lib/block_scout_web/templates/address_contract/index.html.eex:201 lib/block_scout_web/templates/address_contract/index.html.eex:213
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:19
+#: lib/block_scout_web/templates/address_contract/index.html.eex:27
+#: lib/block_scout_web/templates/address_contract/index.html.eex:29 lib/block_scout_web/templates/address_contract/index.html.eex:164
+#: lib/block_scout_web/templates/address_contract/index.html.eex:170 lib/block_scout_web/templates/address_contract/index.html.eex:201
+#: lib/block_scout_web/templates/address_contract/index.html.eex:207 lib/block_scout_web/templates/smart_contract/_functions.html.eex:14
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_via_flattened_code/new.html.eex:227
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:44
+#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:56
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:81
msgid "Verify & publish"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:16
-#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:18 lib/block_scout_web/templates/transaction_log/_logs.html.eex:16
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:10
+#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:12 lib/block_scout_web/templates/transaction_log/_logs.html.eex:10
msgid "Verify the contract "
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_footer.html.eex:86
+#: lib/block_scout_web/templates/layout/_footer.html.eex:91
msgid "Version"
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:42
+msgid "Via Sourcify: Sources and metadata JSON file"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:36
+msgid "Via Standard Input JSON"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:31
+msgid "Via flattened source code"
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/chain/show.html.eex:175
msgid "View All Blocks"
@@ -2748,20 +3048,30 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:16
-#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:20 lib/block_scout_web/templates/tokens/overview/_details.html.eex:77
+#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:20
msgid "View Contract"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/_tile.html.eex:61
+#: lib/block_scout_web/templates/transaction/_tile.html.eex:67
msgid "View Less Transfers"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/_tile.html.eex:60
+#: lib/block_scout_web/templates/transaction/_tile.html.eex:66
msgid "View More Transfers"
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/block/overview.html.eex:39
+msgid "View next block"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/block/overview.html.eex:23
+msgid "View previous block"
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/address/_metatags.html.eex:9
msgid "View the account balance, transactions, and other data for %{address} on the %{network}"
@@ -2778,12 +3088,12 @@ msgid "View transaction %{transaction} on %{subnetwork}"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:130
-msgid "PicoEvmos"
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:48
+msgid "Vyper contract"
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 ""
@@ -2792,14 +3102,19 @@ msgstr ""
msgid "Wallet addresses"
msgstr ""
+#, elixir-format
+#: lib/block_scout_web/templates/common_components/_changed_bytecode_warning.html.eex:3
+msgid "Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky."
+msgstr ""
+
#, elixir-format
#: lib/block_scout_web/templates/stakes/_stakes_modal_claim_reward_content.html.eex:2
msgid "We found the following pools you can claim reward from:"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/views/wei_helpers.ex:76
-msgid "WEI"
+#: lib/block_scout_web/views/wei_helpers.ex:76 lib/block_scout_web/templates/smart_contract/_functions.html.eex:134
+msgid "atevmos"
msgstr ""
#, elixir-format
@@ -2828,26 +3143,26 @@ msgid "Working Stake Amount"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:99
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:98
msgid "Write"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:95
-#: lib/block_scout_web/views/address_view.ex:353
+#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:34 lib/block_scout_web/views/address_view.ex:353
msgid "Write Contract"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:102
-#: lib/block_scout_web/views/address_view.ex:354
+#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:48 lib/block_scout_web/views/address_view.ex:354
msgid "Write Proxy"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:60
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:103
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:146 lib/block_scout_web/templates/stakes/_rows.html.eex:24
+#: lib/block_scout_web/templates/address_contract_verification_common_fields/_fetch_constructor_args.html.eex:14
+#: lib/block_scout_web/templates/address_contract_verification_common_fields/_include_nightly_builds_field.html.eex:14
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:61 lib/block_scout_web/templates/stakes/_rows.html.eex:24
msgid "Yes"
msgstr ""
@@ -2918,12 +3233,27 @@ msgid "all epochs"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address/overview.html.eex:116
+#: lib/block_scout_web/templates/address/overview.html.eex:129
msgid "at"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract/index.html.eex:24
+#: lib/block_scout_web/templates/address_token/overview.html.eex:52
+msgid "balance of the address"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/transaction/overview.html.eex:411
+msgid "burned for this transaction. Equals Block Base Fee per Gas * Gas Used."
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/block/overview.html.eex:215
+msgid "burned from transactions included in the block (Base fee (per unit of gas) * Gas Used)."
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract/index.html.eex:27
msgid "button"
msgstr ""
@@ -2933,7 +3263,7 @@ msgid "candidate"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/overview.html.eex:207
+#: lib/block_scout_web/templates/transaction/overview.html.eex:228
msgid "created"
msgstr ""
@@ -2943,7 +3273,12 @@ msgid "custom RPC"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:43
+#: lib/block_scout_web/templates/common_components/_rap_pagination_container.html.eex:13
+msgid "elements are displayed"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:41
msgid "fallback"
msgstr ""
@@ -2953,8 +3288,8 @@ msgid "false"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:16
-#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:18 lib/block_scout_web/templates/transaction_log/_logs.html.eex:16
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:10
+#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:12 lib/block_scout_web/templates/transaction_log/_logs.html.eex:10
msgid "here"
msgstr ""
@@ -2968,13 +3303,18 @@ 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"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:21
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:16
msgid "page"
msgstr ""
@@ -2984,7 +3324,7 @@ msgid "pool owner"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:45
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:43
msgid "receive"
msgstr ""
@@ -3017,26 +3357,6 @@ msgid "validator"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/block/overview.html.eex:209
-msgid "xDai burned from transactions included in the block (Base fee (per unit of gas) * Gas Used)."
-msgstr ""
-
-#, elixir-format
-#: lib/block_scout_web/templates/stakes/_stakes_modal_withdraw.html.eex:76
-msgid "
Pending stake (stake placed on a candidate pool or placed during the current staking epoch) may be withdrawn now.
\n
Active stake (stake available after the current epoch) can be ordered for withdrawal from the pool, and will be available to claim after the current staking epoch is complete.
\n
If you have already ordered (and the staking window is still open), you may increase your current order by entering a positive value, or decrease your current order by entering a negative value in the box and clicking 'Order Withdrawal'. You must either keep the minimum stake amount in the pool, or order your entire stake for withdrawal.
To become a candidate, your staking address must be funded with %{tokenSymbol} tokens and %{coinSymbol} coins, and your OpenEthereum node must be active and configured with the mining address you specify here.
\n
To become a delegator, close this window and select an address from the list of pools you would like to place stake on. Click the Stake button next to the address to begin the process.
data (bytes) : 0x0000000000000000000000000000000000000000000000000000000000000001
)]
"
+ end
+ 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/block_scout_web/test/block_scout_web/views/tokens/smart_contract_view_test.exs b/apps/block_scout_web/test/block_scout_web/views/tokens/smart_contract_view_test.exs
index 2a23017b492e..39feae6444f6 100644
--- a/apps/block_scout_web/test/block_scout_web/views/tokens/smart_contract_view_test.exs
+++ b/apps/block_scout_web/test/block_scout_web/views/tokens/smart_contract_view_test.exs
@@ -135,7 +135,7 @@ defmodule BlockScoutWeb.SmartContractViewTest do
if length > @max_size do
"Click to view#{output}"
else
- "
#{output}
"
+ "#{output}"
end
end
diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex
index a772f615c50c..e42aad256e77 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
@doc """
@@ -215,7 +219,7 @@ defmodule EthereumJSONRPC do
@spec fetch_beneficiaries([block_number], json_rpc_named_arguments) ::
{:ok, FetchedBeneficiaries.t()} | {:error, reason :: term} | :ignore
def fetch_beneficiaries(block_numbers, json_rpc_named_arguments) when is_list(block_numbers) do
- min_block = first_block_to_fetch()
+ min_block = trace_first_block_to_fetch()
filtered_block_numbers =
block_numbers
@@ -306,7 +310,7 @@ defmodule EthereumJSONRPC do
Fetches internal transactions for entire blocks from variant API.
"""
def fetch_block_internal_transactions(block_numbers, json_rpc_named_arguments) when is_list(block_numbers) do
- min_block = first_block_to_fetch()
+ min_block = trace_first_block_to_fetch()
filtered_block_numbers =
block_numbers
@@ -484,8 +488,12 @@ defmodule EthereumJSONRPC do
end
end
- defp first_block_to_fetch do
- string_value = Application.get_env(:indexer, :first_block)
+ defp trace_first_block_to_fetch do
+ first_block_to_fetch(:trace_first_block)
+ end
+
+ def first_block_to_fetch(config) do
+ string_value = Application.get_env(:indexer, config)
case Integer.parse(string_value) do
{integer, ""} -> integer
diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/contract.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/contract.ex
index 62a582ef4ddf..448d18350f7b 100644
--- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/contract.ex
+++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/contract.ex
@@ -26,8 +26,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()
@@ -68,7 +70,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)
@@ -107,16 +109,21 @@ defmodule EthereumJSONRPC.Contract do
defp convert_int_string_to_array(arg) when is_nil(arg), do: true
+ defp convert_int_string_to_array(arg) when is_list(arg), do: convert_int_string_to_array_inner(arg)
+
defp convert_int_string_to_array(arg) when not is_nil(arg) do
cond do
String.starts_with?(arg, "[") && String.ends_with?(arg, "]") ->
arg
|> String.trim_leading("[")
|> String.trim_trailing("]")
+ |> String.split(",")
|> convert_int_string_to_array_inner()
arg !== "" ->
- convert_int_string_to_array_inner(arg)
+ arg
+ |> String.split(",")
+ |> convert_int_string_to_array_inner()
true ->
[]
@@ -125,7 +132,6 @@ defmodule EthereumJSONRPC.Contract do
defp convert_int_string_to_array_inner(arg) do
arg
- |> String.split(",")
|> Enum.map(fn el ->
{int, _} = Integer.parse(el)
int
@@ -134,6 +140,8 @@ defmodule EthereumJSONRPC.Contract do
defp convert_string_to_array(arg) when is_nil(arg), do: true
+ defp convert_string_to_array(arg) when is_list(arg), do: arg
+
defp convert_string_to_array(arg) when not is_nil(arg) do
cond do
String.starts_with?(arg, "[") && String.ends_with?(arg, "]") ->
@@ -180,10 +188,14 @@ defmodule EthereumJSONRPC.Contract do
block_number -> integer_to_quantity(block_number)
end
+ params =
+ %{to: contract_address, data: data}
+ |> (&if(is_nil(from), do: &1, else: Map.put(&1, :from, from))).()
+
full_params = %{
id: id,
method: "eth_call",
- params: [%{to: contract_address, data: data, from: from}, block]
+ params: [params, block]
}
request(full_params)
@@ -216,6 +228,9 @@ defmodule EthereumJSONRPC.Contract do
end
defp format_error(error) do
- format_error(Exception.message(error))
+ error
+ |> Map.put(:hide_url, true)
+ |> Exception.message()
+ |> format_error()
end
end
diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/decode_error.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/decode_error.ex
index 5b2fea35cc15..08c233a24f6f 100644
--- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/decode_error.ex
+++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/decode_error.ex
@@ -38,10 +38,12 @@ defmodule EthereumJSONRPC.DecodeError do
@request_body_limit 700
@impl Exception
- def message(%EthereumJSONRPC.DecodeError{
- request: %EthereumJSONRPC.DecodeError.Request{url: request_url, body: request_body},
- response: %EthereumJSONRPC.DecodeError.Response{status_code: response_status_code, body: response_body}
- }) do
+ def message(
+ %EthereumJSONRPC.DecodeError{
+ request: %EthereumJSONRPC.DecodeError.Request{url: request_url, body: request_body},
+ response: %EthereumJSONRPC.DecodeError.Response{status_code: response_status_code, body: response_body}
+ } = decode_error
+ ) do
request_body_binary = IO.iodata_to_binary(request_body)
truncated_request_body =
@@ -52,12 +54,14 @@ defmodule EthereumJSONRPC.DecodeError do
result
end
+ hide_url = Map.get(decode_error, :hide_url, false)
+
"""
Failed to decode Ethereum JSONRPC response:
request:
- url: #{request_url}
+ url: #{if hide_url, do: "hidden", else: request_url}
body: #{truncated_request_body}
diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/encoder.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/encoder.ex
index 64d45c144437..eae2cceb1b08 100644
--- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/encoder.ex
+++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/encoder.ex
@@ -23,39 +23,51 @@ defmodule EthereumJSONRPC.Encoder do
"0x" <> encoded_args
end
- defp parse_args(args) do
+ defp parse_args(args) when is_list(args) do
args
- |> Enum.map(fn
- <<"0x", hexadecimal_digits::binary>> ->
- Base.decode16!(hexadecimal_digits, case: :mixed)
-
- item ->
- if is_list(item) do
- item
- |> Enum.map(fn el ->
- <<"0x", hexadecimal_digits::binary>> = el
- Base.decode16!(hexadecimal_digits, case: :mixed)
- end)
- else
- item
- end
- end)
+ |> Enum.map(&parse_args/1)
+ end
+
+ defp parse_args(<<"0x", hexadecimal_digits::binary>>), do: Base.decode16!(hexadecimal_digits, case: :mixed)
+
+ defp parse_args(<>), do: try_to_decode(hexadecimal_digits)
+
+ defp parse_args(arg), do: arg
+
+ defp try_to_decode(hexadecimal_digits) do
+ case Base.decode16(hexadecimal_digits, case: :mixed) do
+ {:ok, decoded_value} ->
+ decoded_value
+
+ _ ->
+ hexadecimal_digits
+ end
end
@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 +84,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/lib/ethereum_jsonrpc/transaction.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex
index 37e0b35c89ab..3ba37558ecdd 100644
--- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex
+++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex
@@ -110,52 +110,9 @@ defmodule EthereumJSONRPC.Transaction do
transaction_index: 0
}
- Ganache bug: https://github.com/trufflesuite/ganache/issues/997
- Invalid input of `0x0` is converted to `0x`.
-
- iex> EthereumJSONRPC.Transaction.elixir_to_params(
- ...> %{
- ...> "blockHash" => "0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd",
- ...> "blockNumber" => 46147,
- ...> "from" => "0xa1e4380a3b1f749673e270229993ee55f35663b4",
- ...> "gas" => 21000,
- ...> "gasPrice" => 50000000000000,
- ...> "hash" => "0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060",
- ...> "input" => "0x0",
- ...> "nonce" => 0,
- ...> "r" => 61965845294689009770156372156374760022787886965323743865986648153755601564112,
- ...> "s" => 31606574786494953692291101914709926755545765281581808821704454381804773090106,
- ...> "to" => "0x5df9b87991262f6ba471f09758cde1c0fc1de734",
- ...> "transactionIndex" => 0,
- ...> "v" => 28,
- ...> "value" => 31337
- ...> }
- ...> )
- %{
- block_hash: "0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd",
- block_number: 46147,
- from_address_hash: "0xa1e4380a3b1f749673e270229993ee55f35663b4",
- gas: 21000,
- gas_price: 50000000000000,
- hash: "0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060",
- index: 0,
- input: "0x",
- nonce: 0,
- r: 61965845294689009770156372156374760022787886965323743865986648153755601564112,
- s: 31606574786494953692291101914709926755545765281581808821704454381804773090106,
- to_address_hash: "0x5df9b87991262f6ba471f09758cde1c0fc1de734",
- v: 28,
- value: 31337,
- transaction_index: 0
- }
-
"""
@spec elixir_to_params(elixir) :: params
- def elixir_to_params(%{"input" => "0x0"} = transaction) do
- elixir_to_params(%{transaction | "input" => "0x"})
- end
-
def elixir_to_params(
%{
"blockHash" => block_hash,
@@ -293,38 +250,6 @@ defmodule EthereumJSONRPC.Transaction do
end
end
- # Ganache bug. it return `to: "0x0"` except of `to: null`
- def elixir_to_params(
- %{
- "to" => "0x0"
- } = transaction
- ) do
- %{transaction | "to" => nil}
- |> elixir_to_params()
- end
-
- # Ganache bug. It don't send `r,s,v` transaction fields.
- # Fix is in sources but not released yet
- def elixir_to_params(
- %{
- "blockHash" => _,
- "blockNumber" => _,
- "from" => _,
- "gas" => _,
- "gasPrice" => _,
- "hash" => _,
- "input" => _,
- "nonce" => _,
- "to" => _,
- "transactionIndex" => _,
- "value" => _
- } = transaction
- ) do
- transaction
- |> Map.merge(%{"r" => 0, "s" => 0, "v" => 0})
- |> elixir_to_params()
- end
-
@doc """
Extracts `t:EthereumJSONRPC.hash/0` from transaction `params`
diff --git a/apps/ethereum_jsonrpc/mix.exs b/apps/ethereum_jsonrpc/mix.exs
index a6eba4532734..f1fa391c9eca 100644
--- a/apps/ethereum_jsonrpc/mix.exs
+++ b/apps/ethereum_jsonrpc/mix.exs
@@ -65,7 +65,7 @@ defmodule EthereumJsonrpc.MixProject do
# JSONRPC HTTP Post calls
{:httpoison, "~> 1.6"},
# Decode/Encode JSON for JSONRPC
- {:jason, "~> 1.2.2"},
+ {:jason, "~> 1.3"},
# Log errors and application output to separate files
{:logger_file_backend, "~> 0.0.10"},
# Mocking `EthereumJSONRPC.Transport` and `EthereumJSONRPC.HTTP` so we avoid hitting real chains for local testing
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/.gitignore b/apps/explorer/.gitignore
index bf75b1935597..78db466aba07 100644
--- a/apps/explorer/.gitignore
+++ b/apps/explorer/.gitignore
@@ -1,2 +1,3 @@
priv/.recovery
priv/solc_compilers/
+priv/vyper_compilers/
diff --git a/apps/explorer/.sobelow-conf b/apps/explorer/.sobelow-conf
index c97cb5e14bc1..c2b7ff895f31 100644
--- a/apps/explorer/.sobelow-conf
+++ b/apps/explorer/.sobelow-conf
@@ -6,6 +6,7 @@
format: "compact",
ignore: ["Config.HTTPS"],
ignore_files: [
- "lib/explorer/smart_contract/solidity/code_compiler.ex"
+ "lib/explorer/smart_contract/solidity/code_compiler.ex",
+ "lib/explorer/smart_contract/vyper/code_compiler.ex"
]
]
diff --git a/apps/explorer/config/config.exs b/apps/explorer/config/config.exs
index 2abf32ba5f24..f83a14c5e7e7 100644
--- a/apps/explorer/config/config.exs
+++ b/apps/explorer/config/config.exs
@@ -13,7 +13,7 @@ config :explorer,
token_functions_reader_max_retries: 3,
allowed_evm_versions:
System.get_env("ALLOWED_EVM_VERSIONS") ||
- "homestead,tangerineWhistle,spuriousDragon,byzantium,constantinople,petersburg,istanbul,default",
+ "homestead,tangerineWhistle,spuriousDragon,byzantium,constantinople,petersburg,istanbul,berlin,london,default",
include_uncles_in_average_block_time:
if(System.get_env("UNCLES_IN_AVERAGE_BLOCK_TIME") == "true", do: true, else: false),
healthy_blocks_period: System.get_env("HEALTHY_BLOCKS_PERIOD") || :timer.minutes(5),
@@ -21,8 +21,7 @@ config :explorer,
if(System.get_env("DISABLE_WEBAPP") != "true",
do: Explorer.Chain.Events.SimpleSender,
else: Explorer.Chain.Events.DBSender
- ),
- enabled_1559_support: System.get_env("ENABLE_1559_SUPPORT") == "true"
+ )
config :explorer, Explorer.Counters.AverageBlockTime,
enabled: true,
@@ -30,7 +29,7 @@ config :explorer, Explorer.Counters.AverageBlockTime,
config :explorer, Explorer.Chain.Events.Listener,
enabled:
- if(System.get_env("DISABLE_WEBAPP") == nil && System.get_env("DISABLE_INDEXER") == nil,
+ if(System.get_env("DISABLE_WEBAPP") == "true" && System.get_env("DISABLE_INDEXER") == "true",
do: false,
else: true
)
@@ -107,6 +106,10 @@ config :explorer, Explorer.Counters.AddressTransactionsCounter,
enabled: true,
enable_consolidation: true
+config :explorer, Explorer.Counters.AddressTokenTransfersCounter,
+ enabled: true,
+ enable_consolidation: true
+
config :explorer, Explorer.Counters.BlockBurnedFeeCounter,
enabled: true,
enable_consolidation: true
@@ -137,6 +140,8 @@ config :explorer, Explorer.Integrations.EctoLogger, query_time_ms_threshold: :ti
config :explorer, Explorer.Market.History.Cataloger, enabled: System.get_env("DISABLE_INDEXER") != "true"
+config :explorer, Explorer.Chain.Cache.MinMissingBlockNumber, enabled: System.get_env("DISABLE_WRITE_API") != "true"
+
txs_stats_init_lag =
System.get_env("TXS_HISTORIAN_INIT_LAG", "0")
|> Integer.parse()
@@ -237,19 +242,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/config/dev.exs b/apps/explorer/config/dev.exs
index 67745c85afcd..5363b392d6df 100644
--- a/apps/explorer/config/dev.exs
+++ b/apps/explorer/config/dev.exs
@@ -3,12 +3,38 @@ use Mix.Config
database = if System.get_env("DATABASE_URL"), do: nil, else: "explorer_dev"
hostname = if System.get_env("DATABASE_URL"), do: nil, else: "localhost"
+database_api_url =
+ if System.get_env("DATABASE_READ_ONLY_API_URL"),
+ do: System.get_env("DATABASE_READ_ONLY_API_URL"),
+ else: System.get_env("DATABASE_URL")
+
+pool_size =
+ if System.get_env("DATABASE_READ_ONLY_API_URL"),
+ do: String.to_integer(System.get_env("POOL_SIZE_API", "40")),
+ else: String.to_integer(System.get_env("POOL_SIZE_API", "50"))
+
# Configure your database
config :explorer, Explorer.Repo,
database: database,
hostname: hostname,
url: System.get_env("DATABASE_URL"),
- pool_size: String.to_integer(System.get_env("POOL_SIZE", "50")),
+ pool_size: pool_size,
+ timeout: :timer.seconds(80)
+
+database_api = if System.get_env("DATABASE_READ_ONLY_API_URL"), do: nil, else: database
+hostname_api = if System.get_env("DATABASE_READ_ONLY_API_URL"), do: nil, else: hostname
+
+pool_size_api =
+ if System.get_env("DATABASE_READ_ONLY_API_URL"),
+ do: String.to_integer(System.get_env("POOL_SIZE_API", "50")),
+ else: String.to_integer(System.get_env("POOL_SIZE_API", "10"))
+
+# Configure API database
+config :explorer, Explorer.Repo.Replica1,
+ database: database_api,
+ hostname: hostname_api,
+ url: database_api_url,
+ pool_size: pool_size_api,
timeout: :timer.seconds(80)
config :explorer, Explorer.Tracer, env: "dev", disabled?: true
diff --git a/apps/explorer/config/dev/arbitrum.exs b/apps/explorer/config/dev/arbitrum.exs
index d96839da1d23..1c80b4176564 100644
--- a/apps/explorer/config/dev/arbitrum.exs
+++ b/apps/explorer/config/dev/arbitrum.exs
@@ -5,7 +5,7 @@ config :explorer,
transport: EthereumJSONRPC.HTTP,
transport_options: [
http: EthereumJSONRPC.HTTP.HTTPoison,
- url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "http://localhost:7545",
+ url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "http://localhost:8545",
http_options: [recv_timeout: :timer.minutes(1), timeout: :timer.minutes(1), hackney: [pool: :ethereum_jsonrpc]]
],
variant: EthereumJSONRPC.Arbitrum
diff --git a/apps/explorer/config/dev/geth.exs b/apps/explorer/config/dev/geth.exs
index 00f52e54e681..f6a51993c225 100644
--- a/apps/explorer/config/dev/geth.exs
+++ b/apps/explorer/config/dev/geth.exs
@@ -5,7 +5,7 @@ config :explorer,
transport: EthereumJSONRPC.HTTP,
transport_options: [
http: EthereumJSONRPC.HTTP.HTTPoison,
- url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "https://mainnet.infura.io/8lTvJTKmHPCHazkneJsY",
+ url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "http://localhost:8545",
http_options: [recv_timeout: :timer.minutes(1), timeout: :timer.minutes(1), hackney: [pool: :ethereum_jsonrpc]]
],
variant: EthereumJSONRPC.Geth
diff --git a/apps/explorer/config/prod.exs b/apps/explorer/config/prod.exs
index f78fbb9473ca..c2f3a64ec285 100644
--- a/apps/explorer/config/prod.exs
+++ b/apps/explorer/config/prod.exs
@@ -1,9 +1,32 @@
use Mix.Config
+pool_size =
+ if System.get_env("DATABASE_READ_ONLY_API_URL"),
+ do: String.to_integer(System.get_env("POOL_SIZE_API", "50")),
+ else: String.to_integer(System.get_env("POOL_SIZE_API", "40"))
+
# Configures the database
config :explorer, Explorer.Repo,
url: System.get_env("DATABASE_URL"),
- pool_size: String.to_integer(System.get_env("POOL_SIZE", "50")),
+ pool_size: pool_size,
+ ssl: String.equivalent?(System.get_env("ECTO_USE_SSL") || "true", "true"),
+ prepare: :unnamed,
+ timeout: :timer.seconds(60)
+
+database_api_url =
+ if System.get_env("DATABASE_READ_ONLY_API_URL"),
+ do: System.get_env("DATABASE_READ_ONLY_API_URL"),
+ else: System.get_env("DATABASE_URL")
+
+pool_size_api =
+ if System.get_env("DATABASE_READ_ONLY_API_URL"),
+ do: String.to_integer(System.get_env("POOL_SIZE_API", "50")),
+ else: String.to_integer(System.get_env("POOL_SIZE_API", "10"))
+
+# Configures API the database
+config :explorer, Explorer.Repo.Replica1,
+ url: database_api_url,
+ pool_size: pool_size_api,
ssl: String.equivalent?(System.get_env("ECTO_USE_SSL") || "true", "true"),
prepare: :unnamed,
timeout: :timer.seconds(60)
diff --git a/apps/explorer/config/test.exs b/apps/explorer/config/test.exs
index 2a474ffbcb40..1cff6ca47af5 100644
--- a/apps/explorer/config/test.exs
+++ b/apps/explorer/config/test.exs
@@ -13,6 +13,16 @@ config :explorer, Explorer.Repo,
timeout: :timer.seconds(60),
queue_target: 1000
+# Configure API database
+config :explorer, Explorer.Repo.Replica1,
+ database: "explorer_test",
+ hostname: "localhost",
+ pool: Ecto.Adapters.SQL.Sandbox,
+ # Default of `5_000` was too low for `BlockFetcher` test
+ ownership_timeout: :timer.minutes(1),
+ timeout: :timer.seconds(60),
+ queue_target: 1000
+
config :explorer, Explorer.ExchangeRates, enabled: false, store: :ets
config :explorer, Explorer.Chain.Cache.BlockNumber, enabled: false
diff --git a/apps/explorer/lib/explorer/application.ex b/apps/explorer/lib/explorer/application.ex
index b6ee12a58e9a..a6b7e28e574b 100644
--- a/apps/explorer/lib/explorer/application.ex
+++ b/apps/explorer/lib/explorer/application.ex
@@ -41,12 +41,14 @@ defmodule Explorer.Application do
# Children to start in all environments
base_children = [
Explorer.Repo,
+ Explorer.Repo.Replica1,
Supervisor.child_spec({SpandexDatadog.ApiServer, datadog_opts()}, id: SpandexDatadog.ApiServer),
Supervisor.child_spec({Task.Supervisor, name: Explorer.HistoryTaskSupervisor}, id: Explorer.HistoryTaskSupervisor),
Supervisor.child_spec({Task.Supervisor, name: Explorer.MarketTaskSupervisor}, id: Explorer.MarketTaskSupervisor),
Supervisor.child_spec({Task.Supervisor, name: Explorer.GenesisDataTaskSupervisor}, id: GenesisDataTaskSupervisor),
Supervisor.child_spec({Task.Supervisor, name: Explorer.TaskSupervisor}, id: Explorer.TaskSupervisor),
Explorer.SmartContract.SolcDownloader,
+ Explorer.SmartContract.VyperDownloader,
{Registry, keys: :duplicate, name: Registry.ChainEvents, id: Registry.ChainEvents},
{Admin.Recovery, [[], [name: Admin.Recovery]]},
TransactionCount,
@@ -61,8 +63,7 @@ defmodule Explorer.Application do
con_cache_child_spec(RSK.cache_name(), ttl_check_interval: :timer.minutes(1), global_ttl: :timer.minutes(30)),
Transactions,
Accounts,
- Uncles,
- MinMissingBlockNumber
+ Uncles
]
children = base_children ++ configurable_children()
@@ -84,6 +85,7 @@ defmodule Explorer.Application do
configure(Explorer.Counters.AddressesWithBalanceCounter),
configure(Explorer.Counters.AddressesCounter),
configure(Explorer.Counters.AddressTransactionsCounter),
+ configure(Explorer.Counters.AddressTokenTransfersCounter),
configure(Explorer.Counters.AddressTransactionsGasUsageCounter),
configure(Explorer.Counters.AddressTokenUsdSum),
configure(Explorer.Counters.TokenHoldersCounter),
@@ -93,7 +95,8 @@ defmodule Explorer.Application do
configure(Explorer.Counters.AverageBlockTime),
configure(Explorer.Counters.Bridge),
configure(Explorer.Validator.MetadataProcessor),
- configure(Explorer.Staking.ContractState)
+ configure(Explorer.Staking.ContractState),
+ configure(MinMissingBlockNumber)
]
|> List.flatten()
end
@@ -110,12 +113,51 @@ defmodule Explorer.Application do
end
end
+ defp datadog_port do
+ if System.get_env("DATADOG_PORT") do
+ case Integer.parse(System.get_env("DATADOG_PORT")) do
+ {integer, ""} -> integer
+ _ -> 8126
+ end
+ else
+ 8126
+ end
+ end
+
+ defp spandex_batch_size do
+ if System.get_env("SPANDEX_BATCH_SIZE") do
+ case Integer.parse(System.get_env("SPANDEX_BATCH_SIZE")) do
+ {integer, ""} -> integer
+ _ -> 100
+ end
+ else
+ 100
+ end
+ end
+
+ defp spandex_sync_threshold do
+ if System.get_env("SPANDEX_SYNC_THRESHOLD") do
+ case Integer.parse(System.get_env("SPANDEX_SYNC_THRESHOLD")) do
+ {integer, ""} -> integer
+ _ -> 100
+ end
+ else
+ 100
+ end
+ end
+
defp datadog_opts do
+ datadog_port = datadog_port()
+
+ spandex_batch_size = spandex_batch_size()
+
+ spandex_sync_threshold = spandex_sync_threshold()
+
[
host: System.get_env("DATADOG_HOST") || "localhost",
- port: System.get_env("DATADOG_PORT") || 8126,
- batch_size: System.get_env("SPANDEX_BATCH_SIZE") || 100,
- sync_threshold: System.get_env("SPANDEX_SYNC_THRESHOLD") || 100,
+ port: datadog_port,
+ batch_size: spandex_batch_size,
+ sync_threshold: spandex_sync_threshold,
http: HTTPoison
]
end
diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex
index 51df2763154c..fc5d6efd275e 100644
--- a/apps/explorer/lib/explorer/chain.ex
+++ b/apps/explorer/lib/explorer/chain.ex
@@ -10,6 +10,7 @@ defmodule Explorer.Chain do
join: 5,
limit: 2,
lock: 2,
+ offset: 2,
order_by: 2,
order_by: 3,
preload: 2,
@@ -79,7 +80,6 @@ defmodule Explorer.Chain do
alias Explorer.Chain.Import.Runner
alias Explorer.Chain.InternalTransaction.{CallType, Type}
- alias Explorer.Chain.Transaction.History.TransactionStats
alias Explorer.Counters.{AddressesCounter, AddressesWithBalanceCounter}
alias Explorer.Market.MarketHistoryCache
alias Explorer.{PagingOptions, Repo}
@@ -101,6 +101,12 @@ defmodule Explorer.Chain do
@burn_address_hash_str "0x0000000000000000000000000000000000000000"
+ # seconds
+ @check_bytecode_interval 86_400
+
+ @limit_showing_transaсtions 10_000
+ @default_page_size 50
+
@typedoc """
The name of an association on the `t:Ecto.Schema.t/0`
"""
@@ -221,24 +227,34 @@ defmodule Explorer.Chain do
def address_to_internal_transactions(hash, options \\ []) do
necessity_by_association = Keyword.get(options, :necessity_by_association, %{})
direction = Keyword.get(options, :direction)
+
+ from_block = from_block(options)
+ to_block = to_block(options)
+
paging_options = Keyword.get(options, :paging_options, @default_paging_options)
if direction == nil do
query_to_address_hash_wrapped =
InternalTransaction
+ |> InternalTransaction.where_nonpending_block()
|> InternalTransaction.where_address_fields_match(hash, :to_address_hash)
+ |> InternalTransaction.where_block_number_in_period(from_block, to_block)
|> common_where_limit_order(paging_options)
|> wrapped_union_subquery()
query_from_address_hash_wrapped =
InternalTransaction
+ |> InternalTransaction.where_nonpending_block()
|> InternalTransaction.where_address_fields_match(hash, :from_address_hash)
+ |> InternalTransaction.where_block_number_in_period(from_block, to_block)
|> common_where_limit_order(paging_options)
|> wrapped_union_subquery()
query_created_contract_address_hash_wrapped =
InternalTransaction
+ |> InternalTransaction.where_nonpending_block()
|> InternalTransaction.where_address_fields_match(hash, :created_contract_address_hash)
+ |> InternalTransaction.where_block_number_in_period(from_block, to_block)
|> common_where_limit_order(paging_options)
|> wrapped_union_subquery()
@@ -262,6 +278,7 @@ defmodule Explorer.Chain do
InternalTransaction
|> InternalTransaction.where_nonpending_block()
|> InternalTransaction.where_address_fields_match(hash, direction)
+ |> InternalTransaction.where_block_number_in_period(from_block, to_block)
|> common_where_limit_order(paging_options)
|> preload(transaction: :block)
|> join_associations(necessity_by_association)
@@ -280,7 +297,7 @@ defmodule Explorer.Chain do
query
|> InternalTransaction.where_is_different_from_parent_transaction()
|> InternalTransaction.where_block_number_is_not_null()
- |> page_internal_transaction(paging_options)
+ |> page_internal_transaction(paging_options, %{index_int_tx_desc_order: true})
|> limit(^paging_options.page_size)
|> order_by(
[it],
@@ -417,9 +434,12 @@ defmodule Explorer.Chain do
end
defp address_to_transactions_tasks_query(options) do
+ from_block = from_block(options)
+ to_block = to_block(options)
+
options
|> Keyword.get(:paging_options, @default_paging_options)
- |> fetch_transactions()
+ |> fetch_transactions(from_block, to_block)
end
defp transactions_block_numbers_at_address(address_hash, options) do
@@ -436,9 +456,13 @@ defmodule Explorer.Chain do
direction = Keyword.get(options, :direction)
necessity_by_association = Keyword.get(options, :necessity_by_association, %{})
+ from_block = from_block(options)
+ to_block = to_block(options)
+
options
|> address_to_transactions_tasks_query()
|> Transaction.not_dropped_or_replaced_transacions()
+ |> where_block_number_in_period(from_block, to_block)
|> join_associations(necessity_by_association)
|> Transaction.matching_address_queries_list(direction, address_hash)
|> Enum.map(fn query -> Task.async(fn -> Repo.all(query) end) end)
@@ -531,10 +555,81 @@ defmodule Explorer.Chain do
|> Repo.all()
end
+ @doc """
+ address_hash_to_token_transfers_including_contract/2 function returns token transfers on address (to/from/contract).
+ It is used by CSV export of token transfers button.
+ """
+ @spec address_hash_to_token_transfers_including_contract(Hash.Address.t(), Keyword.t()) :: [TokenTransfer.t()]
+ def address_hash_to_token_transfers_including_contract(address_hash, options \\ []) do
+ paging_options = Keyword.get(options, :paging_options, @default_paging_options)
+ from_block = Keyword.get(options, :from_block)
+ to_block = Keyword.get(options, :to_block)
+
+ query =
+ from_block
+ |> query_address_hash_to_token_transfers_including_contract(to_block, address_hash)
+ |> order_by([token_transfer], asc: token_transfer.block_number, asc: token_transfer.log_index)
+
+ query
+ |> handle_token_transfer_paging_options(paging_options)
+ |> preload(transaction: :block)
+ |> preload(:token)
+ |> Repo.all()
+ end
+
+ defp query_address_hash_to_token_transfers_including_contract(nil, to_block, address_hash)
+ when not is_nil(to_block) do
+ from(
+ token_transfer in TokenTransfer,
+ where:
+ (token_transfer.to_address_hash == ^address_hash or
+ token_transfer.from_address_hash == ^address_hash or
+ token_transfer.token_contract_address_hash == ^address_hash) and
+ token_transfer.block_number <= ^to_block
+ )
+ end
+
+ defp query_address_hash_to_token_transfers_including_contract(from_block, nil, address_hash)
+ when not is_nil(from_block) do
+ from(
+ token_transfer in TokenTransfer,
+ where:
+ (token_transfer.to_address_hash == ^address_hash or
+ token_transfer.from_address_hash == ^address_hash or
+ token_transfer.token_contract_address_hash == ^address_hash) and
+ token_transfer.block_number >= ^from_block
+ )
+ end
+
+ defp query_address_hash_to_token_transfers_including_contract(from_block, to_block, address_hash)
+ when not is_nil(from_block) and not is_nil(to_block) do
+ from(
+ token_transfer in TokenTransfer,
+ where:
+ (token_transfer.to_address_hash == ^address_hash or
+ token_transfer.from_address_hash == ^address_hash or
+ token_transfer.token_contract_address_hash == ^address_hash) and
+ (token_transfer.block_number >= ^from_block and token_transfer.block_number <= ^to_block)
+ )
+ end
+
+ defp query_address_hash_to_token_transfers_including_contract(_, _, address_hash) do
+ from(
+ token_transfer in TokenTransfer,
+ where:
+ token_transfer.to_address_hash == ^address_hash or
+ token_transfer.from_address_hash == ^address_hash or
+ token_transfer.token_contract_address_hash == ^address_hash
+ )
+ end
+
@spec address_to_logs(Hash.Address.t(), Keyword.t()) :: [Log.t()]
def address_to_logs(address_hash, options \\ []) when is_list(options) do
paging_options = Keyword.get(options, :paging_options) || %PagingOptions{page_size: 50}
+ from_block = from_block(options)
+ to_block = to_block(options)
+
{block_number, transaction_index, log_index} = paging_options.key || {BlockNumber.get_max(), 0, 0}
base_query =
@@ -566,6 +661,7 @@ defmodule Explorer.Chain do
wrapped_query
|> filter_topic(options)
+ |> where_block_number_in_period(from_block, to_block)
|> Repo.all()
|> Enum.take(paging_options.page_size)
end
@@ -580,6 +676,30 @@ defmodule Explorer.Chain do
defp filter_topic(base_query, _), do: base_query
+ def where_block_number_in_period(base_query, from_block, to_block) when is_nil(from_block) and not is_nil(to_block) do
+ from(q in base_query,
+ where: q.block_number <= ^to_block
+ )
+ end
+
+ def where_block_number_in_period(base_query, from_block, to_block) when not is_nil(from_block) and is_nil(to_block) do
+ from(q in base_query,
+ where: q.block_number > ^from_block
+ )
+ end
+
+ def where_block_number_in_period(base_query, from_block, to_block) when is_nil(from_block) and is_nil(to_block) do
+ from(q in base_query,
+ where: 1
+ )
+ end
+
+ def where_block_number_in_period(base_query, from_block, to_block) do
+ from(q in base_query,
+ where: q.block_number > ^from_block and q.block_number <= ^to_block
+ )
+ end
+
@doc """
Finds all `t:Explorer.Chain.Transaction.t/0`s given the address_hash and the token contract
address hash.
@@ -756,7 +876,11 @@ defmodule Explorer.Chain do
select:
sum(
fragment(
- "Case When ? < ? Then ? Else ? End",
+ "CASE
+ WHEN ? = 0 THEN 0
+ WHEN ? < ? THEN ?
+ ELSE ? END",
+ tx.max_fee_per_gas,
tx.max_fee_per_gas - ^base_fee_per_gas,
tx.max_priority_fee_per_gas,
(tx.max_fee_per_gas - ^base_fee_per_gas) * tx.gas_used,
@@ -794,7 +918,7 @@ defmodule Explorer.Chain do
Repo.aggregate(to_address_query, :count, :hash, timeout: :infinity)
end
- @spec address_to_incoming_transaction_gas_usage(Hash.Address.t()) :: non_neg_integer()
+ @spec address_to_incoming_transaction_gas_usage(Hash.Address.t()) :: Decimal.t() | nil
def address_to_incoming_transaction_gas_usage(address_hash) do
to_address_query =
from(
@@ -805,7 +929,7 @@ defmodule Explorer.Chain do
Repo.aggregate(to_address_query, :sum, :gas_used, timeout: :infinity)
end
- @spec address_to_outcoming_transaction_gas_usage(Hash.Address.t()) :: non_neg_integer()
+ @spec address_to_outcoming_transaction_gas_usage(Hash.Address.t()) :: Decimal.t() | nil
def address_to_outcoming_transaction_gas_usage(address_hash) do
to_address_query =
from(
@@ -1585,99 +1709,6 @@ defmodule Explorer.Chain do
Repo.all(query)
end
- @doc """
- Returns the balance of the given address and block combination.
-
- Returns `{:error, :not_found}` if there is no address by that hash present.
- Returns `{:error, :no_balance}` if there is no balance for that address at that block.
- """
- @spec get_balance_as_of_block(Hash.Address.t(), Block.block_number() | :earliest | :latest | :pending) ::
- {:ok, Wei.t()} | {:error, :no_balance} | {:error, :not_found}
- def get_balance_as_of_block(address, block) when is_integer(block) do
- coin_balance_query =
- from(coin_balance in CoinBalance,
- where: coin_balance.address_hash == ^address,
- where: not is_nil(coin_balance.value),
- where: coin_balance.block_number <= ^block,
- order_by: [desc: coin_balance.block_number],
- limit: 1,
- select: coin_balance.value
- )
-
- case Repo.one(coin_balance_query) do
- nil -> {:error, :not_found}
- coin_balance -> {:ok, coin_balance}
- end
- end
-
- def get_balance_as_of_block(address, :latest) do
- case max_consensus_block_number() do
- {:ok, latest_block_number} ->
- get_balance_as_of_block(address, latest_block_number)
-
- {:error, :not_found} ->
- {:error, :not_found}
- end
- end
-
- def get_balance_as_of_block(address, :earliest) do
- query =
- from(coin_balance in CoinBalance,
- where: coin_balance.address_hash == ^address,
- where: not is_nil(coin_balance.value),
- where: coin_balance.block_number == 0,
- limit: 1,
- select: coin_balance.value
- )
-
- case Repo.one(query) do
- nil -> {:error, :not_found}
- coin_balance -> {:ok, coin_balance}
- end
- end
-
- def get_balance_as_of_block(address, :pending) do
- query =
- case max_consensus_block_number() do
- {:ok, latest_block_number} ->
- from(coin_balance in CoinBalance,
- where: coin_balance.address_hash == ^address,
- where: not is_nil(coin_balance.value),
- where: coin_balance.block_number > ^latest_block_number,
- order_by: [desc: coin_balance.block_number],
- limit: 1,
- select: coin_balance.value
- )
-
- {:error, :not_found} ->
- from(coin_balance in CoinBalance,
- where: coin_balance.address_hash == ^address,
- where: not is_nil(coin_balance.value),
- order_by: [desc: coin_balance.block_number],
- limit: 1,
- select: coin_balance.value
- )
- end
-
- case Repo.one(query) do
- nil -> {:error, :not_found}
- coin_balance -> {:ok, coin_balance}
- end
- end
-
- @spec list_ordered_addresses(non_neg_integer(), non_neg_integer()) :: [Address.t()]
- def list_ordered_addresses(offset, limit) do
- query =
- from(
- address in Address,
- order_by: [asc: address.inserted_at],
- offset: ^offset,
- limit: ^limit
- )
-
- Repo.all(query)
- end
-
@doc """
Finds an `t:Explorer.Chain.Address.t/0` that has the provided `t:Explorer.Chain.Address.t/0` `hash` and a contract.
@@ -1720,7 +1751,7 @@ defmodule Explorer.Chain do
case address_result do
%{smart_contract: smart_contract} ->
if smart_contract do
- address_result
+ check_bytecode_matching(address_result)
else
address_verified_twin_contract =
Chain.get_minimal_proxy_template(hash) ||
@@ -1750,6 +1781,46 @@ defmodule Explorer.Chain do
end
end
+ defp check_bytecode_matching(address) do
+ now = DateTime.utc_now()
+ json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments)
+
+ if !address.smart_contract.is_changed_bytecode and
+ address.smart_contract.bytecode_checked_at
+ |> DateTime.add(@check_bytecode_interval, :second)
+ |> DateTime.compare(now) != :gt do
+ case EthereumJSONRPC.fetch_codes(
+ [%{block_quantity: "latest", address: address.smart_contract.address_hash}],
+ json_rpc_named_arguments
+ ) do
+ {:ok, %EthereumJSONRPC.FetchedCodes{params_list: fetched_codes}} ->
+ bytecode_from_node = fetched_codes |> List.first() |> Map.get(:code)
+ bytecode_from_db = "0x" <> (address.contract_code.bytes |> Base.encode16(case: :lower))
+
+ if bytecode_from_node == bytecode_from_db do
+ {:ok, smart_contract} =
+ address.smart_contract
+ |> Changeset.change(%{bytecode_checked_at: now})
+ |> Repo.update()
+
+ %{address | smart_contract: smart_contract}
+ else
+ {:ok, smart_contract} =
+ address.smart_contract
+ |> Changeset.change(%{bytecode_checked_at: now, is_changed_bytecode: true})
+ |> Repo.update()
+
+ %{address | smart_contract: smart_contract}
+ end
+
+ _ ->
+ address
+ end
+ else
+ address
+ end
+ end
+
@spec find_decompiled_contract_address(Hash.Address.t()) :: {:ok, Address.t()} | {:error, :not_found}
def find_decompiled_contract_address(%Hash{byte_count: unquote(Hash.Address.byte_count())} = hash) do
query =
@@ -2020,33 +2091,6 @@ defmodule Explorer.Chain do
Repo.get(Block, block_hash)
end
- @spec fetch_sum_coin_total_supply_minus_burnt() :: non_neg_integer
- def fetch_sum_coin_total_supply_minus_burnt do
- {:ok, burn_address_hash} = Chain.string_to_address_hash(@burn_address_hash_str)
-
- query =
- from(
- a0 in Address,
- select: fragment("SUM(a0.fetched_coin_balance)"),
- where: a0.hash != ^burn_address_hash,
- where: a0.fetched_coin_balance > ^0
- )
-
- Repo.one!(query, timeout: :infinity) || 0
- end
-
- @spec fetch_sum_coin_total_supply() :: non_neg_integer
- def fetch_sum_coin_total_supply do
- query =
- from(
- a0 in Address,
- select: fragment("SUM(a0.fetched_coin_balance)"),
- where: a0.fetched_coin_balance > ^0
- )
-
- Repo.one!(query, timeout: :infinity) || 0
- end
-
@spec fetch_sum_gas_used() :: non_neg_integer
def fetch_sum_gas_used do
query =
@@ -2232,20 +2276,21 @@ defmodule Explorer.Chain do
fetch_top_tokens(filter, paging_options)
end
- @spec list_top_bridged_tokens(atom(), String.t(), [paging_options | necessity_by_association_option]) :: [
- {Token.t(), non_neg_integer()}
- ]
- def list_top_bridged_tokens(destination, filter, options \\ []) do
+ @spec list_top_bridged_tokens(atom(), String.t() | nil, boolean(), [paging_options | necessity_by_association_option]) ::
+ [
+ {Token.t(), BridgedToken.t()}
+ ]
+ def list_top_bridged_tokens(destination, filter, from_api, options \\ []) do
paging_options = Keyword.get(options, :paging_options, @default_paging_options)
- fetch_top_bridged_tokens(destination, paging_options, filter)
+ fetch_top_bridged_tokens(destination, paging_options, filter, from_api)
end
defp fetch_top_tokens(filter, paging_options) 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]
)
@@ -2266,48 +2311,68 @@ defmodule Explorer.Chain do
|> Repo.all()
end
- defp fetch_top_bridged_tokens(destination, paging_options, filter) do
+ defp fetch_top_bridged_tokens(destination, paging_options, filter, from_api) do
+ offset = (max(paging_options.page_number, 1) - 1) * paging_options.page_size
chain_id = translate_destination_to_chain_id(destination)
- bridged_tokens_query =
- from(bt in BridgedToken,
- select: bt,
- where: bt.foreign_chain_id == ^chain_id
- )
+ if chain_id == :undefined do
+ []
+ else
+ bridged_tokens_query =
+ if chain_id do
+ from(bt in BridgedToken,
+ select: bt,
+ where: bt.foreign_chain_id == ^chain_id
+ )
+ else
+ from(bt in BridgedToken,
+ select: bt
+ )
+ end
- base_query =
- from(t in Token,
- right_join: bt in subquery(bridged_tokens_query),
- on: t.contract_address_hash == bt.home_token_contract_address_hash,
- where: t.total_supply > ^0,
- where: t.bridged,
- order_by: [desc: t.holder_count, asc: t.name],
- select: [t, bt],
- preload: [:contract_address]
- )
+ base_query =
+ from(t in Token,
+ right_join: bt in subquery(bridged_tokens_query),
+ on: t.contract_address_hash == bt.home_token_contract_address_hash,
+ where: t.total_supply > ^0,
+ where: t.bridged,
+ order_by: [desc: t.holder_count, asc: t.name],
+ select: [t, bt],
+ preload: [:contract_address]
+ )
- base_query_with_paging =
- base_query
- |> page_tokens(paging_options)
- |> limit(^paging_options.page_size)
+ base_query_with_paging =
+ base_query
+ |> page_tokens(paging_options)
+ |> limit(^paging_options.page_size)
+ |> offset(^offset)
- query =
- if filter && filter !== "" do
- base_query_with_paging
- |> where(fragment("to_tsvector('english', symbol || ' ' || name ) @@ to_tsquery(?)", ^filter))
+ query =
+ if filter && filter !== "" do
+ base_query_with_paging
+ |> where(fragment("to_tsvector('english', symbol || ' ' || name ) @@ to_tsquery(?)", ^filter))
+ else
+ base_query_with_paging
+ end
+
+ if from_api do
+ query
+ |> Repo.replica().all()
else
- base_query_with_paging
+ query
+ |> Repo.all()
end
-
- query
- |> Repo.all()
+ end
end
defp translate_destination_to_chain_id(destination) do
case destination do
:eth -> 1
+ :kovan -> 42
:bsc -> 56
- _ -> 1
+ :poa -> 99
+ nil -> nil
+ _ -> :undefined
end
end
@@ -2442,15 +2507,32 @@ defmodule Explorer.Chain do
end
end
- @spec address_to_gas_usage_count(Address.t()) :: non_neg_integer()
+ @spec address_to_token_transfer_count(Address.t()) :: non_neg_integer()
+ def address_to_token_transfer_count(address) do
+ query =
+ from(
+ token_transfer in TokenTransfer,
+ where: token_transfer.to_address_hash == ^address.hash,
+ or_where: token_transfer.from_address_hash == ^address.hash
+ )
+
+ Repo.aggregate(query, :count, timeout: :infinity)
+ end
+
+ @spec address_to_gas_usage_count(Address.t()) :: Decimal.t() | nil
def address_to_gas_usage_count(address) do
if contract?(address) do
incoming_transaction_gas_usage = address_to_incoming_transaction_gas_usage(address.hash)
- if incoming_transaction_gas_usage == 0 do
- address_to_outcoming_transaction_gas_usage(address.hash)
- else
- incoming_transaction_gas_usage
+ cond do
+ !incoming_transaction_gas_usage ->
+ address_to_outcoming_transaction_gas_usage(address.hash)
+
+ Decimal.cmp(incoming_transaction_gas_usage, 0) == :eq ->
+ address_to_outcoming_transaction_gas_usage(address.hash)
+
+ true ->
+ incoming_transaction_gas_usage
end
else
address_to_outcoming_transaction_gas_usage(address.hash)
@@ -2472,7 +2554,7 @@ defmodule Explorer.Chain do
def address_tokens_usd_sum(token_balances) do
token_balances
- |> Enum.reduce(Decimal.new(0), fn {token_balance, _}, acc ->
+ |> Enum.reduce(Decimal.new(0), fn {token_balance, _, _}, acc ->
if token_balance.value && token_balance.token.usd_value do
Decimal.add(acc, balance_in_usd(token_balance))
else
@@ -2779,32 +2861,6 @@ defmodule Explorer.Chain do
end
end
- @spec max_non_consensus_block_number(integer | nil) :: {:ok, Block.block_number()} | {:error, :not_found}
- def max_non_consensus_block_number(max_consensus_block_number \\ nil) do
- max =
- if max_consensus_block_number do
- {:ok, max_consensus_block_number}
- else
- max_consensus_block_number()
- end
-
- case max do
- {:ok, number} ->
- query =
- from(block in Block,
- where: block.consensus == false,
- where: block.number > ^number
- )
-
- query
- |> Repo.aggregate(:max, :number)
- |> case do
- nil -> {:error, :not_found}
- number -> {:ok, number}
- end
- end
- end
-
@spec block_height() :: block_height()
def block_height do
query = from(block in Block, select: coalesce(max(block.number), 0), where: block.consensus == true)
@@ -3064,8 +3120,9 @@ defmodule Explorer.Chain do
end
end
- @spec timestamp_to_block_number(DateTime.t(), :before | :after) :: {:ok, Block.block_number()} | {:error, :not_found}
- def timestamp_to_block_number(given_timestamp, closest) do
+ @spec timestamp_to_block_number(DateTime.t(), :before | :after, boolean()) ::
+ {:ok, Block.block_number()} | {:error, :not_found}
+ def timestamp_to_block_number(given_timestamp, closest, from_api) do
{:ok, t} = Timex.format(given_timestamp, "%Y-%m-%d %H:%M:%S", :strftime)
inner_query =
@@ -3087,8 +3144,16 @@ defmodule Explorer.Chain do
limit: 1
)
- query
- |> Repo.one()
+ response =
+ if from_api do
+ query
+ |> Repo.replica().one()
+ else
+ query
+ |> Repo.one()
+ end
+
+ response
|> case do
nil ->
{:error, :not_found}
@@ -3183,6 +3248,61 @@ defmodule Explorer.Chain do
end
end
+ # RAP - random access pagination
+ @spec recent_collated_transactions_for_rap([paging_options | necessity_by_association_option]) :: %{
+ :total_transactions_count => non_neg_integer(),
+ :transactions => [Transaction.t()]
+ }
+ def recent_collated_transactions_for_rap(options \\ []) when is_list(options) do
+ necessity_by_association = Keyword.get(options, :necessity_by_association, %{})
+ paging_options = Keyword.get(options, :paging_options, @default_paging_options)
+
+ total_transactions_count = transactions_available_count()
+
+ fetched_transactions =
+ if is_nil(paging_options.key) or paging_options.page_number == 1 do
+ paging_options.page_size
+ |> Kernel.+(1)
+ |> Transactions.take_enough()
+ |> case do
+ nil ->
+ transactions = fetch_recent_collated_transactions_for_rap(paging_options, necessity_by_association)
+ Transactions.update(transactions)
+ transactions
+
+ transactions ->
+ transactions
+ end
+ else
+ fetch_recent_collated_transactions_for_rap(paging_options, necessity_by_association)
+ end
+
+ %{total_transactions_count: total_transactions_count, transactions: fetched_transactions}
+ end
+
+ def default_page_size, do: @default_page_size
+
+ def fetch_recent_collated_transactions_for_rap(paging_options, necessity_by_association) do
+ fetch_transactions_for_rap()
+ |> where([transaction], not is_nil(transaction.block_number) and not is_nil(transaction.index))
+ |> handle_random_access_paging_options(paging_options)
+ |> join_associations(necessity_by_association)
+ |> preload([{:token_transfers, [:token, :from_address, :to_address]}])
+ |> Repo.all()
+ end
+
+ defp fetch_transactions_for_rap do
+ Transaction
+ |> order_by([transaction], desc: transaction.block_number, desc: transaction.index)
+ end
+
+ def transactions_available_count do
+ Transaction
+ |> where([transaction], not is_nil(transaction.block_number) and not is_nil(transaction.index))
+ |> limit(^@limit_showing_transaсtions)
+ |> Repo.aggregate(:count, :hash)
+ end
+
def fetch_recent_collated_transactions(paging_options, necessity_by_association) do
paging_options
|> fetch_transactions()
@@ -3247,6 +3367,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`.
@@ -3255,11 +3400,25 @@ defmodule Explorer.Chain do
:ok,
%Explorer.Chain.Hash{
byte_count: 20,
- bytes: <<0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed :: big-integer-size(20)-unit(8)>>
+ bytes: <<90, 174, 182, 5, 63, 62, 148, 201, 185, 160, 159, 51, 102, 148, 53,
+ 231, 239, 27, 234, 237>>
}
}
- `String.t` format must always have 40 hexadecimal digits after the `0x` base prefix.
+ iex> Explorer.Chain.string_to_address_hash("0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed")
+ {
+ :ok,
+ %Explorer.Chain.Hash{
+ byte_count: 20,
+ bytes: <<90, 174, 182, 5, 63, 62, 148, 201, 185, 160, 159, 51, 102, 148, 53,
+ 231, 239, 27, 234, 237>>
+ }
+ }
+
+ iex> Base.encode16(<<90, 174, 182, 5, 63, 62, 148, 201, 185, 160, 159, 51, 102, 148, 53, 231, 239, 27, 234, 237>>, case: :lower)
+ "5aaeb6053f3e94c9b9a09f33669435e7ef1beaed"
+
+ `String.t` format must always have 40 hexadecimal digits after the `0x` base prefix.
iex> Explorer.Chain.string_to_address_hash("0x0")
:error
@@ -3433,8 +3592,8 @@ defmodule Explorer.Chain do
the `index` that are passed.
"""
- @spec transaction_to_logs(Hash.Full.t(), [paging_options | necessity_by_association_option]) :: [Log.t()]
- def transaction_to_logs(transaction_hash, options \\ []) when is_list(options) do
+ @spec transaction_to_logs(Hash.Full.t(), boolean(), [paging_options | necessity_by_association_option]) :: [Log.t()]
+ def transaction_to_logs(transaction_hash, from_api, options \\ []) when is_list(options) do
necessity_by_association = Keyword.get(options, :necessity_by_association, %{})
paging_options = Keyword.get(options, :paging_options, @default_paging_options)
@@ -3446,13 +3605,21 @@ defmodule Explorer.Chain do
transaction.hash == log.transaction_hash
)
- log_with_transactions
- |> where([_, transaction], transaction.hash == ^transaction_hash)
- |> page_logs(paging_options)
- |> limit(^paging_options.page_size)
- |> order_by([log], asc: log.index)
- |> join_associations(necessity_by_association)
- |> Repo.all()
+ query =
+ log_with_transactions
+ |> where([_, transaction], transaction.hash == ^transaction_hash)
+ |> page_logs(paging_options)
+ |> limit(^paging_options.page_size)
+ |> order_by([log], asc: log.index)
+ |> join_associations(necessity_by_association)
+
+ if from_api do
+ query
+ |> Repo.replica().all()
+ else
+ query
+ |> Repo.all()
+ end
end
@doc """
@@ -3589,7 +3756,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
@@ -3664,8 +3831,11 @@ defmodule Explorer.Chain do
creation_tx_query =
from(
tx in Transaction,
+ left_join: a in Address,
+ on: tx.created_contract_address_hash == a.hash,
where: tx.created_contract_address_hash == ^address_hash,
- select: tx.input
+ where: tx.status == ^1,
+ select: %{init: tx.input, created_contract_code: a.contract_code}
)
tx_input =
@@ -3673,7 +3843,9 @@ defmodule Explorer.Chain do
|> Repo.one()
if tx_input do
- Data.to_string(tx_input)
+ with %{init: input, created_contract_code: created_contract_code} <- tx_input do
+ %{init: Data.to_string(input), created_contract_code: Data.to_string(created_contract_code)}
+ end
else
creation_int_tx_query =
from(
@@ -3681,17 +3853,19 @@ defmodule Explorer.Chain do
join: t in assoc(itx, :transaction),
where: itx.created_contract_address_hash == ^address_hash,
where: t.status == ^1,
- select: itx.init
+ select: %{init: itx.init, created_contract_code: itx.created_contract_code}
)
- itx_init_code =
- creation_int_tx_query
- |> Repo.one()
+ res = creation_int_tx_query |> Repo.one()
- if itx_init_code do
- Data.to_string(itx_init_code)
- else
- nil
+ case res do
+ %{init: init, created_contract_code: created_contract_code} ->
+ init_str = Data.to_string(init)
+ created_contract_code_str = Data.to_string(created_contract_code)
+ %{init: init_str, created_contract_code: created_contract_code_str}
+
+ _ ->
+ nil
end
end
end
@@ -3974,44 +4148,6 @@ defmodule Explorer.Chain do
|> repo.insert(on_conflict: :nothing, conflict_target: [:address_hash, :name])
end
- @spec address_hash_to_address_with_source_code(Hash.Address.t()) :: Address.t() | nil
- def address_hash_to_address_with_source_code(address_hash) do
- case Repo.get(Address, address_hash) do
- nil ->
- nil
-
- address ->
- address_with_smart_contract =
- Repo.preload(address, [:smart_contract, :decompiled_smart_contracts, :smart_contract_additional_sources])
-
- if address_with_smart_contract.smart_contract do
- formatted_code = format_source_code_output(address_with_smart_contract.smart_contract)
-
- %{
- address_with_smart_contract
- | smart_contract: %{address_with_smart_contract.smart_contract | contract_source_code: formatted_code}
- }
- else
- address_verified_twin_contract =
- Chain.get_minimal_proxy_template(address_hash) ||
- Chain.get_address_verified_twin_contract(address_hash).verified_contract
-
- if address_verified_twin_contract do
- formatted_code = format_source_code_output(address_verified_twin_contract)
-
- %{
- address_with_smart_contract
- | smart_contract: %{address_verified_twin_contract | contract_source_code: formatted_code}
- }
- else
- address_with_smart_contract
- end
- end
- end
- end
-
- defp format_source_code_output(smart_contract), do: smart_contract.contract_source_code
-
@doc """
Finds metadata for verification of a contract from verified twins: contracts with the same bytecode
which were verified previously, returns a single t:SmartContract.t/0
@@ -4169,7 +4305,7 @@ defmodule Explorer.Chain do
result = Repo.one(query)
- if result, do: !result.partially_verified
+ if result, do: !result.partially_verified, else: false
end
def smart_contract_verified?(address_hash_str) when is_binary(address_hash_str) do
@@ -4196,9 +4332,10 @@ defmodule Explorer.Chain do
if Repo.one(query), do: true, else: false
end
- defp fetch_transactions(paging_options \\ nil) do
+ defp fetch_transactions(paging_options \\ nil, from_block \\ nil, to_block \\ nil) do
Transaction
|> order_by([transaction], desc: transaction.block_number, desc: transaction.index)
+ |> where_block_number_in_period(from_block, to_block)
|> handle_paging_options(paging_options)
end
@@ -4224,6 +4361,55 @@ defmodule Explorer.Chain do
|> limit(^paging_options.page_size)
end
+ defp handle_token_transfer_paging_options(query, nil), do: query
+
+ defp handle_token_transfer_paging_options(query, paging_options) do
+ query
+ |> TokenTransfer.page_token_transfer(paging_options)
+ |> limit(^paging_options.page_size)
+ end
+
+ defp handle_random_access_paging_options(query, empty_options) when empty_options in [nil, [], %{}],
+ do: limit(query, ^(@default_page_size + 1))
+
+ defp handle_random_access_paging_options(query, paging_options) do
+ query
+ |> (&if(paging_options |> Map.get(:page_number, 1) |> proccess_page_number() == 1,
+ do: &1,
+ else: page_transaction(&1, paging_options)
+ )).()
+ |> handle_page(paging_options)
+ end
+
+ defp handle_page(query, paging_options) do
+ page_number = paging_options |> Map.get(:page_number, 1) |> proccess_page_number()
+ page_size = Map.get(paging_options, :page_size, @default_page_size)
+
+ cond do
+ page_in_bounds?(page_number, page_size) && page_number == 1 ->
+ query
+ |> limit(^(page_size + 1))
+
+ page_in_bounds?(page_number, page_size) ->
+ query
+ |> limit(^page_size)
+ |> offset(^((page_number - 2) * page_size))
+
+ true ->
+ query
+ |> limit(^(@default_page_size + 1))
+ end
+ end
+
+ defp proccess_page_number(number) when number < 1, do: 1
+
+ defp proccess_page_number(number), do: number
+
+ defp page_in_bounds?(page_number, page_size),
+ do: page_size <= @limit_showing_transaсtions && @limit_showing_transaсtions - page_number * page_size >= 0
+
+ def limit_shownig_transactions, do: @limit_showing_transaсtions
+
defp join_association(query, [{association, nested_preload}], necessity)
when is_atom(association) and is_atom(nested_preload) do
case necessity do
@@ -4287,24 +4473,48 @@ defmodule Explorer.Chain do
where(query, [coin_balance], coin_balance.block_number < ^block_number)
end
- defp page_internal_transaction(query, %PagingOptions{key: nil}), do: query
+ defp page_internal_transaction(_, _, _ \\ %{index_int_tx_desc_order: false})
- defp page_internal_transaction(query, %PagingOptions{key: {block_number, transaction_index, index}}) do
- where(
- query,
- [internal_transaction],
- internal_transaction.block_number < ^block_number or
- (internal_transaction.block_number == ^block_number and
- internal_transaction.transaction_index < ^transaction_index) or
- (internal_transaction.block_number == ^block_number and
- internal_transaction.transaction_index == ^transaction_index and internal_transaction.index < ^index)
- )
+ defp page_internal_transaction(query, %PagingOptions{key: nil}, _), do: query
+
+ defp page_internal_transaction(query, %PagingOptions{key: {block_number, transaction_index, index}}, %{
+ index_int_tx_desc_order: desc
+ }) do
+ hardcoded_where_for_page_int_tx(query, block_number, transaction_index, index, desc)
end
- defp page_internal_transaction(query, %PagingOptions{key: {index}}) do
- where(query, [internal_transaction], internal_transaction.index > ^index)
+ defp page_internal_transaction(query, %PagingOptions{key: {index}}, %{index_int_tx_desc_order: desc}) do
+ if desc do
+ where(query, [internal_transaction], internal_transaction.index < ^index)
+ else
+ where(query, [internal_transaction], internal_transaction.index > ^index)
+ end
end
+ defp hardcoded_where_for_page_int_tx(query, block_number, transaction_index, index, false),
+ do:
+ where(
+ query,
+ [internal_transaction],
+ internal_transaction.block_number < ^block_number or
+ (internal_transaction.block_number == ^block_number and
+ internal_transaction.transaction_index < ^transaction_index) or
+ (internal_transaction.block_number == ^block_number and
+ internal_transaction.transaction_index == ^transaction_index and internal_transaction.index > ^index)
+ )
+
+ defp hardcoded_where_for_page_int_tx(query, block_number, transaction_index, index, true),
+ do:
+ where(
+ query,
+ [internal_transaction],
+ internal_transaction.block_number < ^block_number or
+ (internal_transaction.block_number == ^block_number and
+ internal_transaction.transaction_index < ^transaction_index) or
+ (internal_transaction.block_number == ^block_number and
+ internal_transaction.transaction_index == ^transaction_index and internal_transaction.index < ^index)
+ )
+
defp page_logs(query, %PagingOptions{key: nil}), do: query
defp page_logs(query, %PagingOptions{key: {index}}) do
@@ -4365,6 +4575,29 @@ defmodule Explorer.Chain do
)
end
+ def page_token_balances(query, %PagingOptions{key: nil}), do: query
+
+ def page_token_balances(query, %PagingOptions{key: {value, address_hash}}) do
+ where(
+ query,
+ [tb],
+ tb.value < ^value or (tb.value == ^value and tb.address_hash < ^address_hash)
+ )
+ end
+
+ def page_current_token_balances(query, %PagingOptions{key: nil}), do: query
+
+ def page_current_token_balances(query, paging_options: %PagingOptions{key: nil}), do: query
+
+ def page_current_token_balances(query, paging_options: %PagingOptions{key: {name, type, value}}) do
+ where(
+ query,
+ [ctb, bt, t],
+ ctb.value < ^value or (ctb.value == ^value and t.type < ^type) or
+ (ctb.value == ^value and t.type == ^type and t.name < ^name)
+ )
+ end
+
@doc """
Ensures the following conditions are true:
@@ -4446,7 +4679,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
)
@@ -4690,6 +4923,8 @@ defmodule Explorer.Chain do
else
{:ok, false}
end
+ else
+ {:ok, false}
end
end
@@ -5555,6 +5790,14 @@ defmodule Explorer.Chain do
|> Repo.all()
end
+ @spec fetch_last_token_balances(Hash.Address.t(), [paging_options]) :: []
+ def fetch_last_token_balances(address_hash, paging_options) do
+ address_hash
+ |> CurrentTokenBalance.last_token_balances(paging_options)
+ |> page_current_token_balances(paging_options)
+ |> Repo.all()
+ end
+
@spec erc721_token_instance_from_token_id_and_token_address(binary(), Hash.Address.t()) ::
{:ok, TokenTransfer.t()} | {:error, :not_found}
def erc721_token_instance_from_token_id_and_token_address(token_id, token_contract_address) do
@@ -5587,9 +5830,22 @@ defmodule Explorer.Chain do
@spec fetch_last_token_balance(Hash.Address.t(), Hash.Address.t()) :: Decimal.t()
def fetch_last_token_balance(address_hash, token_contract_address_hash) do
- address_hash
- |> CurrentTokenBalance.last_token_balance(token_contract_address_hash)
- |> Repo.one() || Decimal.new(0)
+ if address_hash !== %{} do
+ address_hash
+ |> CurrentTokenBalance.last_token_balance(token_contract_address_hash) || Decimal.new(0)
+ else
+ Decimal.new(0)
+ end
+ 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
+ if address_hash !== %{} do
+ address_hash
+ |> CurrentTokenBalance.last_token_balance_1155(token_contract_address_hash, token_id) || Decimal.new(0)
+ else
+ Decimal.new(0)
+ end
end
@spec address_to_coin_balances(Hash.Address.t(), [paging_options]) :: []
@@ -5705,16 +5961,51 @@ defmodule Explorer.Chain do
end
end
- @spec fetch_token_holders_from_token_hash(Hash.Address.t(), [paging_options]) :: [TokenBalance.t()]
- def fetch_token_holders_from_token_hash(contract_address_hash, options \\ []) do
+ @spec fetch_token_holders_from_token_hash(Hash.Address.t(), boolean(), [paging_options]) :: [TokenBalance.t()]
+ def fetch_token_holders_from_token_hash(contract_address_hash, from_api, options \\ []) do
+ query =
+ contract_address_hash
+ |> CurrentTokenBalance.token_holders_ordered_by_value(options)
+
+ if from_api do
+ query
+ |> Repo.replica().all()
+ else
+ query
+ |> Repo.all()
+ end
+ end
+
+ def fetch_token_holders_from_token_hash_and_token_id(contract_address_hash, token_id, options \\ []) do
contract_address_hash
- |> CurrentTokenBalance.token_holders_ordered_by_value(options)
+ |> 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
@@ -5733,26 +6024,8 @@ defmodule Explorer.Chain do
@spec data() :: Dataloader.Ecto.t()
def data, do: DataloaderEcto.new(Repo)
- def list_decompiled_contracts(limit, offset, not_decompiled_with_version \\ nil) do
- query =
- from(
- address in Address,
- where: address.contract_code != <<>>,
- where: not is_nil(address.contract_code),
- where: address.decompiled == true,
- limit: ^limit,
- offset: ^offset,
- order_by: [asc: address.inserted_at],
- preload: [:smart_contract]
- )
-
- query
- |> reject_decompiled_with_version(not_decompiled_with_version)
- |> Repo.all()
- 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,
@@ -5799,10 +6072,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}]
@@ -5810,7 +6097,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
@@ -5826,7 +6113,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
@@ -5836,6 +6132,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
@@ -5843,98 +6140,6 @@ defmodule Explorer.Chain do
end
end
- defp reject_decompiled_with_version(query, nil), do: query
-
- defp reject_decompiled_with_version(query, reject_version) do
- from(
- address in query,
- left_join: decompiled_smart_contract in assoc(address, :decompiled_smart_contracts),
- on: decompiled_smart_contract.decompiler_version == ^reject_version,
- where: is_nil(decompiled_smart_contract.address_hash)
- )
- end
-
- def list_verified_contracts(limit, offset) do
- query =
- from(
- smart_contract in SmartContract,
- order_by: [asc: smart_contract.inserted_at],
- limit: ^limit,
- offset: ^offset,
- preload: [:address]
- )
-
- query
- |> Repo.all()
- |> Enum.map(fn smart_contract ->
- Map.put(smart_contract.address, :smart_contract, smart_contract)
- end)
- end
-
- def list_contracts(limit, offset) do
- query =
- from(
- address in Address,
- where: not is_nil(address.contract_code),
- preload: [:smart_contract],
- order_by: [asc: address.inserted_at],
- limit: ^limit,
- offset: ^offset
- )
-
- Repo.all(query)
- end
-
- def list_unordered_unverified_contracts(limit, offset) do
- query =
- from(
- address in Address,
- where: address.contract_code != <<>>,
- where: not is_nil(address.contract_code),
- where: fragment("? IS NOT TRUE", address.verified),
- limit: ^limit,
- offset: ^offset
- )
-
- query
- |> Repo.all()
- |> Enum.map(fn address ->
- %{address | smart_contract: nil}
- end)
- end
-
- def list_empty_contracts(limit, offset) do
- query =
- from(address in Address,
- where: address.contract_code == <<>>,
- preload: [:smart_contract, :decompiled_smart_contracts],
- order_by: [asc: address.inserted_at],
- limit: ^limit,
- offset: ^offset
- )
-
- Repo.all(query)
- end
-
- def list_unordered_not_decompiled_contracts(limit, offset) do
- query =
- from(
- address in Address,
- where: fragment("? IS NOT TRUE", address.verified),
- where: fragment("? IS NOT TRUE", address.decompiled),
- where: address.contract_code != <<>>,
- where: not is_nil(address.contract_code),
- limit: ^limit,
- offset: ^offset
- )
-
- query
- |> Repo.all()
- |> Enum.map(fn address ->
- %{address | smart_contract: nil}
- end)
- end
-
@doc """
Combined block reward from all the fees.
"""
@@ -6135,7 +6340,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], %{
@@ -6145,6 +6352,8 @@ defmodule Explorer.Chain do
|> Repo.one()
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
@@ -6168,6 +6377,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.
@@ -6476,30 +6696,6 @@ defmodule Explorer.Chain do
defp boolean_to_check_result(false), do: :not_found
- def extract_db_name(db_url) do
- if db_url == nil do
- ""
- else
- db_url
- |> String.split("/")
- |> Enum.take(-1)
- |> Enum.at(0)
- end
- end
-
- def extract_db_host(db_url) do
- if db_url == nil do
- ""
- else
- db_url
- |> String.split("@")
- |> Enum.take(-1)
- |> Enum.at(0)
- |> String.split(":")
- |> Enum.at(0)
- end
- end
-
@doc """
Fetches the first trace from the Parity trace URL.
"""
@@ -6589,30 +6785,17 @@ defmodule Explorer.Chain do
# https://eips.ethereum.org/EIPS/eip-1967
storage_slot_logic_contract_address = "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"
- storage_slot_beacon_contract_address = "0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50"
- {status, implementation_address} =
+ {_status, implementation_address} =
case Contract.eth_get_storage_at_request(
proxy_address_hash,
storage_slot_logic_contract_address,
nil,
json_rpc_named_arguments
) do
- {:ok, "0x"} ->
- Contract.eth_get_storage_at_request(
- proxy_address_hash,
- storage_slot_beacon_contract_address,
- nil,
- json_rpc_named_arguments
- )
-
- {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"} ->
- Contract.eth_get_storage_at_request(
- proxy_address_hash,
- storage_slot_beacon_contract_address,
- nil,
- json_rpc_named_arguments
- )
+ {:ok, empty_address}
+ when empty_address in ["0x", "0x0", "0x0000000000000000000000000000000000000000000000000000000000000000"] ->
+ fetch_beacon_proxy_implementation(proxy_address_hash, json_rpc_named_arguments)
{:ok, implementation_logic_address} ->
{:ok, implementation_logic_address}
@@ -6621,15 +6804,62 @@ defmodule Explorer.Chain do
{:ok, "0x"}
end
- abi_decode_address_output(if status == :ok, do: implementation_address, else: "0x")
+ abi_decode_address_output(implementation_address)
+ 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", "0x0", "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
@@ -6674,11 +6904,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
@@ -6686,6 +6916,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
@@ -6721,24 +6953,8 @@ defmodule Explorer.Chain do
def get_implementation_abi_from_proxy(proxy_address_hash, abi)
when not is_nil(proxy_address_hash) and not is_nil(abi) do
- implementation_method_abi =
- abi
- |> Enum.find(fn method ->
- Map.get(method, "name") == "implementation" ||
- master_copy_pattern?(method)
- end)
-
- if implementation_method_abi do
- implementation_address_hash_string = get_implementation_address_hash(proxy_address_hash, abi)
-
- if implementation_address_hash_string do
- get_implementation_abi(implementation_address_hash_string)
- else
- []
- end
- else
- []
- end
+ implementation_address_hash_string = get_implementation_address_hash(proxy_address_hash, abi)
+ get_implementation_abi(implementation_address_hash_string)
end
def get_implementation_abi_from_proxy(proxy_address_hash, abi) when is_nil(proxy_address_hash) or is_nil(abi) do
@@ -6850,7 +7066,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)
@@ -6905,28 +7121,6 @@ defmodule Explorer.Chain do
end
end
- @doc """
- It is used by `totalfees` API endpoint of `stats` module for retrieving of total fee per day
- """
- @spec get_total_fees_per_day(String.t()) :: {:ok, non_neg_integer() | nil} | {:error, String.t()}
- def get_total_fees_per_day(date_string) do
- case Date.from_iso8601(date_string) do
- {:ok, date} ->
- query =
- from(
- tx_stats in TransactionStats,
- where: tx_stats.date == ^date,
- select: tx_stats.total_fee
- )
-
- total_fees = Repo.one(query)
- {:ok, total_fees}
-
- _ ->
- {:error, "An incorrect input date provided. It should be in ISO 8601 format (yyyy-mm-dd)."}
- end
- end
-
@spec get_token_transfer_type(TokenTransfer.t()) ::
:token_burning | :token_minting | :token_spawning | :token_transfer
def get_token_transfer_type(transfer) do
@@ -6968,15 +7162,43 @@ defmodule Explorer.Chain do
try_url =
"https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/#{chain_name}/assets/#{address_hash}/logo.png"
- %HTTPoison.Response{status_code: status_code} = HTTPoison.get!(try_url)
-
- if status_code == 200 do
- try_url
- else
- nil
- end
+ try_url
else
nil
end
end
+
+ defp from_block(options) do
+ Keyword.get(options, :from_block) || nil
+ end
+
+ def to_block(options) do
+ Keyword.get(options, :to_block) || nil
+ end
+
+ def convert_date_to_min_block(date_str) do
+ date_format = "%Y-%m-%d"
+
+ {:ok, date} =
+ date_str
+ |> Timex.parse(date_format, :strftime)
+
+ {:ok, day_before} =
+ date
+ |> Timex.shift(days: -1)
+ |> Timex.format(date_format, :strftime)
+
+ convert_date_to_max_block(day_before)
+ end
+
+ def convert_date_to_max_block(date) do
+ query =
+ from(block in Block,
+ where: fragment("DATE(timestamp) = TO_DATE(?, 'YYYY-MM-DD')", ^date),
+ select: max(block.number)
+ )
+
+ query
+ |> Repo.one()
+ end
end
diff --git a/apps/explorer/lib/explorer/chain/address.ex b/apps/explorer/lib/explorer/chain/address.ex
index c31060078941..704b1c9f48c9 100644
--- a/apps/explorer/lib/explorer/chain/address.ex
+++ b/apps/explorer/lib/explorer/chain/address.ex
@@ -25,7 +25,7 @@ defmodule Explorer.Chain.Address do
alias Explorer.Chain.Cache.NetVersion
- @optional_attrs ~w(contract_code fetched_coin_balance fetched_coin_balance_block_number nonce decompiled verified)a
+ @optional_attrs ~w(contract_code fetched_coin_balance fetched_coin_balance_block_number nonce decompiled verified gas_used transactions_count token_transfers_count)a
@required_attrs ~w(hash)a
@allowed_attrs @optional_attrs ++ @required_attrs
@@ -58,7 +58,10 @@ defmodule Explorer.Chain.Address do
contracts_creation_transaction: %Ecto.Association.NotLoaded{} | Transaction.t(),
inserted_at: DateTime.t(),
updated_at: DateTime.t(),
- nonce: non_neg_integer() | nil
+ nonce: non_neg_integer() | nil,
+ transactions_count: non_neg_integer() | nil,
+ token_transfers_count: non_neg_integer() | nil,
+ gas_used: non_neg_integer() | nil
}
@derive {Poison.Encoder,
@@ -95,6 +98,9 @@ defmodule Explorer.Chain.Address do
field(:verified, :boolean, default: false)
field(:has_decompiled_code?, :boolean, virtual: true)
field(:stale?, :boolean, virtual: true)
+ field(:transactions_count, :integer)
+ field(:token_transfers_count, :integer)
+ field(:gas_used, :integer)
has_one(:smart_contract, SmartContract)
has_one(:token, Token, foreign_key: :contract_address_hash)
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 298159f6a9ac..299af670ddda 100644
--- a/apps/explorer/lib/explorer/chain/address/current_token_balance.ex
+++ b/apps/explorer/lib/explorer/chain/address/current_token_balance.ex
@@ -9,9 +9,9 @@ defmodule Explorer.Chain.Address.CurrentTokenBalance do
use Explorer.Schema
import Ecto.Changeset
- import Ecto.Query, only: [from: 2, limit: 2, offset: 2, order_by: 3, preload: 2, where: 3]
+ import Ecto.Query, only: [from: 2, limit: 2, offset: 2, order_by: 3, preload: 2]
- alias Explorer.{Chain, PagingOptions}
+ alias Explorer.{Chain, PagingOptions, Repo}
alias Explorer.Chain.{Address, Block, BridgedToken, Hash, Token}
@default_paging_options %PagingOptions{page_size: 50}
@@ -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
@@ -91,11 +99,62 @@ defmodule Explorer.Chain.Address.CurrentTokenBalance do
|> token_holders_query
|> preload(:address)
|> order_by([tb], desc: :value, desc: :address_hash)
- |> page_token_balances(paging_options)
+ |> Chain.page_token_balances(paging_options)
|> limit(^paging_options.page_size)
|> 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.
"""
@@ -106,20 +165,84 @@ defmodule Explorer.Chain.Address.CurrentTokenBalance do
where: ctb.value > 0,
left_join: bt in BridgedToken,
on: ctb.token_contract_address_hash == bt.home_token_contract_address_hash,
+ left_join: t in Token,
+ on: ctb.token_contract_address_hash == t.contract_address_hash,
preload: :token,
- select: {ctb, bt}
+ select: {ctb, bt, t},
+ order_by: [desc: ctb.value, asc: t.type, asc: t.name]
)
end
+ @doc """
+ Builds an `t:Ecto.Query.t/0` to fetch the current token balances of the given address (paginated version).
+ """
+ def last_token_balances(address_hash, options) do
+ paging_options = Keyword.get(options, :paging_options, @default_paging_options)
+
+ address_hash
+ |> last_token_balances()
+ |> limit(^paging_options.page_size)
+ end
+
@doc """
Builds an `t:Ecto.Query.t/0` to fetch the current balance of the given address for the given token.
"""
def last_token_balance(address_hash, token_contract_address_hash) do
+ query =
+ from(
+ tb in __MODULE__,
+ where: tb.token_contract_address_hash == ^token_contract_address_hash,
+ where: tb.address_hash == ^address_hash,
+ select: tb.value
+ )
+
+ query
+ |> Repo.one()
+ 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
+ query =
+ 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
+ )
+
+ query
+ |> Repo.one()
+ 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(
- tb in __MODULE__,
- where: tb.token_contract_address_hash == ^token_contract_address_hash,
- where: tb.address_hash == ^address_hash,
- select: tb.value
+ 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
@@ -136,14 +259,4 @@ defmodule Explorer.Chain.Address.CurrentTokenBalance do
where: tb.value > 0
)
end
-
- defp page_token_balances(query, %PagingOptions{key: nil}), do: query
-
- defp page_token_balances(query, %PagingOptions{key: {value, address_hash}}) do
- where(
- query,
- [tb],
- tb.value < ^value or (tb.value == ^value and tb.address_hash < ^address_hash)
- )
- end
end
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/address_internal_transaction_csv_explorer.ex b/apps/explorer/lib/explorer/chain/address_internal_transaction_csv_explorer.ex
new file mode 100644
index 000000000000..a2aca6a635a1
--- /dev/null
+++ b/apps/explorer/lib/explorer/chain/address_internal_transaction_csv_explorer.ex
@@ -0,0 +1,101 @@
+defmodule Explorer.Chain.AddressInternalTransactionCsvExporter do
+ @moduledoc """
+ Exports internal transactions to a csv file.
+ """
+
+ alias Explorer.{Chain, PagingOptions}
+ alias Explorer.Chain.{Address, InternalTransaction, Wei}
+ alias NimbleCSV.RFC4180
+
+ @page_size 150
+
+ @paging_options %PagingOptions{page_size: @page_size + 1}
+
+ @spec export(Address.t(), String.t(), String.t()) :: Enumerable.t()
+ def export(address, from_period, to_period) do
+ from_block = Chain.convert_date_to_min_block(from_period)
+ to_block = Chain.convert_date_to_max_block(to_period)
+
+ address.hash
+ |> fetch_all_internal_transactions(from_block, to_block, @paging_options)
+ |> to_csv_format()
+ |> dump_to_stream()
+ end
+
+ defp fetch_all_internal_transactions(address_hash, from_block, to_block, paging_options, acc \\ []) do
+ options =
+ []
+ |> Keyword.put(:paging_options, paging_options)
+ |> Keyword.put(:from_block, from_block)
+ |> Keyword.put(:to_block, to_block)
+
+ internal_transactions = Chain.address_to_internal_transactions(address_hash, options)
+
+ new_acc = internal_transactions ++ acc
+
+ case Enum.split(internal_transactions, @page_size) do
+ {_internal_transactions,
+ [%InternalTransaction{block_number: block_number, transaction_index: transaction_index, index: index}]} ->
+ new_paging_options = %{@paging_options | key: {block_number, transaction_index, index}}
+ fetch_all_internal_transactions(address_hash, from_block, to_block, new_paging_options, new_acc)
+
+ {_, []} ->
+ new_acc
+ end
+ end
+
+ defp dump_to_stream(internal_transactions) do
+ internal_transactions
+ |> RFC4180.dump_to_stream()
+ end
+
+ defp to_csv_format(internal_transactions) do
+ row_names = [
+ "TxHash",
+ "Index",
+ "BlockNumber",
+ "BlockHash",
+ "TxIndex",
+ "BlockIndex",
+ "UnixTimestamp",
+ "FromAddress",
+ "ToAddress",
+ "ContractAddress",
+ "Type",
+ "CallType",
+ "Gas",
+ "GasUsed",
+ "Value",
+ "Input",
+ "Output",
+ "ErrCode"
+ ]
+
+ internal_transaction_lists =
+ internal_transactions
+ |> Stream.map(fn internal_transaction ->
+ [
+ to_string(internal_transaction.transaction_hash),
+ internal_transaction.index,
+ internal_transaction.block_number,
+ internal_transaction.block_hash,
+ internal_transaction.block_index,
+ internal_transaction.transaction_index,
+ internal_transaction.transaction.block.timestamp,
+ to_string(internal_transaction.from_address_hash),
+ to_string(internal_transaction.to_address_hash),
+ to_string(internal_transaction.created_contract_address_hash),
+ internal_transaction.type,
+ internal_transaction.call_type,
+ internal_transaction.gas,
+ internal_transaction.gas_used,
+ Wei.to(internal_transaction.value, :wei),
+ internal_transaction.input,
+ internal_transaction.output,
+ internal_transaction.error
+ ]
+ end)
+
+ Stream.concat([row_names], internal_transaction_lists)
+ end
+end
diff --git a/apps/explorer/lib/explorer/chain/address_log_csv_explorer.ex b/apps/explorer/lib/explorer/chain/address_log_csv_explorer.ex
new file mode 100644
index 000000000000..ed44957ab67a
--- /dev/null
+++ b/apps/explorer/lib/explorer/chain/address_log_csv_explorer.ex
@@ -0,0 +1,84 @@
+defmodule Explorer.Chain.AddressLogCsvExporter do
+ @moduledoc """
+ Exports internal transactions to a csv file.
+ """
+
+ alias Explorer.{Chain, PagingOptions}
+ alias Explorer.Chain.{Address, Log, Transaction}
+ alias NimbleCSV.RFC4180
+
+ @page_size 150
+
+ @paging_options %PagingOptions{page_size: @page_size + 1}
+
+ @spec export(Address.t(), String.t(), String.t()) :: Enumerable.t()
+ def export(address, from_period, to_period) do
+ from_block = Chain.convert_date_to_min_block(from_period)
+ to_block = Chain.convert_date_to_max_block(to_period)
+
+ address.hash
+ |> fetch_all_logs(from_block, to_block, @paging_options)
+ |> to_csv_format()
+ |> dump_to_stream()
+ end
+
+ defp fetch_all_logs(address_hash, from_block, to_block, paging_options, acc \\ []) do
+ options =
+ []
+ |> Keyword.put(:paging_options, paging_options)
+ |> Keyword.put(:from_block, from_block)
+ |> Keyword.put(:to_block, to_block)
+
+ logs = Chain.address_to_logs(address_hash, options)
+
+ new_acc = logs ++ acc
+
+ case Enum.split(logs, @page_size) do
+ {_logs, [%Log{block_number: block_number, transaction: %Transaction{index: transaction_index}, index: index}]} ->
+ new_paging_options = %{@paging_options | key: {block_number, transaction_index, index}}
+ fetch_all_logs(address_hash, from_block, to_block, new_paging_options, new_acc)
+
+ {_, []} ->
+ new_acc
+ end
+ end
+
+ defp dump_to_stream(logs) do
+ logs
+ |> RFC4180.dump_to_stream()
+ end
+
+ defp to_csv_format(logs) do
+ row_names = [
+ "TxHash",
+ "Index",
+ "BlockNumber",
+ "BlockHash",
+ "ContractAddress",
+ "Data",
+ "FirstTopic",
+ "SecondTopic",
+ "ThirdTopic",
+ "FourthTopic"
+ ]
+
+ log_lists =
+ logs
+ |> Stream.map(fn log ->
+ [
+ to_string(log.transaction_hash),
+ log.index,
+ log.block_number,
+ log.block_hash,
+ to_string(log.address_hash),
+ to_string(log.data),
+ to_string(log.first_topic),
+ to_string(log.second_topic),
+ to_string(log.third_topic),
+ to_string(log.fourth_topic)
+ ]
+ end)
+
+ Stream.concat([row_names], log_lists)
+ end
+end
diff --git a/apps/explorer/lib/explorer/chain/address_token_transfer_csv_exporter.ex b/apps/explorer/lib/explorer/chain/address_token_transfer_csv_exporter.ex
new file mode 100644
index 000000000000..fa8139a412a3
--- /dev/null
+++ b/apps/explorer/lib/explorer/chain/address_token_transfer_csv_exporter.ex
@@ -0,0 +1,102 @@
+defmodule Explorer.Chain.AddressTokenTransferCsvExporter do
+ @moduledoc """
+ Exports token transfers to a csv file.
+ """
+
+ alias Explorer.{Chain, PagingOptions}
+ alias Explorer.Chain.{Address, TokenTransfer}
+ alias NimbleCSV.RFC4180
+
+ @page_size 150
+ @paging_options %PagingOptions{page_size: @page_size + 1, asc_order: true}
+
+ @spec export(Address.t(), String.t(), String.t()) :: Enumerable.t()
+ def export(address, from_period, to_period) do
+ from_block = Chain.convert_date_to_min_block(from_period)
+ to_block = Chain.convert_date_to_max_block(to_period)
+
+ address.hash
+ |> fetch_all_token_transfers(from_block, to_block, @paging_options)
+ |> to_csv_format(address)
+ |> dump_to_stream()
+ end
+
+ def fetch_all_token_transfers(address_hash, from_block, to_block, paging_options, acc \\ []) do
+ options =
+ []
+ |> Keyword.put(:paging_options, paging_options)
+ |> Keyword.put(:from_block, from_block)
+ |> Keyword.put(:to_block, to_block)
+
+ token_transfers = Chain.address_hash_to_token_transfers_including_contract(address_hash, options)
+
+ new_acc = acc ++ token_transfers
+
+ case Enum.split(token_transfers, @page_size) do
+ {_token_transfers, [%TokenTransfer{block_number: block_number, log_index: log_index}]} ->
+ new_paging_options = %{@paging_options | key: {block_number, log_index}}
+ fetch_all_token_transfers(address_hash, from_block, to_block, new_paging_options, new_acc)
+
+ {_, []} ->
+ new_acc
+ end
+ end
+
+ defp dump_to_stream(transactions) do
+ transactions
+ |> RFC4180.dump_to_stream()
+ end
+
+ defp to_csv_format(token_transfers, address) do
+ row_names = [
+ "TxHash",
+ "BlockNumber",
+ "UnixTimestamp",
+ "FromAddress",
+ "ToAddress",
+ "TokenContractAddress",
+ "Type",
+ "TokenSymbol",
+ "TokensTransferred",
+ "TransactionFee",
+ "Status",
+ "ErrCode"
+ ]
+
+ token_transfer_lists =
+ token_transfers
+ |> Stream.map(fn token_transfer ->
+ [
+ to_string(token_transfer.transaction_hash),
+ token_transfer.transaction.block_number,
+ token_transfer.transaction.block.timestamp,
+ token_transfer.from_address_hash |> to_string() |> String.downcase(),
+ token_transfer.to_address_hash |> to_string() |> String.downcase(),
+ token_transfer.token_contract_address_hash |> to_string() |> String.downcase(),
+ type(token_transfer, address.hash),
+ token_transfer.token.symbol,
+ token_transfer.amount,
+ fee(token_transfer.transaction),
+ token_transfer.transaction.status,
+ token_transfer.transaction.error
+ ]
+ end)
+
+ Stream.concat([row_names], token_transfer_lists)
+ end
+
+ defp type(%TokenTransfer{from_address_hash: address_hash}, address_hash), do: "OUT"
+
+ defp type(%TokenTransfer{to_address_hash: address_hash}, address_hash), do: "IN"
+
+ defp type(_, _), do: ""
+
+ defp fee(transaction) do
+ transaction
+ |> Chain.fee(:wei)
+ |> case do
+ {:actual, value} -> value
+ {:maximum, value} -> "Max of #{value}"
+ end
+ end
+end
diff --git a/apps/explorer/lib/explorer/chain/address_transaction_csv_exporter.ex b/apps/explorer/lib/explorer/chain/address_transaction_csv_exporter.ex
new file mode 100644
index 000000000000..008c9a0a8e4a
--- /dev/null
+++ b/apps/explorer/lib/explorer/chain/address_transaction_csv_exporter.ex
@@ -0,0 +1,145 @@
+defmodule Explorer.Chain.AddressTransactionCsvExporter do
+ @moduledoc """
+ Exports transactions to a csv file.
+ """
+
+ import Ecto.Query,
+ only: [
+ from: 2
+ ]
+
+ alias Explorer.{Chain, Market, PagingOptions, Repo}
+ alias Explorer.Market.MarketHistory
+ alias Explorer.Chain.{Address, Transaction, Wei}
+ alias Explorer.ExchangeRates.Token
+ alias NimbleCSV.RFC4180
+
+ @necessity_by_association [
+ necessity_by_association: %{
+ [created_contract_address: :names] => :optional,
+ [from_address: :names] => :optional,
+ [to_address: :names] => :optional,
+ [token_transfers: :token] => :optional,
+ [token_transfers: :to_address] => :optional,
+ [token_transfers: :from_address] => :optional,
+ [token_transfers: :token_contract_address] => :optional,
+ :block => :required
+ }
+ ]
+
+ @page_size 150
+
+ @paging_options %PagingOptions{page_size: @page_size + 1}
+
+ @spec export(Address.t(), String.t(), String.t()) :: Enumerable.t()
+ def export(address, from_period, to_period) do
+ from_block = Chain.convert_date_to_min_block(from_period)
+ to_block = Chain.convert_date_to_max_block(to_period)
+ exchange_rate = Market.get_exchange_rate(Explorer.coin()) || Token.null()
+
+ address.hash
+ |> fetch_all_transactions(from_block, to_block, @paging_options)
+ |> to_csv_format(address, exchange_rate)
+ |> dump_to_stream()
+ end
+
+ def fetch_all_transactions(address_hash, from_block, to_block, paging_options, acc \\ []) do
+ options =
+ @necessity_by_association
+ |> Keyword.put(:paging_options, paging_options)
+ |> Keyword.put(:from_block, from_block)
+ |> Keyword.put(:to_block, to_block)
+
+ transactions = Chain.address_to_transactions_without_rewards(address_hash, options)
+
+ new_acc = transactions ++ acc
+
+ case Enum.split(transactions, @page_size) do
+ {_transactions, [%Transaction{block_number: block_number, index: index}]} ->
+ new_paging_options = %{@paging_options | key: {block_number, index}}
+ fetch_all_transactions(address_hash, from_block, to_block, new_paging_options, new_acc)
+
+ {_, []} ->
+ new_acc
+ end
+ end
+
+ defp dump_to_stream(transactions) do
+ transactions
+ |> RFC4180.dump_to_stream()
+ end
+
+ defp to_csv_format(transactions, address, exchange_rate) do
+ row_names = [
+ "TxHash",
+ "BlockNumber",
+ "UnixTimestamp",
+ "FromAddress",
+ "ToAddress",
+ "ContractAddress",
+ "Type",
+ "Value",
+ "Fee",
+ "Status",
+ "ErrCode",
+ "CurrentPrice",
+ "TxDateOpeningPrice",
+ "TxDateClosingPrice"
+ ]
+
+ transaction_lists =
+ transactions
+ |> Stream.map(fn transaction ->
+ {opening_price, closing_price} = price_at_date(transaction.block.timestamp)
+
+ [
+ to_string(transaction.hash),
+ transaction.block_number,
+ transaction.block.timestamp,
+ to_string(transaction.from_address),
+ to_string(transaction.to_address),
+ to_string(transaction.created_contract_address),
+ type(transaction, address.hash),
+ Wei.to(transaction.value, :wei),
+ fee(transaction),
+ transaction.status,
+ transaction.error,
+ exchange_rate.usd_value,
+ opening_price,
+ closing_price
+ ]
+ end)
+
+ Stream.concat([row_names], transaction_lists)
+ end
+
+ defp type(%Transaction{from_address_hash: address_hash}, address_hash), do: "OUT"
+
+ defp type(%Transaction{to_address_hash: address_hash}, address_hash), do: "IN"
+
+ defp type(_, _), do: ""
+
+ defp fee(transaction) do
+ transaction
+ |> Chain.fee(:wei)
+ |> case do
+ {:actual, value} -> value
+ {:maximum, value} -> "Max of #{value}"
+ end
+ end
+
+ defp price_at_date(datetime) do
+ date = DateTime.to_date(datetime)
+
+ query =
+ from(
+ mh in MarketHistory,
+ where: mh.date == ^date
+ )
+
+ case Repo.one(query) do
+ nil -> {nil, nil}
+ price -> {price.opening_price, price.closing_price}
+ end
+ end
+end
diff --git a/apps/explorer/lib/explorer/chain/block.ex b/apps/explorer/lib/explorer/chain/block.ex
index a2b91e9a450e..dfd4512644de 100644
--- a/apps/explorer/lib/explorer/chain/block.ex
+++ b/apps/explorer/lib/explorer/chain/block.ex
@@ -64,7 +64,8 @@ defmodule Explorer.Chain.Block do
total_difficulty: difficulty(),
transactions: %Ecto.Association.NotLoaded{} | [Transaction.t()],
refetch_needed: boolean(),
- base_fee_per_gas: Wei.t()
+ base_fee_per_gas: Wei.t(),
+ is_empty: boolean()
}
@primary_key {:hash, Hash.Full, autogenerate: false}
@@ -80,6 +81,7 @@ defmodule Explorer.Chain.Block do
field(:total_difficulty, :decimal)
field(:refetch_needed, :boolean)
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/address_sum.ex b/apps/explorer/lib/explorer/chain/cache/address_sum.ex
index 107932b730c0..01bb57fa65e4 100644
--- a/apps/explorer/lib/explorer/chain/cache/address_sum.ex
+++ b/apps/explorer/lib/explorer/chain/cache/address_sum.ex
@@ -13,7 +13,7 @@ defmodule Explorer.Chain.Cache.AddressSum do
global_ttl: Application.get_env(:explorer, __MODULE__)[:global_ttl],
callback: &async_task_on_deletion(&1)
- alias Explorer.Chain
+ alias Explorer.Etherscan
defp handle_fallback(:sum) do
# This will get the task PID if one exists and launch a new task if not
@@ -29,7 +29,7 @@ defmodule Explorer.Chain.Cache.AddressSum do
{:ok, task} =
Task.start(fn ->
try do
- result = Chain.fetch_sum_coin_total_supply()
+ result = Etherscan.fetch_sum_coin_total_supply()
set_sum(result)
rescue
diff --git a/apps/explorer/lib/explorer/chain/cache/address_sum_minus_burnt.ex b/apps/explorer/lib/explorer/chain/cache/address_sum_minus_burnt.ex
index 635b19241e4c..a8494d969082 100644
--- a/apps/explorer/lib/explorer/chain/cache/address_sum_minus_burnt.ex
+++ b/apps/explorer/lib/explorer/chain/cache/address_sum_minus_burnt.ex
@@ -13,7 +13,7 @@ defmodule Explorer.Chain.Cache.AddressSumMinusBurnt do
global_ttl: Application.get_env(:explorer, __MODULE__)[:global_ttl],
callback: &async_task_on_deletion(&1)
- alias Explorer.Chain
+ alias Explorer.{Chain, Etherscan}
defp handle_fallback(:sum_minus_burnt) do
# This will get the task PID if one exists and launch a new task if not
@@ -29,7 +29,7 @@ defmodule Explorer.Chain.Cache.AddressSumMinusBurnt do
{:ok, task} =
Task.start(fn ->
try do
- result = Chain.fetch_sum_coin_total_supply_minus_burnt()
+ result = Etherscan.fetch_sum_coin_total_supply_minus_burnt()
params = %{
counter_type: "sum_coin_total_supply_minus_burnt",
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/events/listener.ex b/apps/explorer/lib/explorer/chain/events/listener.ex
index 7040a3bd3241..2d93eeada0b4 100644
--- a/apps/explorer/lib/explorer/chain/events/listener.ex
+++ b/apps/explorer/lib/explorer/chain/events/listener.ex
@@ -6,23 +6,15 @@ defmodule Explorer.Chain.Events.Listener do
use GenServer
alias Postgrex.Notifications
- import Explorer.Chain, only: [extract_db_name: 1, extract_db_host: 1]
def start_link(_) do
GenServer.start_link(__MODULE__, "chain_event", name: __MODULE__)
end
def init(channel) do
- explorer_repo =
+ {:ok, pid} =
:explorer
|> Application.get_env(Explorer.Repo)
-
- db_url = explorer_repo[:url]
-
- {:ok, pid} =
- explorer_repo
- |> Keyword.put(:database, extract_db_name(db_url))
- |> Keyword.put(:hostname, extract_db_host(db_url))
|> Notifications.start_link()
ref = Notifications.listen!(pid, channel)
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 b8d67208ced1..a4b68d12c4ff 100644
--- a/apps/explorer/lib/explorer/chain/import/runner/blocks.ex
+++ b/apps/explorer/lib/explorer/chain/import/runner/blocks.ex
@@ -13,6 +13,7 @@ defmodule Explorer.Chain.Import.Runner.Blocks do
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
@@ -119,13 +120,13 @@ defmodule Explorer.Chain.Import.Runner.Blocks 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
+ select: {ctb.token_contract_address_hash, ctb.token_id},
+ distinct: [ctb.token_contract_address_hash, ctb.token_id]
)
- contract_address_hashes = repo.all(query)
+ contract_address_hashes_and_token_ids = repo.all(query)
- Tokens.acquire_contract_address_tokens(repo, contract_address_hashes)
+ Tokens.acquire_contract_address_tokens(repo, contract_address_hashes_and_token_ids)
end
defp fork_transactions(%{
@@ -309,6 +310,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, []}
@@ -341,10 +351,11 @@ defmodule Explorer.Chain.Import.Runner.Blocks do
ordered_query =
from(tb in Address.TokenBalance,
where: tb.block_number in ^consensus_block_numbers,
- select: map(tb, [:address_hash, :token_contract_address_hash, :block_number]),
+ select: map(tb, [:address_hash, :token_contract_address_hash, :token_id, :block_number]),
# Enforce TokenBalance ShareLocks order (see docs: sharelocks.md)
order_by: [
tb.token_contract_address_hash,
+ tb.token_id,
tb.address_hash,
tb.block_number
],
@@ -359,6 +370,9 @@ defmodule Explorer.Chain.Import.Runner.Blocks do
ordered_address_token_balance.address_hash == tb.address_hash and
ordered_address_token_balance.token_contract_address_hash ==
tb.token_contract_address_hash and
+ ((is_nil(ordered_address_token_balance.token_id) and is_nil(tb.token_id)) or
+ (ordered_address_token_balance.token_id == tb.token_id and
+ not is_nil(ordered_address_token_balance.token_id) and not is_nil(tb.token_id))) and
ordered_address_token_balance.block_number == tb.block_number
)
@@ -378,10 +392,11 @@ defmodule Explorer.Chain.Import.Runner.Blocks 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]),
+ select: map(ctb, [:address_hash, :token_contract_address_hash, :token_id]),
# Enforce CurrentTokenBalance ShareLocks order (see docs: sharelocks.md)
order_by: [
ctb.token_contract_address_hash,
+ ctb.token_id,
ctb.address_hash
],
lock: "FOR UPDATE"
@@ -393,6 +408,7 @@ defmodule Explorer.Chain.Import.Runner.Blocks do
map(ctb, [
:address_hash,
:token_contract_address_hash,
+ :token_id,
# 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`.
@@ -401,8 +417,10 @@ defmodule Explorer.Chain.Import.Runner.Blocks do
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
+ ordered_address_current_token_balance.token_contract_address_hash == ctb.token_contract_address_hash and
+ ((is_nil(ordered_address_current_token_balance.token_id) and is_nil(ctb.token_id)) or
+ (ordered_address_current_token_balance.token_id == ctb.token_id and
+ not is_nil(ordered_address_current_token_balance.token_id) and not is_nil(ctb.token_id)))
)
try do
@@ -417,31 +435,13 @@ defmodule Explorer.Chain.Import.Runner.Blocks do
defp derive_address_current_token_balances(_, [], _), do: {:ok, []}
- defp derive_address_current_token_balances(repo, deleted_address_current_token_balances, %{timeout: timeout})
+ defp derive_address_current_token_balances(
+ repo,
+ deleted_address_current_token_balances,
+ %{timeout: timeout} = options
+ )
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)
+ 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),
@@ -449,42 +449,82 @@ defmodule Explorer.Chain.Import.Runner.Blocks do
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]]
+ w: [partition_by: [tb.address_hash, tb.token_contract_address_hash, tb.token_id]]
]
)
- ordered_current_token_balance =
+ 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
+ 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, :block_number, :value]))
+ 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.
defp delete_rewards(repo, blocks_changes, %{timeout: timeout}) do
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 960af51780e9..1286825e8690 100644
--- a/apps/explorer/lib/explorer/chain/import/runner/internal_transactions.ex
+++ b/apps/explorer/lib/explorer/chain/import/runner/internal_transactions.ex
@@ -221,7 +221,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
block_numbers =
changes_list
|> Enum.map(& &1.block_number)
- |> Enum.dedup()
+ |> Enum.uniq()
query =
from(
@@ -258,7 +258,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"
)
@@ -442,7 +442,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
update_transactions_inner(
repo,
- valid_internal_transactions_count,
+ valid_internal_transactions,
transaction_hashes,
transaction_hashes_iterator,
timeout,
@@ -454,7 +454,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
transaction_from_db && Map.get(transaction_from_db, :cumulative_gas_used) ->
update_transactions_inner(
repo,
- valid_internal_transactions_count,
+ valid_internal_transactions,
transaction_hashes,
transaction_hashes_iterator,
timeout,
@@ -468,7 +468,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
update_transactions_inner(
repo,
- valid_internal_transactions_count,
+ valid_internal_transactions,
transaction_hashes,
transaction_hashes_iterator,
timeout,
@@ -489,6 +489,13 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
end
end
+ defp get_trivial_tx_hashes_with_error_in_internal_tx(internal_transactions) do
+ internal_transactions
+ |> Enum.filter(fn internal_tx -> internal_tx[:index] != 0 && !is_nil(internal_tx[:error]) end)
+ |> Enum.map(fn internal_tx -> internal_tx[:transaction_hash] end)
+ |> MapSet.new()
+ end
+
defp fetch_transaction_receipt_from_node(transaction_hash, json_rpc_named_arguments) do
receipt_response =
EthereumJSONRPC.fetch_transaction_receipts(
@@ -517,7 +524,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
defp update_transactions_inner(
repo,
- valid_internal_transactions_count,
+ valid_internal_transactions,
transaction_hashes,
transaction_hashes_iterator,
timeout,
@@ -525,7 +532,16 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
first_trace,
transaction_receipt_from_node \\ nil
) do
- set = generate_transaction_set_to_update(first_trace, transaction_receipt_from_node, timestamps)
+ valid_internal_transactions_count = Enum.count(valid_internal_transactions)
+ txs_with_error_in_internal_txs = get_trivial_tx_hashes_with_error_in_internal_tx(valid_internal_transactions)
+
+ set =
+ generate_transaction_set_to_update(
+ first_trace,
+ transaction_receipt_from_node,
+ timestamps,
+ txs_with_error_in_internal_txs
+ )
update_query =
from(
@@ -553,7 +569,12 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
end
end
- def generate_transaction_set_to_update(first_trace, transaction_receipt_from_node, timestamps) do
+ def generate_transaction_set_to_update(
+ first_trace,
+ transaction_receipt_from_node,
+ timestamps,
+ txs_with_error_in_internal_txs
+ ) do
default_set = [
created_contract_address_hash: first_trace.created_contract_address_hash,
error: first_trace.error,
@@ -570,16 +591,16 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
:cumulative_gas_used,
transaction_receipt_from_node && transaction_receipt_from_node.cumulative_gas_used
)
+ |> Keyword.put_new(
+ :has_error_in_internal_txs,
+ if(Enum.member?(txs_with_error_in_internal_txs, first_trace.transaction_hash), do: true, else: false)
+ )
set_with_gas_used =
- if first_trace.gas_used do
- Keyword.put_new(set, :gas_used, first_trace.gas_used)
+ if transaction_receipt_from_node && transaction_receipt_from_node.gas_used do
+ Keyword.put_new(set, :gas_used, transaction_receipt_from_node.gas_used)
else
- if transaction_receipt_from_node && transaction_receipt_from_node.gas_used do
- Keyword.put_new(set, :gas_used, transaction_receipt_from_node.gas_used)
- else
- set
- end
+ set
end
filtered_set = Enum.reject(set_with_gas_used, fn {_key, value} -> is_nil(value) end)
@@ -588,11 +609,14 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
end
defp remove_consensus_of_invalid_blocks(repo, invalid_block_numbers) do
+ minimal_block = first_block_to_fetch()
+
if Enum.count(invalid_block_numbers) > 0 do
update_query =
from(
b in Block,
where: b.number in ^invalid_block_numbers and b.consensus,
+ where: b.number > ^minimal_block,
select: b.hash,
# ShareLocks order already enforced by `acquire_blocks` (see docs: sharelocks.md)
update: [set: [consensus: false]]
@@ -619,6 +643,10 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
end
end
+ def first_block_to_fetch do
+ EthereumJSONRPC.first_block_to_fetch(:trace_first_block)
+ end
+
def update_pending_blocks_status(repo, pending_hashes, invalid_block_hashes) do
valid_block_hashes =
pending_hashes
diff --git a/apps/explorer/lib/explorer/chain/import/runner/tokens.ex b/apps/explorer/lib/explorer/chain/import/runner/tokens.ex
index d6a254911cb4..1e24d15d291c 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 db0a6ea96811..0d18c1f3e877 100644
--- a/apps/explorer/lib/explorer/chain/import/runner/transactions.ex
+++ b/apps/explorer/lib/explorer/chain/import/runner/transactions.ex
@@ -118,6 +118,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)
@@ -125,13 +130,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_used, 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_used, 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,
@@ -144,7 +148,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/internal_transaction.ex b/apps/explorer/lib/explorer/chain/internal_transaction.ex
index 8fcc5eeb0a88..60cb8b89beb7 100644
--- a/apps/explorer/lib/explorer/chain/internal_transaction.ex
+++ b/apps/explorer/lib/explorer/chain/internal_transaction.ex
@@ -575,6 +575,38 @@ defmodule Explorer.Chain.InternalTransaction do
)
end
+ def where_block_number_in_period(query, from_number, to_number) when is_nil(from_number) and not is_nil(to_number) do
+ where(
+ query,
+ [it],
+ it.block_number <= ^to_number
+ )
+ end
+
+ def where_block_number_in_period(query, from_number, to_number) when not is_nil(from_number) and is_nil(to_number) do
+ where(
+ query,
+ [it],
+ it.block_number > ^from_number
+ )
+ end
+
+ def where_block_number_in_period(query, from_number, to_number) when is_nil(from_number) and is_nil(to_number) do
+ where(
+ query,
+ [it],
+ 1
+ )
+ end
+
+ def where_block_number_in_period(query, from_number, to_number) do
+ where(
+ query,
+ [it],
+ it.block_number > ^from_number and it.block_number <= ^to_number
+ )
+ end
+
def where_block_number_is_not_null(query) do
where(query, [t], not is_nil(t.block_number))
end
diff --git a/apps/explorer/lib/explorer/chain/log.ex b/apps/explorer/lib/explorer/chain/log.ex
index 7887fb0be0af..68c227f1c6bc 100644
--- a/apps/explorer/lib/explorer/chain/log.ex
+++ b/apps/explorer/lib/explorer/chain/log.ex
@@ -133,8 +133,24 @@ defmodule Explorer.Chain.Log do
with {:ok, selector, mapping} <- find_and_decode(full_abi, log, transaction),
identifier <- Base.encode16(selector.method_id, case: :lower),
- text <- function_call(selector.function, mapping),
- do: {:ok, identifier, text, mapping}
+ text <- function_call(selector.function, mapping) do
+ {:ok, identifier, text, mapping}
+ else
+ {:error, :could_not_decode} ->
+ case find_candidates(log, transaction) do
+ {:error, :contract_not_verified, []} ->
+ {:error, :could_not_decode}
+
+ {:error, :contract_not_verified, candidates} ->
+ {:error, :contract_verified, candidates}
+
+ _ ->
+ {:error, :could_not_decode}
+ end
+
+ output ->
+ output
+ end
_ ->
find_candidates(log, transaction)
diff --git a/apps/explorer/lib/explorer/chain/smart_contract.ex b/apps/explorer/lib/explorer/chain/smart_contract.ex
index 0df4b02a54cf..502a7e80886d 100644
--- a/apps/explorer/lib/explorer/chain/smart_contract.ex
+++ b/apps/explorer/lib/explorer/chain/smart_contract.ex
@@ -194,6 +194,10 @@ defmodule Explorer.Chain.SmartContract do
contract.
* `verified_via_sourcify` - whether contract verified through Sourcify utility or not.
* `partially_verified` - whether contract verified using partial matched source code or not.
+ * `is_vyper_contract` - boolean flag, determines if contract is Vyper or not
+ * `file_path` - show the filename or path to the file of the contract source file
+ * `is_changed_bytecode` - boolean flag, determines if contract's bytecode was modified
+ * `bytecode_checked_at` - timestamp of the last check of contract's bytecode matching (DB and BlockChain)
"""
@type t :: %Explorer.Chain.SmartContract{
@@ -207,7 +211,10 @@ defmodule Explorer.Chain.SmartContract do
abi: [function_description],
verified_via_sourcify: boolean | nil,
partially_verified: boolean | nil,
- file_path: String.t()
+ file_path: String.t(),
+ is_vyper_contract: boolean | nil,
+ is_changed_bytecode: boolean,
+ bytecode_checked_at: DateTime.t()
}
schema "smart_contracts" do
@@ -223,6 +230,9 @@ defmodule Explorer.Chain.SmartContract do
field(:verified_via_sourcify, :boolean)
field(:partially_verified, :boolean)
field(:file_path, :string)
+ field(:is_vyper_contract, :boolean)
+ field(:is_changed_bytecode, :boolean, default: false)
+ field(:bytecode_checked_at, :utc_datetime_usec, default: DateTime.add(DateTime.utc_now(), -86400, :second))
has_many(
:decompiled_smart_contracts,
@@ -259,14 +269,23 @@ defmodule Explorer.Chain.SmartContract do
:optimization_runs,
:verified_via_sourcify,
:partially_verified,
- :file_path
+ :file_path,
+ :is_vyper_contract,
+ :is_changed_bytecode,
+ :bytecode_checked_at
])
|> validate_required([:name, :compiler_version, :optimization, :contract_source_code, :abi, :address_hash])
|> unique_constraint(:address_hash)
|> prepare_changes(&upsert_contract_methods/1)
end
- def invalid_contract_changeset(%__MODULE__{} = smart_contract, attrs, error, error_message) do
+ def invalid_contract_changeset(
+ %__MODULE__{} = smart_contract,
+ attrs,
+ error,
+ error_message,
+ json_verification \\ false
+ ) do
validated =
smart_contract
|> cast(attrs, [
@@ -280,14 +299,22 @@ defmodule Explorer.Chain.SmartContract do
:constructor_arguments,
:verified_via_sourcify,
:partially_verified,
- :file_path
+ :file_path,
+ :is_vyper_contract,
+ :is_changed_bytecode,
+ :bytecode_checked_at
])
- |> validate_required([:name, :compiler_version, :optimization, :address_hash])
+ |> (&if(json_verification,
+ do: &1,
+ else: validate_required(&1, [:name, :compiler_version, :optimization, :address_hash])
+ )).()
+
+ field_to_put_message = if json_verification, do: :file, else: :contract_source_code
if error_message do
- add_error(validated, :contract_source_code, error_message(error, error_message))
+ add_error(validated, field_to_put_message, error_message(error, error_message))
else
- add_error(validated, :contract_source_code, error_message(error))
+ add_error(validated, field_to_put_message, error_message(error))
end
end
@@ -350,6 +377,7 @@ defmodule Explorer.Chain.SmartContract do
defp error_message(:generated_bytecode), do: "Bytecode does not match, please try again."
defp error_message(:constructor_arguments), do: "Constructor arguments do not match, please try again."
defp error_message(:name), do: "Wrong contract name, please try again."
+ defp error_message(:json), do: "Invalid JSON file."
defp error_message(_), do: "There was an error validating your contract, please try again."
defp error_message(:compilation, error_message), do: "There was an error compiling your contract: #{error_message}"
end
diff --git a/apps/explorer/lib/explorer/chain/smart_contract/verification_status.ex b/apps/explorer/lib/explorer/chain/smart_contract/verification_status.ex
new file mode 100644
index 000000000000..dd336dcf5ac6
--- /dev/null
+++ b/apps/explorer/lib/explorer/chain/smart_contract/verification_status.ex
@@ -0,0 +1,133 @@
+defmodule Explorer.Chain.SmartContract.VerificationStatus do
+ @moduledoc """
+ Represents single verification try
+ """
+
+ use Explorer.Schema
+
+ import Ecto.Changeset
+
+ alias Explorer.Chain.Hash
+ alias Explorer.{Chain, Repo}
+
+ @typedoc """
+ * `address_hash` - address of the contract which was tried to verify
+ * `status` - try status: :pending | :pass | :fail
+ * `uid` - unique verification try identifer
+ """
+
+ @type t :: %__MODULE__{
+ uid: String.t(),
+ address_hash: Hash.Address.t(),
+ status: non_neg_integer()
+ }
+
+ @primary_key false
+ schema "contract_verification_status" do
+ field(:uid, :string, primary_key: true)
+ field(:status, :integer)
+ field(:address_hash, Hash.Address)
+
+ timestamps()
+ end
+
+ @required_fields ~w(uid status address_hash)a
+
+ def changeset(%__MODULE__{} = struct, params \\ %{}) do
+ casted_params = encode_status(params)
+
+ struct
+ |> cast(casted_params, @required_fields)
+ |> validate_required(@required_fields)
+ end
+
+ def encode_status(%{status: status} = changeset) do
+ case status do
+ :pending ->
+ Map.put(changeset, :status, 0)
+
+ :pass ->
+ Map.put(changeset, :status, 1)
+
+ :fail ->
+ Map.put(changeset, :status, 2)
+
+ _ ->
+ changeset
+ end
+ end
+
+ def encode_status(changeset), do: changeset
+
+ def decode_status(number) when number in [0, 1, 2, 3] do
+ case number do
+ 0 ->
+ :pending
+
+ 1 ->
+ :pass
+
+ 2 ->
+ :fail
+
+ 3 ->
+ :unknown_uid
+ end
+ end
+
+ def insert_status(uid, status, address_hash) do
+ {:ok, hash} = if is_binary(address_hash), do: Chain.string_to_address_hash(address_hash), else: address_hash
+
+ %__MODULE__{}
+ |> changeset(%{uid: uid, status: status, address_hash: hash})
+ |> Repo.insert()
+ end
+
+ def update_status(uid, status) do
+ __MODULE__
+ |> Repo.get_by(uid: uid)
+ |> changeset(%{status: status})
+ |> Repo.update()
+ end
+
+ def fetch_status(uid) do
+ case validate_uid(uid) do
+ {:ok, valid_uid} ->
+ __MODULE__
+ |> Repo.get_by(uid: valid_uid)
+ |> (&if(is_nil(&1), do: 3, else: Map.get(&1, :status))).()
+ |> decode_status()
+
+ _ ->
+ :unknown_uid
+ end
+ end
+
+ def generate_uid(address_hash) do
+ case Chain.string_to_address_hash(address_hash) do
+ :error ->
+ nil
+
+ {:ok, %Hash{byte_count: 20, bytes: address_hash}} ->
+ address_encoded = Base.encode16(address_hash, case: :lower)
+ timestamp = DateTime.utc_now() |> DateTime.to_unix() |> Integer.to_string(16) |> String.downcase()
+ address_encoded <> timestamp
+ end
+ end
+
+ def validate_uid(<<_address::binary-size(40), timestamp_hex::binary>> = uid) do
+ case Integer.parse(timestamp_hex, 16) do
+ {timestamp, ""} ->
+ if DateTime.utc_now() |> DateTime.to_unix() > timestamp do
+ {:ok, uid}
+ else
+ :error
+ end
+
+ _ ->
+ :error
+ end
+ end
+
+ def validate_uid(_), do: :error
+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 29a93c507499..6a8022db352f 100644
--- a/apps/explorer/lib/explorer/chain/token_transfer.ex
+++ b/apps/explorer/lib/explorer/chain/token_transfer.ex
@@ -47,9 +47,11 @@ 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)
"""
@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,12 +63,16 @@ defmodule Explorer.Chain.TokenTransfer do
token_id: non_neg_integer() | nil,
transaction: %Ecto.Association.NotLoaded{} | Transaction.t(),
transaction_hash: Hash.Full.t(),
- log_index: non_neg_integer()
+ log_index: non_neg_integer(),
+ amounts: [Decimal.t()] | nil,
+ token_ids: [non_neg_integer()] | nil
}
@typep paging_options :: {:paging_options, PagingOptions.t()}
@constant "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
+ @erc1155_single_transfer_signature "0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62"
+ @erc1155_batch_transfer_signature "0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb"
@transfer_function_signature "0xa9059cbb"
@@ -76,6 +82,8 @@ defmodule Explorer.Chain.TokenTransfer do
field(:block_number, :integer)
field(:log_index, :integer, primary_key: true)
field(:token_id, :decimal)
+ 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)
@@ -115,7 +123,7 @@ defmodule Explorer.Chain.TokenTransfer do
end
@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)a
+ @optional_attrs ~w(amount token_id amounts token_ids)a
@doc false
def changeset(%TokenTransfer{} = struct, params \\ %{}) do
@@ -134,6 +142,10 @@ defmodule Explorer.Chain.TokenTransfer do
"""
def constant, do: @constant
+ 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
"""
@@ -207,6 +219,14 @@ defmodule Explorer.Chain.TokenTransfer do
where(query, [tt], tt.token_id < ^token_id)
end
+ def page_token_transfer(query, %PagingOptions{key: {block_number, log_index}, asc_order: true}) do
+ where(
+ query,
+ [tt],
+ tt.block_number > ^block_number or (tt.block_number == ^block_number and tt.log_index > ^log_index)
+ )
+ end
+
def page_token_transfer(query, %PagingOptions{key: {block_number, log_index}}) do
where(
query,
@@ -294,6 +314,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 bd8ca71267c8..08cd9b8c2ced 100644
--- a/apps/explorer/lib/explorer/chain/transaction.ex
+++ b/apps/explorer/lib/explorer/chain/transaction.ex
@@ -30,13 +30,10 @@ defmodule Explorer.Chain.Transaction do
alias Explorer.Chain.Transaction.{Fork, Status}
@optional_attrs ~w(max_priority_fee_per_gas max_fee_per_gas block_hash block_number created_contract_address_hash cumulative_gas_used earliest_processing_start
- error gas_used index created_contract_code_indexed_at status
- to_address_hash revert_reason)a
+ error gas_used index created_contract_code_indexed_at status to_address_hash revert_reason type has_error_in_internal_txs)a
@required_attrs ~w(from_address_hash gas gas_price hash input nonce r s v value)a
- @required_attrs_for_1559 ~w(type)a
-
@typedoc """
X coordinate module n in
[Elliptic Curve Digital Signature Algorithm](https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm)
@@ -137,6 +134,7 @@ defmodule Explorer.Chain.Transaction do
* `max_priority_fee_per_gas` - User defined maximum fee (tip) per unit of gas paid to validator for transaction prioritization.
* `max_fee_per_gas` - Maximum total amount per unit of gas a user is willing to pay for a transaction, including base fee and priority fee.
* `type` - New transaction type identifier introduced in EIP 2718 (Berlin HF)
+ * `has_error_in_internal_txs` - shows if the internal transactions related to transaction have errors
"""
@type t :: %__MODULE__{
block: %Ecto.Association.NotLoaded{} | Block.t() | nil,
@@ -168,10 +166,11 @@ defmodule Explorer.Chain.Transaction do
uncles: %Ecto.Association.NotLoaded{} | [Block.t()],
v: v(),
value: Wei.t(),
- revert_reason: String.t(),
+ revert_reason: String.t() | nil,
max_priority_fee_per_gas: wei_per_gas | nil,
max_fee_per_gas: wei_per_gas | nil,
- type: non_neg_integer() | nil
+ type: non_neg_integer() | nil,
+ has_error_in_internal_txs: boolean()
}
@derive {Poison.Encoder,
@@ -236,6 +235,7 @@ defmodule Explorer.Chain.Transaction do
field(:max_priority_fee_per_gas, Wei)
field(:max_fee_per_gas, Wei)
field(:type, :integer)
+ field(:has_error_in_internal_txs, :boolean)
# A transient field for deriving old block hash during transaction upserts.
# Used to force refetch of a block in case a transaction is re-collated
@@ -409,18 +409,11 @@ defmodule Explorer.Chain.Transaction do
"""
def changeset(%__MODULE__{} = transaction, attrs \\ %{}) do
- enabled_1559 = Application.get_env(:explorer, :enabled_1559_support)
-
- required_attrs = if enabled_1559, do: @required_attrs ++ @required_attrs_for_1559, else: @required_attrs
-
- attrs_to_cast =
- if enabled_1559,
- do: @required_attrs ++ @required_attrs_for_1559 ++ @optional_attrs,
- else: @required_attrs ++ @optional_attrs
+ attrs_to_cast = @required_attrs ++ @optional_attrs
transaction
|> cast(attrs, attrs_to_cast)
- |> validate_required(required_attrs)
+ |> validate_required(@required_attrs)
|> validate_collated()
|> validate_error()
|> validate_status()
@@ -444,6 +437,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}
@@ -483,7 +497,27 @@ defmodule Explorer.Chain.Transaction do
to_address: %{smart_contract: %{abi: abi, address_hash: address_hash}},
hash: hash
}) do
- do_decoded_input_data(data, abi, address_hash, hash)
+ case do_decoded_input_data(data, abi, address_hash, hash) do
+ # In some cases transactions use methods of some unpredictadle contracts, so we can try to look up for method in a whole DB
+ {:error, :could_not_decode} ->
+ case decoded_input_data(%__MODULE__{
+ to_address: %{smart_contract: nil},
+ input: %{bytes: data},
+ hash: hash
+ }) do
+ {:error, :contract_not_verified, []} ->
+ {:error, :could_not_decode}
+
+ {:error, :contract_not_verified, candidates} ->
+ {:error, :contract_verified, candidates}
+
+ _ ->
+ {:error, :could_not_decode}
+ end
+
+ output ->
+ output
+ end
end
defp do_decoded_input_data(data, abi, address_hash, hash) do
diff --git a/apps/explorer/lib/explorer/chain/transaction/history/historian.ex b/apps/explorer/lib/explorer/chain/transaction/history/historian.ex
index 664a5e7b294d..254ce53699ef 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,107 @@ 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)}")
+
+ from_api = false
+
+ with {:ok, min_block} <- Chain.timestamp_to_block_number(earliest, :after, from_api),
+ {:ok, max_block} <- Chain.timestamp_to_block_number(latest, :after, from_api) 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/wei.ex b/apps/explorer/lib/explorer/chain/wei.ex
index 71b68a6029ff..b77f71f90f4d 100644
--- a/apps/explorer/lib/explorer/chain/wei.ex
+++ b/apps/explorer/lib/explorer/chain/wei.ex
@@ -22,11 +22,6 @@ defmodule Explorer.Chain.Wei do
alias Explorer.Chain.Wei
- @derive {Jason.Encoder,
- except: [
- :__meta__
- ]}
-
defstruct ~w(value)a
use Ecto.Type
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..5dd1a825b10a 100644
--- a/apps/explorer/lib/explorer/counters/address_gas_usage_counter.ex
+++ b/apps/explorer/lib/explorer/counters/address_gas_usage_counter.ex
@@ -4,7 +4,8 @@ defmodule Explorer.Counters.AddressTransactionsGasUsageCounter do
"""
use GenServer
- alias Explorer.Chain
+ alias Ecto.Changeset
+ alias Explorer.{Chain, Repo}
@cache_name :address_transactions_gas_usage_counter
@last_update_key "last_update"
@@ -16,7 +17,7 @@ defmodule Explorer.Counters.AddressTransactionsGasUsageCounter do
read_concurrency: true
]
- config = Application.get_env(:explorer, Explorer.Counters.AddressTransactionsGasUsageCounter)
+ config = Application.get_env(:explorer, __MODULE__)
@enable_consolidation Keyword.get(config, :enable_consolidation)
@spec start_link(term()) :: GenServer.on_start()
@@ -48,12 +49,10 @@ 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)
+ address_hash_string = to_string(address.hash)
fetch_from_cache("hash_#{address_hash_string}")
end
@@ -61,7 +60,7 @@ defmodule Explorer.Counters.AddressTransactionsGasUsageCounter do
defp cache_expired?(address) do
cache_period = address_transactions_gas_usage_counter_cache_period()
- address_hash_string = get_address_hash_string(address)
+ address_hash_string = to_string(address.hash)
updated_at = fetch_from_cache("hash_#{address_hash_string}_#{@last_update_key}")
cond do
@@ -72,10 +71,11 @@ defmodule Explorer.Counters.AddressTransactionsGasUsageCounter do
end
defp update_cache(address) do
- address_hash_string = get_address_hash_string(address)
+ address_hash_string = to_string(address.hash)
put_into_cache("hash_#{address_hash_string}_#{@last_update_key}", current_time())
new_data = Chain.address_to_gas_usage_count(address)
put_into_cache("hash_#{address_hash_string}", new_data)
+ put_into_db(address, new_data)
end
defp fetch_from_cache(key) do
@@ -92,8 +92,12 @@ defmodule Explorer.Counters.AddressTransactionsGasUsageCounter do
:ets.insert(@cache_name, {key, value})
end
- defp get_address_hash_string(address) do
- Base.encode16(address.hash.bytes, case: :lower)
+ defp put_into_db(_address, value) when is_nil(value), do: :ignore
+
+ defp put_into_db(address, value) do
+ address
+ |> Changeset.change(%{gas_used: Decimal.to_integer(value)})
+ |> Repo.update()
end
defp current_time do
diff --git a/apps/explorer/lib/explorer/counters/address_token_transfers_counter.ex b/apps/explorer/lib/explorer/counters/address_token_transfers_counter.ex
new file mode 100644
index 000000000000..3cad4650c599
--- /dev/null
+++ b/apps/explorer/lib/explorer/counters/address_token_transfers_counter.ex
@@ -0,0 +1,121 @@
+defmodule Explorer.Counters.AddressTokenTransfersCounter do
+ @moduledoc """
+ Caches Address token transfers counter.
+ """
+ use GenServer
+
+ alias Ecto.Changeset
+ alias Explorer.{Chain, Repo}
+
+ @cache_name :address_token_transfers_counter
+ @last_update_key "last_update"
+
+ @ets_opts [
+ :set,
+ :named_table,
+ :public,
+ read_concurrency: true
+ ]
+
+ config = Application.get_env(:explorer, __MODULE__)
+ @enable_consolidation Keyword.get(config, :enable_consolidation)
+
+ @spec start_link(term()) :: GenServer.on_start()
+ def start_link(_) do
+ GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
+ end
+
+ @impl true
+ def init(_args) do
+ create_cache_table()
+
+ {:ok, %{consolidate?: enable_consolidation?()}, {:continue, :ok}}
+ end
+
+ @impl true
+ def handle_continue(:ok, %{consolidate?: true} = state) do
+ {:noreply, state}
+ end
+
+ @impl true
+ def handle_continue(:ok, state) do
+ {:noreply, state}
+ end
+
+ @impl true
+ def handle_info(:consolidate, state) do
+ {:noreply, state}
+ end
+
+ def fetch(address) do
+ if cache_expired?(address) do
+ update_cache(address)
+ end
+
+ address_hash_string = to_string(address.hash)
+ fetch_from_cache("hash_#{address_hash_string}")
+ end
+
+ def cache_name, do: @cache_name
+
+ defp cache_expired?(address) do
+ cache_period = address_token_transfers_counter_cache_period()
+ address_hash_string = to_string(address.hash)
+ updated_at = fetch_from_cache("hash_#{address_hash_string}_#{@last_update_key}")
+
+ cond do
+ is_nil(updated_at) -> true
+ current_time() - updated_at > cache_period -> true
+ true -> false
+ end
+ end
+
+ defp update_cache(address) do
+ address_hash_string = to_string(address.hash)
+ put_into_cache("hash_#{address_hash_string}_#{@last_update_key}", current_time())
+ new_data = Chain.address_to_token_transfer_count(address)
+ put_into_cache("hash_#{address_hash_string}", new_data)
+ put_into_db(address, new_data)
+ end
+
+ defp fetch_from_cache(key) do
+ case :ets.lookup(@cache_name, key) do
+ [{_, value}] ->
+ value
+
+ [] ->
+ 0
+ end
+ end
+
+ defp put_into_cache(key, value) do
+ :ets.insert(@cache_name, {key, value})
+ end
+
+ defp current_time do
+ utc_now = DateTime.utc_now()
+
+ DateTime.to_unix(utc_now, :millisecond)
+ end
+
+ def create_cache_table do
+ if :ets.whereis(@cache_name) == :undefined do
+ :ets.new(@cache_name, @ets_opts)
+ end
+ end
+
+ def enable_consolidation?, do: @enable_consolidation
+
+ defp address_token_transfers_counter_cache_period do
+ case Integer.parse(System.get_env("ADDRESS_TOKEN_TRANSFERS_COUNTER_CACHE_PERIOD", "")) do
+ {secs, ""} -> :timer.seconds(secs)
+ _ -> :timer.hours(1)
+ end
+ end
+
+ defp put_into_db(address, value) do
+ address
+ |> Changeset.change(%{token_transfers_count: value})
+ |> Repo.update()
+ end
+end
diff --git a/apps/explorer/lib/explorer/counters/address_transactions_counter.ex b/apps/explorer/lib/explorer/counters/address_transactions_counter.ex
index 2116ec3b4df2..0ad5dc655a9d 100644
--- a/apps/explorer/lib/explorer/counters/address_transactions_counter.ex
+++ b/apps/explorer/lib/explorer/counters/address_transactions_counter.ex
@@ -4,7 +4,8 @@ defmodule Explorer.Counters.AddressTransactionsCounter do
"""
use GenServer
- alias Explorer.Chain
+ alias Ecto.Changeset
+ alias Explorer.{Chain, Repo}
@cache_name :address_transactions_counter
@last_update_key "last_update"
@@ -16,7 +17,7 @@ defmodule Explorer.Counters.AddressTransactionsCounter do
read_concurrency: true
]
- config = Application.get_env(:explorer, Explorer.Counters.AddressTransactionsCounter)
+ config = Application.get_env(:explorer, __MODULE__)
@enable_consolidation Keyword.get(config, :enable_consolidation)
@spec start_link(term()) :: GenServer.on_start()
@@ -48,12 +49,10 @@ 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)
+ address_hash_string = to_string(address.hash)
fetch_from_cache("hash_#{address_hash_string}")
end
@@ -61,7 +60,7 @@ defmodule Explorer.Counters.AddressTransactionsCounter do
defp cache_expired?(address) do
cache_period = address_transactions_counter_cache_period()
- address_hash_string = get_address_hash_string(address)
+ address_hash_string = to_string(address.hash)
updated_at = fetch_from_cache("hash_#{address_hash_string}_#{@last_update_key}")
cond do
@@ -72,10 +71,11 @@ defmodule Explorer.Counters.AddressTransactionsCounter do
end
defp update_cache(address) do
- address_hash_string = get_address_hash_string(address)
+ address_hash_string = to_string(address.hash)
put_into_cache("hash_#{address_hash_string}_#{@last_update_key}", current_time())
new_data = Chain.address_to_transaction_count(address)
put_into_cache("hash_#{address_hash_string}", new_data)
+ put_into_db(address, new_data)
end
defp fetch_from_cache(key) do
@@ -92,10 +92,6 @@ defmodule Explorer.Counters.AddressTransactionsCounter do
:ets.insert(@cache_name, {key, value})
end
- defp get_address_hash_string(address) do
- Base.encode16(address.hash.bytes, case: :lower)
- end
-
defp current_time do
utc_now = DateTime.utc_now()
@@ -116,4 +112,10 @@ defmodule Explorer.Counters.AddressTransactionsCounter do
_ -> :timer.hours(1)
end
end
+
+ defp put_into_db(address, value) do
+ address
+ |> Changeset.change(%{transactions_count: value})
+ |> Repo.update()
+ end
end
diff --git a/apps/explorer/lib/explorer/counters/token_holders_counter.ex b/apps/explorer/lib/explorer/counters/token_holders_counter.ex
index 00681b985f1b..98c7164bc535 100644
--- a/apps/explorer/lib/explorer/counters/token_holders_counter.ex
+++ b/apps/explorer/lib/explorer/counters/token_holders_counter.ex
@@ -48,12 +48,10 @@ defmodule Explorer.Counters.TokenHoldersCounter do
def fetch(address_hash) do
if cache_expired?(address_hash) do
- Task.start_link(fn ->
- update_cache(address_hash)
- end)
+ update_cache(address_hash)
end
- address_hash_string = get_address_hash_string(address_hash)
+ address_hash_string = to_string(address_hash)
fetch_from_cache("hash_#{address_hash_string}")
end
@@ -61,7 +59,7 @@ defmodule Explorer.Counters.TokenHoldersCounter do
defp cache_expired?(address_hash) do
cache_period = token_holders_counter_cache_period()
- address_hash_string = get_address_hash_string(address_hash)
+ address_hash_string = to_string(address_hash)
updated_at = fetch_from_cache("hash_#{address_hash_string}_#{@last_update_key}")
cond do
@@ -72,7 +70,7 @@ defmodule Explorer.Counters.TokenHoldersCounter do
end
defp update_cache(address_hash) do
- address_hash_string = get_address_hash_string(address_hash)
+ address_hash_string = to_string(address_hash)
put_into_cache("hash_#{address_hash_string}_#{@last_update_key}", current_time())
new_data = Chain.count_token_holders_from_token_hash(address_hash)
put_into_cache("hash_#{address_hash_string}", new_data)
@@ -92,10 +90,6 @@ defmodule Explorer.Counters.TokenHoldersCounter do
:ets.insert(@cache_name, {key, value})
end
- defp get_address_hash_string(address_hash) do
- Base.encode16(address_hash.bytes, case: :lower)
- end
-
defp current_time do
utc_now = DateTime.utc_now()
diff --git a/apps/explorer/lib/explorer/counters/token_transfers_counter.ex b/apps/explorer/lib/explorer/counters/token_transfers_counter.ex
index 2026be651984..856c81b68eab 100644
--- a/apps/explorer/lib/explorer/counters/token_transfers_counter.ex
+++ b/apps/explorer/lib/explorer/counters/token_transfers_counter.ex
@@ -48,12 +48,10 @@ defmodule Explorer.Counters.TokenTransfersCounter do
def fetch(address_hash) do
if cache_expired?(address_hash) do
- Task.start_link(fn ->
- update_cache(address_hash)
- end)
+ update_cache(address_hash)
end
- address_hash_string = get_address_hash_string(address_hash)
+ address_hash_string = to_string(address_hash)
fetch_from_cache("hash_#{address_hash_string}")
end
@@ -61,7 +59,7 @@ defmodule Explorer.Counters.TokenTransfersCounter do
defp cache_expired?(address_hash) do
cache_period = token_transfers_counter_cache_period()
- address_hash_string = get_address_hash_string(address_hash)
+ address_hash_string = to_string(address_hash)
updated_at = fetch_from_cache("hash_#{address_hash_string}_#{@last_update_key}")
cond do
@@ -72,7 +70,7 @@ defmodule Explorer.Counters.TokenTransfersCounter do
end
defp update_cache(address_hash) do
- address_hash_string = get_address_hash_string(address_hash)
+ address_hash_string = to_string(address_hash)
put_into_cache("hash_#{address_hash_string}_#{@last_update_key}", current_time())
new_data = Chain.count_token_transfers_from_token_hash(address_hash)
put_into_cache("hash_#{address_hash_string}", new_data)
@@ -92,10 +90,6 @@ defmodule Explorer.Counters.TokenTransfersCounter do
:ets.insert(@cache_name, {key, value})
end
- defp get_address_hash_string(address_hash) do
- Base.encode16(address_hash.bytes, case: :lower)
- end
-
defp current_time do
utc_now = DateTime.utc_now()
diff --git a/apps/explorer/lib/explorer/eth_rpc.ex b/apps/explorer/lib/explorer/eth_rpc.ex
index b7345a70aab7..ed7e6830802b 100644
--- a/apps/explorer/lib/explorer/eth_rpc.ex
+++ b/apps/explorer/lib/explorer/eth_rpc.ex
@@ -1,13 +1,13 @@
defmodule Explorer.EthRPC do
@moduledoc """
- Ethreum JSON RPC methods logic implementation.
+ Ethereum JSON RPC methods logic implementation.
"""
alias Ecto.Type, as: EctoType
alias Explorer.{Chain, Repo}
alias Explorer.Chain.{Block, Data, Hash, Hash.Address, Wei}
alias Explorer.Chain.Cache.BlockNumber
- alias Explorer.Etherscan.Logs
+ alias Explorer.Etherscan.{Blocks, Logs, RPC}
@methods %{
"eth_blockNumber" => %{
@@ -119,7 +119,7 @@ defmodule Explorer.EthRPC do
def eth_get_balance(address_param, block_param \\ nil) do
with {:address, {:ok, address}} <- {:address, Chain.string_to_address_hash(address_param)},
{:block, {:ok, block}} <- {:block, block_param(block_param)},
- {:balance, {:ok, balance}} <- {:balance, Chain.get_balance_as_of_block(address, block)} do
+ {:balance, {:ok, balance}} <- {:balance, Blocks.get_balance_as_of_block(address, block)} do
{:ok, Wei.hex_format(balance)}
else
{:address, :error} ->
@@ -262,7 +262,7 @@ defmodule Explorer.EthRPC do
defp logs_blocks_filter(filter_options) do
with {:filter, %{"blockHash" => block_hash_param}} <- {:filter, filter_options},
{:block_hash, {:ok, block_hash}} <- {:block_hash, Hash.Full.cast(block_hash_param)},
- {:block, %{number: number}} <- {:block, Repo.get(Block, block_hash)} do
+ {:block, %{number: number}} <- {:block, Repo.replica().get(Block, block_hash)} do
{:ok, number, number}
else
{:filter, filters} ->
@@ -356,7 +356,7 @@ defmodule Explorer.EthRPC do
defp to_number(_, error_message), do: {:error, error_message}
defp max_non_consensus_block_number(max) do
- case Chain.max_non_consensus_block_number(max) do
+ case RPC.max_non_consensus_block_number(max) do
{:ok, number} -> number
_ -> nil
end
diff --git a/apps/explorer/lib/explorer/etherscan.ex b/apps/explorer/lib/explorer/etherscan.ex
index 1e888c66f2aa..406fa4bc3f39 100644
--- a/apps/explorer/lib/explorer/etherscan.ex
+++ b/apps/explorer/lib/explorer/etherscan.ex
@@ -8,7 +8,8 @@ defmodule Explorer.Etherscan do
alias Explorer.Etherscan.Logs
alias Explorer.{Chain, Repo}
alias Explorer.Chain.Address.{CurrentTokenBalance, TokenBalance}
- alias Explorer.Chain.{Block, Hash, InternalTransaction, TokenTransfer, Transaction}
+ alias Explorer.Chain.{Address, Block, Hash, InternalTransaction, TokenTransfer, Transaction}
+ alias Explorer.Chain.Transaction.History.TransactionStats
@default_options %{
order_by_direction: :desc,
@@ -20,6 +21,8 @@ defmodule Explorer.Etherscan do
end_timestamp: nil
}
+ @burn_address_hash_str "0x0000000000000000000000000000000000000000"
+
@doc """
Returns the maximum allowed page size number.
@@ -115,7 +118,7 @@ defmodule Explorer.Etherscan do
|> Chain.where_transaction_has_multiple_internal_transactions()
|> InternalTransaction.where_is_different_from_parent_transaction()
|> InternalTransaction.where_nonpending_block()
- |> Repo.all()
+ |> Repo.replica().all()
end
@doc """
@@ -198,7 +201,7 @@ defmodule Explorer.Etherscan do
query_to_address_hash_wrapped
|> union(^query_from_address_hash_wrapped)
|> union(^query_created_contract_address_hash_wrapped)
- |> Repo.all()
+ |> Repo.replica().all()
else
query =
from(
@@ -222,7 +225,7 @@ defmodule Explorer.Etherscan do
|> where_start_block_match(options)
|> where_end_block_match(options)
|> InternalTransaction.where_nonpending_block()
- |> Repo.all()
+ |> Repo.replica().all()
end
end
@@ -279,7 +282,7 @@ defmodule Explorer.Etherscan do
}
)
- Repo.all(query)
+ Repo.replica().all(query)
end
@doc """
@@ -300,7 +303,7 @@ defmodule Explorer.Etherscan do
select: ctb
)
- Repo.one(query)
+ Repo.replica().one(query)
end
@doc """
@@ -326,7 +329,7 @@ defmodule Explorer.Etherscan do
}
)
- Repo.all(query)
+ Repo.replica().all(query)
end
@transaction_fields ~w(
@@ -377,7 +380,7 @@ defmodule Explorer.Etherscan do
|> where_address_match(address_hash, options)
|> Chain.pending_transactions_query()
|> order_by([transaction], desc: transaction.inserted_at, desc: transaction.hash)
- |> Repo.all()
+ |> Repo.replica().all()
end
defp list_transactions(address_hash, max_block_number, options) do
@@ -401,7 +404,7 @@ defmodule Explorer.Etherscan do
|> where_end_block_match(options)
|> where_start_timestamp_match(options)
|> where_end_timestamp_match(options)
- |> Repo.all()
+ |> Repo.replica().all()
end
defp where_address_match(query, address_hash, %{filter_by: "to"}) do
@@ -490,7 +493,7 @@ defmodule Explorer.Etherscan do
wrapped_query
|> where_start_block_match(options)
|> where_end_block_match(options)
- |> Repo.all()
+ |> Repo.replica().all()
end
defp where_start_block_match(query, %{start_block: nil}), do: query
@@ -556,4 +559,53 @@ defmodule Explorer.Etherscan do
"""
@spec list_logs(map()) :: [map()]
def list_logs(filter), do: Logs.list_logs(filter)
+
+ @spec fetch_sum_coin_total_supply() :: non_neg_integer
+ def fetch_sum_coin_total_supply do
+ query =
+ from(
+ a0 in Address,
+ select: fragment("SUM(a0.fetched_coin_balance)"),
+ where: a0.fetched_coin_balance > ^0
+ )
+
+ Repo.replica().one!(query, timeout: :infinity) || 0
+ end
+
+ @spec fetch_sum_coin_total_supply_minus_burnt() :: non_neg_integer
+ def fetch_sum_coin_total_supply_minus_burnt do
+ {:ok, burn_address_hash} = Chain.string_to_address_hash(@burn_address_hash_str)
+
+ query =
+ from(
+ a0 in Address,
+ select: fragment("SUM(a0.fetched_coin_balance)"),
+ where: a0.hash != ^burn_address_hash,
+ where: a0.fetched_coin_balance > ^0
+ )
+
+ Repo.replica().one!(query, timeout: :infinity) || 0
+ end
+
+ @doc """
+ It is used by `totalfees` API endpoint of `stats` module for retrieving of total fee per day
+ """
+ @spec get_total_fees_per_day(String.t()) :: {:ok, non_neg_integer() | nil} | {:error, String.t()}
+ def get_total_fees_per_day(date_string) do
+ case Date.from_iso8601(date_string) do
+ {:ok, date} ->
+ query =
+ from(
+ tx_stats in TransactionStats,
+ where: tx_stats.date == ^date,
+ select: tx_stats.total_fee
+ )
+
+ total_fees = Repo.replica().one(query)
+ {:ok, total_fees}
+
+ _ ->
+ {:error, "An incorrect input date provided. It should be in ISO 8601 format (yyyy-mm-dd)."}
+ end
+ end
end
diff --git a/apps/explorer/lib/explorer/etherscan/addresses.ex b/apps/explorer/lib/explorer/etherscan/addresses.ex
new file mode 100644
index 000000000000..beb69d051d38
--- /dev/null
+++ b/apps/explorer/lib/explorer/etherscan/addresses.ex
@@ -0,0 +1,28 @@
+defmodule Explorer.Etherscan.Addresses do
+ @moduledoc """
+ This module contains functions for working with addresses, as they pertain to the
+ `Explorer.Etherscan` context.
+
+ """
+
+ import Ecto.Query,
+ only: [
+ from: 2
+ ]
+
+ alias Explorer.Chain.Address
+ alias Explorer.Repo
+
+ @spec list_ordered_addresses(non_neg_integer(), non_neg_integer()) :: [Address.t()]
+ def list_ordered_addresses(offset, limit) do
+ query =
+ from(
+ address in Address,
+ order_by: [asc: address.inserted_at],
+ offset: ^offset,
+ limit: ^limit
+ )
+
+ Repo.replica().all(query)
+ end
+end
diff --git a/apps/explorer/lib/explorer/etherscan/blocks.ex b/apps/explorer/lib/explorer/etherscan/blocks.ex
new file mode 100644
index 000000000000..d7238fb7a20f
--- /dev/null
+++ b/apps/explorer/lib/explorer/etherscan/blocks.ex
@@ -0,0 +1,95 @@
+defmodule Explorer.Etherscan.Blocks do
+ @moduledoc """
+ This module contains functions for working with blocks, as they pertain to the
+ `Explorer.Etherscan` context.
+
+ """
+
+ import Ecto.Query,
+ only: [
+ from: 2
+ ]
+
+ alias Explorer.{Chain, Repo}
+ alias Explorer.Chain.{Address.CoinBalance, Block, Hash, Wei}
+
+ @doc """
+ Returns the balance of the given address and block combination.
+
+ Returns `{:error, :not_found}` if there is no address by that hash present.
+ Returns `{:error, :no_balance}` if there is no balance for that address at that block.
+ """
+ @spec get_balance_as_of_block(Hash.Address.t(), Block.block_number() | :earliest | :latest | :pending) ::
+ {:ok, Wei.t()} | {:error, :no_balance} | {:error, :not_found}
+ def get_balance_as_of_block(address, block) when is_integer(block) do
+ coin_balance_query =
+ from(coin_balance in CoinBalance,
+ where: coin_balance.address_hash == ^address,
+ where: not is_nil(coin_balance.value),
+ where: coin_balance.block_number <= ^block,
+ order_by: [desc: coin_balance.block_number],
+ limit: 1,
+ select: coin_balance.value
+ )
+
+ case Repo.replica().one(coin_balance_query) do
+ nil -> {:error, :not_found}
+ coin_balance -> {:ok, coin_balance}
+ end
+ end
+
+ def get_balance_as_of_block(address, :latest) do
+ case Chain.max_consensus_block_number() do
+ {:ok, latest_block_number} ->
+ get_balance_as_of_block(address, latest_block_number)
+
+ {:error, :not_found} ->
+ {:error, :not_found}
+ end
+ end
+
+ def get_balance_as_of_block(address, :earliest) do
+ query =
+ from(coin_balance in CoinBalance,
+ where: coin_balance.address_hash == ^address,
+ where: not is_nil(coin_balance.value),
+ where: coin_balance.block_number == 0,
+ limit: 1,
+ select: coin_balance.value
+ )
+
+ case Repo.replica().one(query) do
+ nil -> {:error, :not_found}
+ coin_balance -> {:ok, coin_balance}
+ end
+ end
+
+ def get_balance_as_of_block(address, :pending) do
+ query =
+ case Chain.max_consensus_block_number() do
+ {:ok, latest_block_number} ->
+ from(coin_balance in CoinBalance,
+ where: coin_balance.address_hash == ^address,
+ where: not is_nil(coin_balance.value),
+ where: coin_balance.block_number > ^latest_block_number,
+ order_by: [desc: coin_balance.block_number],
+ limit: 1,
+ select: coin_balance.value
+ )
+
+ {:error, :not_found} ->
+ from(coin_balance in CoinBalance,
+ where: coin_balance.address_hash == ^address,
+ where: not is_nil(coin_balance.value),
+ order_by: [desc: coin_balance.block_number],
+ limit: 1,
+ select: coin_balance.value
+ )
+ end
+
+ case Repo.replica().one(query) do
+ nil -> {:error, :not_found}
+ coin_balance -> {:ok, coin_balance}
+ end
+ end
+end
diff --git a/apps/explorer/lib/explorer/etherscan/contracts.ex b/apps/explorer/lib/explorer/etherscan/contracts.ex
new file mode 100644
index 000000000000..c265a40845c9
--- /dev/null
+++ b/apps/explorer/lib/explorer/etherscan/contracts.ex
@@ -0,0 +1,167 @@
+defmodule Explorer.Etherscan.Contracts do
+ @moduledoc """
+ This module contains functions for working with contracts, as they pertain to the
+ `Explorer.Etherscan` context.
+
+ """
+
+ import Ecto.Query,
+ only: [
+ from: 2
+ ]
+
+ alias Explorer.{Chain, Repo}
+ alias Explorer.Chain.{Address, Hash, SmartContract}
+
+ @spec address_hash_to_address_with_source_code(Hash.Address.t()) :: Address.t() | nil
+ def address_hash_to_address_with_source_code(address_hash) do
+ case Repo.replica().get(Address, address_hash) do
+ nil ->
+ nil
+
+ address ->
+ address_with_smart_contract =
+ Repo.replica().preload(address, [
+ :smart_contract,
+ :decompiled_smart_contracts,
+ :smart_contract_additional_sources
+ ])
+
+ if address_with_smart_contract.smart_contract do
+ formatted_code = format_source_code_output(address_with_smart_contract.smart_contract)
+
+ %{
+ address_with_smart_contract
+ | smart_contract: %{address_with_smart_contract.smart_contract | contract_source_code: formatted_code}
+ }
+ else
+ address_verified_twin_contract =
+ Chain.get_minimal_proxy_template(address_hash) ||
+ Chain.get_address_verified_twin_contract(address_hash).verified_contract
+
+ if address_verified_twin_contract do
+ formatted_code = format_source_code_output(address_verified_twin_contract)
+
+ %{
+ address_with_smart_contract
+ | smart_contract: %{address_verified_twin_contract | contract_source_code: formatted_code}
+ }
+ else
+ address_with_smart_contract
+ end
+ end
+ end
+ end
+
+ def list_verified_contracts(limit, offset) do
+ query =
+ from(
+ smart_contract in SmartContract,
+ order_by: [asc: smart_contract.inserted_at],
+ limit: ^limit,
+ offset: ^offset,
+ preload: [:address]
+ )
+
+ query
+ |> Repo.replica().all()
+ |> Enum.map(fn smart_contract ->
+ Map.put(smart_contract.address, :smart_contract, smart_contract)
+ end)
+ end
+
+ def list_decompiled_contracts(limit, offset, not_decompiled_with_version \\ nil) do
+ query =
+ from(
+ address in Address,
+ where: address.contract_code != <<>>,
+ where: not is_nil(address.contract_code),
+ where: address.decompiled == true,
+ limit: ^limit,
+ offset: ^offset,
+ order_by: [asc: address.inserted_at],
+ preload: [:smart_contract]
+ )
+
+ query
+ |> reject_decompiled_with_version(not_decompiled_with_version)
+ |> Repo.replica().all()
+ end
+
+ def list_unordered_unverified_contracts(limit, offset) do
+ query =
+ from(
+ address in Address,
+ where: address.contract_code != <<>>,
+ where: not is_nil(address.contract_code),
+ where: fragment("? IS NOT TRUE", address.verified),
+ limit: ^limit,
+ offset: ^offset
+ )
+
+ query
+ |> Repo.replica().all()
+ |> Enum.map(fn address ->
+ %{address | smart_contract: nil}
+ end)
+ end
+
+ def list_unordered_not_decompiled_contracts(limit, offset) do
+ query =
+ from(
+ address in Address,
+ where: fragment("? IS NOT TRUE", address.verified),
+ where: fragment("? IS NOT TRUE", address.decompiled),
+ where: address.contract_code != <<>>,
+ where: not is_nil(address.contract_code),
+ limit: ^limit,
+ offset: ^offset
+ )
+
+ query
+ |> Repo.replica().all()
+ |> Enum.map(fn address ->
+ %{address | smart_contract: nil}
+ end)
+ end
+
+ def list_empty_contracts(limit, offset) do
+ query =
+ from(address in Address,
+ where: address.contract_code == <<>>,
+ preload: [:smart_contract, :decompiled_smart_contracts],
+ order_by: [asc: address.inserted_at],
+ limit: ^limit,
+ offset: ^offset
+ )
+
+ Repo.replica().all(query)
+ end
+
+ def list_contracts(limit, offset) do
+ query =
+ from(
+ address in Address,
+ where: not is_nil(address.contract_code),
+ preload: [:smart_contract],
+ order_by: [asc: address.inserted_at],
+ limit: ^limit,
+ offset: ^offset
+ )
+
+ Repo.replica().all(query)
+ end
+
+ defp format_source_code_output(smart_contract), do: smart_contract.contract_source_code
+
+ defp reject_decompiled_with_version(query, nil), do: query
+
+ defp reject_decompiled_with_version(query, reject_version) do
+ from(
+ address in query,
+ left_join: decompiled_smart_contract in assoc(address, :decompiled_smart_contracts),
+ on: decompiled_smart_contract.decompiler_version == ^reject_version,
+ where: is_nil(decompiled_smart_contract.address_hash)
+ )
+ end
+end
diff --git a/apps/explorer/lib/explorer/etherscan/logs.ex b/apps/explorer/lib/explorer/etherscan/logs.ex
index c19a3c7d4f8c..006c0a2e191b 100644
--- a/apps/explorer/lib/explorer/etherscan/logs.ex
+++ b/apps/explorer/lib/explorer/etherscan/logs.ex
@@ -146,7 +146,7 @@ defmodule Explorer.Etherscan.Logs do
query_with_consensus
|> order_by([log], asc: log.index)
|> page_logs(paging_options)
- |> Repo.all()
+ |> Repo.replica().all()
end
# Since address_hash was not present, we know that a
@@ -198,7 +198,7 @@ defmodule Explorer.Etherscan.Logs do
query_with_block_transaction_data
|> order_by([log], asc: log.index)
|> page_logs(paging_options)
- |> Repo.all()
+ |> Repo.replica().all()
end
@topics [
diff --git a/apps/explorer/lib/explorer/etherscan/rpc.ex b/apps/explorer/lib/explorer/etherscan/rpc.ex
new file mode 100644
index 000000000000..efb163d64351
--- /dev/null
+++ b/apps/explorer/lib/explorer/etherscan/rpc.ex
@@ -0,0 +1,40 @@
+defmodule Explorer.Etherscan.RPC do
+ @moduledoc """
+ This module contains functions for working with mimicking of ETH RPC.
+
+ """
+
+ import Ecto.Query,
+ only: [
+ from: 2
+ ]
+
+ alias Explorer.{Chain, Repo}
+ alias Explorer.Chain.Block
+
+ @spec max_non_consensus_block_number(integer | nil) :: {:ok, Block.block_number()} | {:error, :not_found}
+ def max_non_consensus_block_number(max_consensus_block_number \\ nil) do
+ max =
+ if max_consensus_block_number do
+ {:ok, max_consensus_block_number}
+ else
+ Chain.max_consensus_block_number()
+ end
+
+ case max do
+ {:ok, number} ->
+ query =
+ from(block in Block,
+ where: block.consensus == false,
+ where: block.number > ^number
+ )
+
+ query
+ |> Repo.replica().aggregate(:max, :number)
+ |> case do
+ nil -> {:error, :not_found}
+ number -> {:ok, number}
+ end
+ end
+ end
+end
diff --git a/apps/explorer/lib/explorer/exchange_rates/token.ex b/apps/explorer/lib/explorer/exchange_rates/token.ex
index 9eadf020e45f..6521181c1234 100644
--- a/apps/explorer/lib/explorer/exchange_rates/token.ex
+++ b/apps/explorer/lib/explorer/exchange_rates/token.ex
@@ -30,11 +30,6 @@ defmodule Explorer.ExchangeRates.Token do
volume_24h_usd: Decimal.t()
}
- @derive {Jason.Encoder,
- except: [
- :__meta__
- ]}
-
@enforce_keys ~w(available_supply total_supply btc_value id last_updated market_cap_usd name symbol usd_value volume_24h_usd)a
defstruct ~w(available_supply total_supply btc_value id last_updated market_cap_usd name symbol usd_value volume_24h_usd)a
diff --git a/apps/explorer/lib/explorer/graphql.ex b/apps/explorer/lib/explorer/graphql.ex
index 1541b5382e52..e892ef584ac8 100644
--- a/apps/explorer/lib/explorer/graphql.ex
+++ b/apps/explorer/lib/explorer/graphql.ex
@@ -40,7 +40,7 @@ defmodule Explorer.GraphQL do
"""
@spec get_internal_transaction(map()) :: {:ok, InternalTransaction.t()} | {:error, String.t()}
def get_internal_transaction(%{transaction_hash: _, index: _} = clauses) do
- if internal_transaction = Repo.get_by(InternalTransaction.where_nonpending_block(), clauses) do
+ if internal_transaction = Repo.replica().get_by(InternalTransaction.where_nonpending_block(), clauses) do
{:ok, internal_transaction}
else
{:error, "Internal transaction not found."}
@@ -75,7 +75,7 @@ defmodule Explorer.GraphQL do
"""
@spec get_token_transfer(map()) :: {:ok, TokenTransfer.t()} | {:error, String.t()}
def get_token_transfer(%{transaction_hash: _, log_index: _} = clauses) do
- if token_transfer = Repo.get_by(TokenTransfer, clauses) do
+ if token_transfer = Repo.replica().get_by(TokenTransfer, clauses) do
{:ok, token_transfer}
else
{:error, "Token transfer not found."}
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/market/market.ex b/apps/explorer/lib/explorer/market/market.ex
index e080fb7cf7f7..b7182355d838 100644
--- a/apps/explorer/lib/explorer/market/market.ex
+++ b/apps/explorer/lib/explorer/market/market.ex
@@ -84,8 +84,8 @@ defmodule Explorer.Market do
def add_price(tokens) when is_list(tokens) do
Enum.map(tokens, fn item ->
case item do
- {token_balance, bridged_token} ->
- {add_price(token_balance), bridged_token}
+ {token_balance, bridged_token, token} ->
+ {add_price(token_balance), bridged_token, token}
token_balance ->
add_price(token_balance)
diff --git a/apps/explorer/lib/explorer/paging_options.ex b/apps/explorer/lib/explorer/paging_options.ex
index cd7cbb28822c..9af5b6bd6b03 100644
--- a/apps/explorer/lib/explorer/paging_options.ex
+++ b/apps/explorer/lib/explorer/paging_options.ex
@@ -9,7 +9,8 @@ defmodule Explorer.PagingOptions do
page_size: page_size,
page_number: page_number,
is_pending_tx: is_pending_tx,
- is_index_in_asc_order: is_index_in_asc_order
+ is_index_in_asc_order: is_index_in_asc_order,
+ asc_order: asc_order
}
@typep key :: any()
@@ -17,6 +18,7 @@ defmodule Explorer.PagingOptions do
@typep page_number :: pos_integer()
@typep is_pending_tx :: atom()
@typep is_index_in_asc_order :: atom()
+ @typep asc_order :: atom()
- defstruct [:key, :page_size, page_number: 1, is_pending_tx: false, is_index_in_asc_order: false]
+ defstruct [:key, :page_size, page_number: 1, is_pending_tx: false, is_index_in_asc_order: false, asc_order: false]
end
diff --git a/apps/explorer/lib/explorer/repo.ex b/apps/explorer/lib/explorer/repo.ex
index 1bd5adda1e1b..14a48d9d335b 100644
--- a/apps/explorer/lib/explorer/repo.ex
+++ b/apps/explorer/lib/explorer/repo.ex
@@ -5,12 +5,28 @@ defmodule Explorer.Repo do
require Logger
+ alias Explorer.Repo.ConfigHelper
+
@doc """
Dynamically loads the repository url from the
DATABASE_URL environment variable.
"""
def init(_, opts) do
- {:ok, Keyword.put(opts, :url, System.get_env("DATABASE_URL"))}
+ db_url = System.get_env("DATABASE_URL")
+ repo_conf = Application.get_env(:explorer, Explorer.Repo)
+
+ merged =
+ %{url: db_url}
+ |> ConfigHelper.get_db_config()
+ |> Keyword.merge(repo_conf, fn
+ _key, v1, nil -> v1
+ _key, nil, v2 -> v2
+ _, _, v2 -> v2
+ end)
+
+ Application.put_env(:explorer, Explorer.Repo, merged)
+
+ {:ok, Keyword.put(opts, :url, db_url)}
end
def logged_transaction(fun_or_multi, opts \\ []) do
@@ -105,4 +121,17 @@ defmodule Explorer.Repo do
def stream_reduce(query, initial, reducer) when is_function(reducer, 2) do
stream_in_transaction(query, &Enum.reduce(&1, initial, reducer))
end
+
+ if Mix.env() == :test do
+ def replica, do: __MODULE__
+ else
+ def replica, do: Explorer.Repo.Replica1
+ end
+
+ defmodule Replica1 do
+ use Ecto.Repo,
+ otp_app: :explorer,
+ adapter: Ecto.Adapters.Postgres,
+ read_only: true
+ end
end
diff --git a/apps/explorer/lib/explorer/repo/config_helper.ex b/apps/explorer/lib/explorer/repo/config_helper.ex
new file mode 100644
index 000000000000..08550335821b
--- /dev/null
+++ b/apps/explorer/lib/explorer/repo/config_helper.ex
@@ -0,0 +1,47 @@
+defmodule Explorer.Repo.ConfigHelper do
+ @moduledoc """
+ Extracts values from environment and adds them to application config.
+
+ Notably, this module processes the DATABASE_URL environment variable and extracts discrete parameters.
+
+ The priority of vars is postgrex enviroment vars < DATABASE_URL components, with values being overwritted by higher priority.
+ """
+
+ # https://hexdocs.pm/postgrex/Postgrex.html#start_link/1-options
+ @postgrex_env_vars [
+ username: "PGUSER",
+ password: "PGPASSWORD",
+ host: "PGHOST",
+ port: "PGPORT",
+ database: "PGDATABASE"
+ ]
+
+ def get_db_config(opts) do
+ url = opts[:url] || System.get_env("DATABASE_URL")
+ env_function = opts[:env_func] || (&System.get_env/1)
+
+ @postgrex_env_vars
+ |> get_env_vars(env_function)
+ |> Keyword.merge(extract_parameters(url))
+ end
+
+ defp extract_parameters(empty) when empty == nil or empty == "", do: []
+
+ # sobelow_skip ["DOS.StringToAtom"]
+ defp extract_parameters(database_url) do
+ ~r/\w*:\/\/(?\w+):(?\w*)?@(?[a-zA-Z\d\.]+):(?\d+)\/(?\w+)/
+ |> Regex.named_captures(database_url)
+ |> Keyword.new(fn {k, v} -> {String.to_atom(k), v} end)
+ |> Keyword.put(:url, database_url)
+ end
+
+ defp get_env_vars(vars, env_function) do
+ Enum.reduce(vars, [], fn {name, var}, opts ->
+ case env_function.(var) do
+ nil -> opts
+ "" -> opts
+ env_value -> Keyword.put(opts, name, env_value)
+ end
+ end)
+ end
+end
diff --git a/apps/explorer/lib/explorer/smart_contract/compiler_version.ex b/apps/explorer/lib/explorer/smart_contract/compiler_version.ex
new file mode 100644
index 000000000000..d54522c63fca
--- /dev/null
+++ b/apps/explorer/lib/explorer/smart_contract/compiler_version.ex
@@ -0,0 +1,197 @@
+defmodule Explorer.SmartContract.CompilerVersion do
+ @moduledoc """
+ Adapter for fetching compiler versions from https://solc-bin.ethereum.org/bin/list.json.
+ """
+
+ @unsupported_solc_versions ~w(0.1.1 0.1.2)
+ @unsupported_vyper_versions ~w(v0.2.9 v0.2.10)
+
+ @doc """
+ Fetches a list of compilers from the Ethereum Solidity API.
+ """
+ @spec fetch_versions(:solc | :vyper) :: {atom, [map]}
+ def fetch_versions(compiler) do
+ case compiler do
+ :solc -> fetch_solc_versions()
+ :vyper -> fetch_vyper_versions()
+ end
+ end
+
+ defp fetch_solc_versions do
+ headers = [{"Content-Type", "application/json"}]
+
+ case HTTPoison.get(source_url(:solc), headers) do
+ {:ok, %{status_code: 200, body: body}} ->
+ {:ok, format_data(body, :solc)}
+
+ {:ok, %{status_code: _status_code, body: body}} ->
+ {:error, decode_json(body)["error"]}
+
+ {:error, %{reason: reason}} ->
+ {:error, reason}
+ end
+ end
+
+ defp fetch_vyper_versions do
+ headers = [{"Content-Type", "application/json"}]
+
+ case HTTPoison.get(source_url(:vyper), headers) do
+ {:ok, %{status_code: 200, body: body}} ->
+ {:ok, format_data(body, :vyper)}
+
+ {:ok, %{status_code: _status_code, body: body}} ->
+ {:error, decode_json(body)["error"]}
+
+ {:error, %{reason: reason}} ->
+ {:error, reason}
+ end
+ end
+
+ @spec vyper_releases_url :: String.t()
+ def vyper_releases_url do
+ "https://api.github.com/repos/vyperlang/vyper/releases"
+ end
+
+ defp format_data(json, compiler) do
+ versions =
+ case compiler do
+ :solc ->
+ json
+ |> Jason.decode!()
+ |> Map.fetch!("builds")
+ |> remove_unsupported_versions(compiler)
+ |> format_versions(compiler)
+ |> Enum.reverse()
+
+ :vyper ->
+ json
+ |> Jason.decode!()
+ |> remove_unsupported_versions(compiler)
+ |> format_versions(compiler)
+ |> Enum.sort(fn version1, version2 ->
+ versions1 = String.split(version1, ".")
+ versions2 = String.split(version2, ".")
+ major1 = versions1 |> Enum.at(0) |> parse_integer()
+ major2 = versions2 |> Enum.at(0) |> parse_integer()
+ minor1 = versions1 |> Enum.at(1) |> parse_integer()
+ minor2 = versions2 |> Enum.at(1) |> parse_integer()
+ patch1 = versions1 |> Enum.at(2) |> String.split("-") |> Enum.at(0) |> parse_integer()
+ patch2 = versions2 |> Enum.at(2) |> String.split("-") |> Enum.at(0) |> parse_integer()
+ major1 >= major2 && minor1 >= minor2 && patch1 >= patch2
+ end)
+ end
+
+ ["latest" | versions]
+ end
+
+ defp parse_integer(string) do
+ case Integer.parse(string) do
+ {number, ""} -> number
+ _ -> nil
+ end
+ end
+
+ @spec remove_unsupported_versions([String.t()], :solc | :vyper) :: [String.t()]
+ defp remove_unsupported_versions(builds, compiler) do
+ case compiler do
+ :solc ->
+ Enum.reject(builds, fn %{"version" => version} ->
+ Enum.member?(@unsupported_solc_versions, version)
+ end)
+
+ :vyper ->
+ Enum.reject(builds, fn %{"tag_name" => version} ->
+ Enum.member?(@unsupported_vyper_versions, version)
+ end)
+ end
+ end
+
+ defp format_versions(builds, compiler) do
+ case compiler do
+ :solc ->
+ Enum.map(builds, fn build ->
+ build
+ |> Map.fetch!("path")
+ |> String.replace_prefix("soljson-", "")
+ |> String.replace_suffix(".js", "")
+ end)
+
+ :vyper ->
+ Enum.map(builds, fn build ->
+ build
+ |> Map.fetch!("tag_name")
+ end)
+ end
+ end
+
+ defp decode_json(json) do
+ Jason.decode!(json)
+ end
+
+ @spec source_url(:solc | :vyper) :: String.t()
+ defp source_url(compiler) do
+ case compiler do
+ :solc ->
+ solc_bin_api_url = Application.get_env(:explorer, :solc_bin_api_url)
+ "#{solc_bin_api_url}/bin/list.json"
+
+ :vyper ->
+ vyper_releases_url()
+ end
+ end
+
+ def get_strict_compiler_version(compiler, compiler_version) do
+ case compiler do
+ :solc ->
+ if compiler_version == "latest" do
+ compiler_versions = get_compiler_versions(:solc)
+
+ if Enum.count(compiler_versions) > 1 do
+ latest_stable_version =
+ compiler_versions
+ |> Enum.drop(1)
+ |> Enum.reduce_while("", fn version, acc ->
+ if String.contains?(version, "-nightly") do
+ {:cont, acc}
+ else
+ {:halt, version}
+ end
+ end)
+
+ latest_stable_version
+ else
+ "latest"
+ end
+ else
+ compiler_version
+ end
+
+ :vyper ->
+ if compiler_version == "latest" do
+ compiler_versions = get_compiler_versions(:vyper)
+
+ if Enum.count(compiler_versions) > 1 do
+ latest_stable_version =
+ compiler_versions
+ |> Enum.at(1)
+
+ latest_stable_version
+ else
+ "latest"
+ end
+ else
+ compiler_version
+ end
+ end
+ end
+
+ defp get_compiler_versions(compiler) do
+ case fetch_versions(compiler) do
+ {:ok, compiler_versions} ->
+ compiler_versions
+
+ {:error, _} ->
+ []
+ end
+ end
+end
diff --git a/apps/explorer/lib/explorer/smart_contract/reader.ex b/apps/explorer/lib/explorer/smart_contract/reader.ex
index dce7165d280e..16e03cd48b9b 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}
@@ -169,6 +178,13 @@ defmodule Explorer.SmartContract.Reader do
EthereumJSONRPC.execute_contract_functions(requests, abi, json_rpc_named_arguments)
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.
@@ -216,7 +232,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
@@ -232,7 +248,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
@@ -277,43 +295,18 @@ defmodule Explorer.SmartContract.Reader do
end
end
- defp get_abi_with_method_id(abi) do
- parsed_abi =
- abi
- |> ABI.parse_specification()
-
- abi_with_method_id =
- abi
- |> Enum.map(fn target_method ->
- methods =
- parsed_abi
- |> Enum.filter(fn method ->
- Atom.to_string(method.type) == Map.get(target_method, "type") &&
- method.function == Map.get(target_method, "name") &&
- Enum.count(method.input_names) == Enum.count(Map.get(target_method, "inputs")) &&
- input_types_matched?(method.types, target_method)
- end)
-
- if Enum.count(methods) > 0 do
- method = Enum.at(methods, 0)
- method_id = Map.get(method, :method_id)
- method_with_id = Map.put(target_method, "method_id", Base.encode16(method_id, case: :lower))
- method_with_id
- else
- target_method
- end
- end)
-
- abi_with_method_id
- end
-
- defp input_types_matched?(types, target_method) do
- types
- |> Enum.with_index()
- |> Enum.all?(fn {target_type, index} ->
- type_to_compare = Map.get(Enum.at(Map.get(target_method, "inputs"), index), "type")
- target_type_formatted = format_type(target_type)
- target_type_formatted == type_to_compare
+ def get_abi_with_method_id(abi) do
+ abi
+ |> Enum.map(fn method ->
+ with parsed_method <- [method] |> ABI.parse_specification() |> Enum.at(0),
+ true <- is_map(parsed_method),
+ method_id <- Map.get(parsed_method, :method_id),
+ true <- !is_nil(method_id) do
+ Map.put(method, "method_id", Base.encode16(method_id, case: :lower))
+ else
+ _ ->
+ method
+ end
end)
end
@@ -350,7 +343,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" => []} ->
@@ -359,7 +352,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)
_ ->
@@ -372,13 +365,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
@@ -396,7 +390,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
@@ -404,28 +398,30 @@ 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 =
@@ -434,7 +430,7 @@ defmodule Explorer.SmartContract.Reader do
%{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 proccess_abi(nil, _method_id), do: nil
@@ -447,9 +443,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
@@ -545,8 +541,7 @@ defmodule Explorer.SmartContract.Reader do
|> Tuple.to_list()
|> Enum.map(fn value ->
if is_list(value) do
- value
- |> Enum.join("")
+ parse_item(value)
else
hex =
value
@@ -557,6 +552,10 @@ defmodule Explorer.SmartContract.Reader do
end)
end
+ defp parse_item(items) when is_list(items) do
+ Enum.map(items, &parse_item/1)
+ end
+
defp parse_item(item) do
response = Integer.parse(item)
diff --git a/apps/explorer/lib/explorer/smart_contract/solc_downloader.ex b/apps/explorer/lib/explorer/smart_contract/solc_downloader.ex
index 896dbe58e88d..5f63b3189dbc 100644
--- a/apps/explorer/lib/explorer/smart_contract/solc_downloader.ex
+++ b/apps/explorer/lib/explorer/smart_contract/solc_downloader.ex
@@ -5,7 +5,7 @@ defmodule Explorer.SmartContract.SolcDownloader do
"""
use GenServer
- alias Explorer.SmartContract.Solidity.CompilerVersion
+ alias Explorer.SmartContract.CompilerVersion
@latest_compiler_refetch_time :timer.minutes(30)
@@ -16,7 +16,7 @@ defmodule Explorer.SmartContract.SolcDownloader do
path
else
compiler_versions =
- case CompilerVersion.fetch_versions() do
+ case CompilerVersion.fetch_versions(:solc) do
{:ok, compiler_versions} ->
compiler_versions
diff --git a/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex b/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex
index 4e72e8a5de26..adb077990ec0 100644
--- a/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex
+++ b/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex
@@ -9,6 +9,10 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
@new_contract_name "New.sol"
+ @required_standard_input_fields ~w(language sources settings)
+
+ @default_output_selection %{"*" => %{"*" => ["*"]}}
+
@doc """
Compiles a code in the solidity command line.
@@ -125,6 +129,101 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
end
end
+ def run(params, json_input) do
+ name = Keyword.fetch!(params, :name)
+ compiler_version = Keyword.fetch!(params, :compiler_version)
+
+ path = SolcDownloader.ensure_exists(compiler_version)
+
+ if path do
+ with {:ok, valid_json} <- tune_json(json_input),
+ {response, _status} <-
+ System.cmd(
+ "node",
+ [
+ Application.app_dir(:explorer, "priv/compile_solc_standard_json_input.js"),
+ create_source_file(valid_json),
+ path
+ ]
+ ),
+ {:ok, decoded} <- Jason.decode(response),
+ {:ok, contracts} <- get_contracts_standard_input_verification(decoded) do
+ fetch_candidates(contracts, name)
+ else
+ {:error, %Jason.DecodeError{}} ->
+ {:error, :compilation}
+
+ {:error, reason} when reason in [:name, :compilation, :json] ->
+ {:error, reason}
+
+ error ->
+ error = parse_error(error)
+ Logger.warn(["There was an error compiling a provided contract: ", inspect(error)])
+ {:error, [first_error | _]} = error
+ %{"message" => error_message} = first_error
+ {:error, :compilation, error_message}
+ end
+ else
+ {:error, :compilation}
+ end
+ end
+
+ defp tune_json(json_input) when is_binary(json_input) do
+ case Jason.decode(json_input) do
+ {:ok, map_input} ->
+ map_set_input_keys = map_input |> Map.keys() |> MapSet.new()
+ map_set_required_keys = MapSet.new(@required_standard_input_fields)
+
+ if MapSet.subset?(map_set_required_keys, map_set_input_keys) do
+ settings = Map.fetch!(map_input, "settings")
+ new_settings = Map.put(settings, "outputSelection", @default_output_selection)
+ map_input |> Map.replace("settings", new_settings) |> Jason.encode()
+ else
+ {:error, :json}
+ end
+
+ _ ->
+ {:error, :json}
+ end
+ end
+
+ defp tune_json(_json_input), do: {:error, :json}
+
+ defp fetch_candidates(contracts, "") when is_map(contracts) do
+ candidates =
+ for {file, content} <- contracts,
+ {contract_name, %{"abi" => abi, "evm" => %{"bytecode" => %{"object" => bytecode}}}} <- content,
+ do: %{"abi" => abi, "bytecode" => bytecode, "name" => contract_name, "file_path" => file}
+
+ {:ok, candidates}
+ end
+
+ defp fetch_candidates(contracts, name) when is_binary(name) and is_map(contracts) do
+ if String.contains?(name, ":") do
+ [file_name, contract_name] = String.split(name, ":")
+ fetch_candidates(contracts, file_name, contract_name)
+ else
+ candidates =
+ for {file, content} <- contracts,
+ {contract_name, %{"abi" => abi, "evm" => %{"bytecode" => %{"object" => bytecode}}}} <- content,
+ contract_name == name,
+ do: %{"abi" => abi, "bytecode" => bytecode, "name" => contract_name, "file_path" => file}
+
+ {:ok, candidates}
+ end
+ end
+
+ defp fetch_candidates(contracts, file_name, name)
+ when is_binary(name) and is_binary(file_name) and is_map(contracts) do
+ case contracts[file_name][name] do
+ %{"abi" => abi, "evm" => %{"bytecode" => %{"object" => bytecode}}} ->
+ {:ok, [%{"abi" => abi, "bytecode" => bytecode, "name" => name, "file_path" => file_name}]}
+
+ _ ->
+ {:ok, []}
+ end
+ end
+
def allowed_evm_versions do
:explorer
|> Application.get_env(:allowed_evm_versions)
@@ -158,6 +257,9 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
defp get_contracts(%{"contracts" => %{"" => contracts}}), do: {:ok, contracts}
defp get_contracts(response), do: {:error, response}
+ defp get_contracts_standard_input_verification(%{"contracts" => contracts}), do: {:ok, contracts}
+ defp get_contracts_standard_input_verification(response), do: {:error, response}
+
defp optimize_value(false), do: "0"
defp optimize_value("false"), do: "0"
diff --git a/apps/explorer/lib/explorer/smart_contract/solidity/compiler_version.ex b/apps/explorer/lib/explorer/smart_contract/solidity/compiler_version.ex
deleted file mode 100644
index 91177e147a65..000000000000
--- a/apps/explorer/lib/explorer/smart_contract/solidity/compiler_version.ex
+++ /dev/null
@@ -1,95 +0,0 @@
-defmodule Explorer.SmartContract.Solidity.CompilerVersion do
- @moduledoc """
- Adapter for fetching compiler versions from https://solc-bin.ethereum.org/bin/list.json.
- """
-
- @unsupported_versions ~w(0.1.1 0.1.2)
-
- @doc """
- Fetches a list of compilers from the Ethereum Solidity API.
- """
- @spec fetch_versions :: {atom, [map]}
- def fetch_versions do
- headers = [{"Content-Type", "application/json"}]
-
- case HTTPoison.get(source_url(), headers) do
- {:ok, %{status_code: 200, body: body}} ->
- {:ok, format_data(body)}
-
- {:ok, %{status_code: _status_code, body: body}} ->
- {:error, decode_json(body)["error"]}
-
- {:error, %{reason: reason}} ->
- {:error, reason}
- end
- end
-
- defp format_data(json) do
- versions =
- json
- |> Jason.decode!()
- |> Map.fetch!("builds")
- |> remove_unsupported_versions()
- |> format_versions()
- |> Enum.reverse()
-
- ["latest" | versions]
- end
-
- defp remove_unsupported_versions(builds) do
- Enum.reject(builds, fn %{"version" => version} ->
- Enum.member?(@unsupported_versions, version)
- end)
- end
-
- defp format_versions(builds) do
- Enum.map(builds, fn build ->
- build
- |> Map.fetch!("path")
- |> String.replace_prefix("soljson-", "")
- |> String.replace_suffix(".js", "")
- end)
- end
-
- defp decode_json(json) do
- Jason.decode!(json)
- end
-
- defp source_url do
- solc_bin_api_url = Application.get_env(:explorer, :solc_bin_api_url)
-
- "#{solc_bin_api_url}/bin/list.json"
- end
-
- def get_strict_compiler_version(compiler_version) do
- if compiler_version == "latest" do
- compiler_versions =
- case fetch_versions() do
- {:ok, compiler_versions} ->
- compiler_versions
-
- {:error, _} ->
- []
- end
-
- if Enum.count(compiler_versions) > 1 do
- latest_stable_version =
- compiler_versions
- |> Enum.drop(1)
- |> Enum.reduce_while("", fn version, acc ->
- if String.contains?(version, "-nightly") do
- {:cont, acc}
- else
- {:halt, version}
- end
- end)
-
- latest_stable_version
- else
- "latest"
- end
- else
- compiler_version
- end
- end
-end
diff --git a/apps/explorer/lib/explorer/smart_contract/publisher.ex b/apps/explorer/lib/explorer/smart_contract/solidity/publisher.ex
similarity index 74%
rename from apps/explorer/lib/explorer/smart_contract/publisher.ex
rename to apps/explorer/lib/explorer/smart_contract/solidity/publisher.ex
index 377fa9ed16c7..e312f88ebff9 100644
--- a/apps/explorer/lib/explorer/smart_contract/publisher.ex
+++ b/apps/explorer/lib/explorer/smart_contract/solidity/publisher.ex
@@ -1,18 +1,18 @@
-defmodule Explorer.SmartContract.Publisher do
+defmodule Explorer.SmartContract.Solidity.Publisher do
@moduledoc """
Module responsible to control the contract verification.
"""
alias Explorer.Chain
alias Explorer.Chain.SmartContract
- alias Explorer.SmartContract.Solidity.CompilerVersion
- alias Explorer.SmartContract.Verifier
+ alias Explorer.SmartContract.CompilerVersion
+ alias Explorer.SmartContract.Solidity.Verifier
@doc """
Evaluates smart contract authenticity and saves its details.
## Examples
- Explorer.SmartContract.Publisher.publish(
+ Explorer.SmartContract.Solidity.Publisher.publish(
"0x0f95fa9bc0383e699325f2658d04e8d96d87b90c",
%{
"compiler_version" => "0.4.24",
@@ -45,6 +45,31 @@ defmodule Explorer.SmartContract.Publisher do
end
end
+ def publish_with_standart_json_input(%{"address_hash" => address_hash} = params, json_input) do
+ case Verifier.evaluate_authenticity_via_standard_json_input(address_hash, params, json_input) do
+ {:ok, %{abi: abi, constructor_arguments: constructor_arguments}, additional_params} ->
+ params_with_constructor_arguments =
+ params
+ |> Map.put("constructor_arguments", constructor_arguments)
+ |> Map.merge(additional_params)
+
+ publish_smart_contract(address_hash, params_with_constructor_arguments, abi)
+
+ {:ok, %{abi: abi}, additional_params} ->
+ merged_params = Map.merge(params, additional_params)
+ publish_smart_contract(address_hash, merged_params, abi)
+
+ {:error, error} ->
+ {:error, unverified_smart_contract(address_hash, params, error, nil, true)}
+
+ {:error, error, error_message} ->
+ {:error, unverified_smart_contract(address_hash, params, error, error_message, true)}
+
+ _ ->
+ {:error, unverified_smart_contract(address_hash, params, "Failed to verify", nil, true)}
+ end
+ end
+
def publish_smart_contract(address_hash, params, abi) do
attrs = address_hash |> attributes(params, abi)
@@ -65,7 +90,7 @@ defmodule Explorer.SmartContract.Publisher do
end
end
- defp unverified_smart_contract(address_hash, params, error, error_message) do
+ defp unverified_smart_contract(address_hash, params, error, error_message, json_verification \\ false) do
attrs = attributes(address_hash, params)
changeset =
@@ -73,7 +98,8 @@ defmodule Explorer.SmartContract.Publisher do
%SmartContract{address_hash: address_hash},
attrs,
error,
- error_message
+ error_message,
+ json_verification
)
%{changeset | action: :insert}
@@ -95,11 +121,12 @@ defmodule Explorer.SmartContract.Publisher do
prepared_external_libraries = prepare_external_libraies(params["external_libraries"])
- compiler_version = CompilerVersion.get_strict_compiler_version(params["compiler_version"])
+ compiler_version = CompilerVersion.get_strict_compiler_version(:solc, params["compiler_version"])
%{
address_hash: address_hash,
name: params["name"],
+ file_path: params["file_path"],
compiler_version: compiler_version,
evm_version: params["evm_version"],
optimization_runs: params["optimization_runs"],
@@ -110,7 +137,8 @@ defmodule Explorer.SmartContract.Publisher do
secondary_sources: params["secondary_sources"],
abi: abi,
verified_via_sourcify: params["verified_via_sourcify"],
- partially_verified: params["partially_verified"]
+ partially_verified: params["partially_verified"],
+ is_vyper_contract: false
}
end
diff --git a/apps/explorer/lib/explorer/smart_contract/solidity/publisher_worker.ex b/apps/explorer/lib/explorer/smart_contract/solidity/publisher_worker.ex
new file mode 100644
index 000000000000..de7962d10719
--- /dev/null
+++ b/apps/explorer/lib/explorer/smart_contract/solidity/publisher_worker.ex
@@ -0,0 +1,49 @@
+defmodule Explorer.SmartContract.Solidity.PublisherWorker do
+ @moduledoc """
+ Background smart contract verification worker.
+ """
+
+ use Que.Worker, concurrency: 5
+
+ alias Explorer.Chain.Events.Publisher, as: EventsPublisher
+ alias Explorer.Chain.SmartContract.VerificationStatus
+ alias Explorer.SmartContract.Solidity.Publisher
+
+ def perform({address_hash, params, external_libraries, conn}) do
+ result =
+ case Publisher.publish(address_hash, params, external_libraries) do
+ {:ok, _contract} = result ->
+ result
+
+ {:error, changeset} ->
+ {:error, changeset}
+ end
+
+ EventsPublisher.broadcast([{:contract_verification_result, {address_hash, result, conn}}], :on_demand)
+ end
+
+ def perform({%{"address_hash" => address_hash} = params, json_input, uid}) when is_binary(uid) do
+ VerificationStatus.insert_status(uid, :pending, address_hash)
+
+ case Publisher.publish_with_standart_json_input(params, json_input) do
+ {:ok, _contract} ->
+ VerificationStatus.update_status(uid, :pass)
+
+ {:error, _changeset} ->
+ VerificationStatus.update_status(uid, :fail)
+ end
+ end
+
+ def perform({%{"address_hash" => address_hash} = params, json_input, conn}) do
+ result =
+ case Publisher.publish_with_standart_json_input(params, json_input) do
+ {:ok, _contract} = result ->
+ result
+
+ {:error, changeset} ->
+ {:error, changeset}
+ end
+
+ EventsPublisher.broadcast([{:contract_verification_result, {address_hash, result, conn}}], :on_demand)
+ end
+end
diff --git a/apps/explorer/lib/explorer/smart_contract/verifier.ex b/apps/explorer/lib/explorer/smart_contract/solidity/verifier.ex
similarity index 75%
rename from apps/explorer/lib/explorer/smart_contract/verifier.ex
rename to apps/explorer/lib/explorer/smart_contract/solidity/verifier.ex
index 6153a4c5f991..6235dae8da54 100644
--- a/apps/explorer/lib/explorer/smart_contract/verifier.ex
+++ b/apps/explorer/lib/explorer/smart_contract/solidity/verifier.ex
@@ -1,5 +1,5 @@
# credo:disable-for-this-file
-defmodule Explorer.SmartContract.Verifier do
+defmodule Explorer.SmartContract.Solidity.Verifier do
@moduledoc """
Module responsible to verify the Smart Contract.
@@ -51,6 +51,79 @@ defmodule Explorer.SmartContract.Verifier do
end)
end
+ def evaluate_authenticity_via_standard_json_input(address_hash, params, json_input) do
+ verify(address_hash, params, json_input)
+ end
+
+ defp verify(address_hash, params, json_input) do
+ name = Map.get(params, "name", "")
+ compiler_version = Map.fetch!(params, "compiler_version")
+ constructor_arguments = Map.get(params, "constructor_arguments", "")
+ autodetect_constructor_arguments = params |> Map.get("autodetect_constructor_args", "false") |> parse_boolean()
+
+ solc_output =
+ CodeCompiler.run(
+ [
+ name: name,
+ compiler_version: compiler_version
+ ],
+ json_input
+ )
+
+ case solc_output do
+ {:ok, candidates} ->
+ case Jason.decode(json_input) do
+ {:ok, map_json_input} ->
+ Enum.reduce_while(candidates, %{}, fn candidate, _acc ->
+ file_path = candidate["file_path"]
+ source_code = map_json_input["sources"][file_path]["content"]
+ contract_name = candidate["name"]
+
+ case compare_bytecodes(
+ candidate,
+ address_hash,
+ constructor_arguments,
+ autodetect_constructor_arguments,
+ source_code,
+ contract_name
+ ) do
+ {:ok, verified_data} ->
+ secondary_sources =
+ for {file, %{"content" => source}} <- map_json_input["sources"],
+ file != file_path,
+ do: %{"file_name" => file, "contract_source_code" => source, "address_hash" => address_hash}
+
+ additional_params =
+ map_json_input
+ |> extract_settings_from_json()
+ |> Map.put("contract_source_code", source_code)
+ |> Map.put("file_path", file_path)
+ |> Map.put("name", contract_name)
+ |> Map.put("secondary_sources", secondary_sources)
+
+ {:halt, {:ok, verified_data, additional_params}}
+
+ err ->
+ {:cont, {:error, err}}
+ end
+ end)
+
+ _ ->
+ {:error, :json}
+ end
+
+ error_response ->
+ error_response
+ end
+ end
+
+ defp extract_settings_from_json(json_input) when is_map(json_input) do
+ %{"enabled" => optimization, "runs" => optimization_runs} = json_input["settings"]["optimizer"]
+
+ %{"optimization" => optimization}
+ |> (&if(parse_boolean(optimization), do: Map.put(&1, "optimization_runs", optimization_runs), else: &1)).()
+ end
+
defp verify(address_hash, params) do
name = Map.fetch!(params, "name")
contract_source_code = Map.fetch!(params, "contract_source_code")
@@ -90,6 +163,24 @@ defmodule Explorer.SmartContract.Verifier do
{:error, :compilation, error_message}
end
+ defp compare_bytecodes(
+ %{"abi" => abi, "bytecode" => bytecode},
+ address_hash,
+ arguments_data,
+ autodetect_constructor_arguments,
+ contract_source_code,
+ contract_name
+ ),
+ do:
+ compare_bytecodes(
+ {:ok, %{"abi" => abi, "bytecode" => bytecode}},
+ address_hash,
+ arguments_data,
+ autodetect_constructor_arguments,
+ contract_source_code,
+ contract_name
+ )
+
# credo:disable-for-next-line /Complexity/
defp compare_bytecodes(
{:ok, %{"abi" => abi, "bytecode" => bytecode}},
@@ -107,12 +198,12 @@ defmodule Explorer.SmartContract.Verifier do
blockchain_created_tx_input =
case Chain.smart_contract_creation_tx_bytecode(address_hash) do
- nil ->
- bytecode
+ %{init: init, created_contract_code: _created_contract_code} ->
+ "0x" <> init_without_0x = init
+ init_without_0x
- blockchain_created_tx_input_with_0x ->
- "0x" <> blockchain_created_tx_input = blockchain_created_tx_input_with_0x
- blockchain_created_tx_input
+ _ ->
+ bytecode
end
%{
@@ -132,12 +223,26 @@ defmodule Explorer.SmartContract.Verifier do
{:error, :generated_bytecode}
has_constructor_with_params?(abi) && autodetect_constructor_arguments ->
- result = ConstructorArguments.find_constructor_arguments(address_hash, abi, contract_source_code, contract_name)
+ result_1 =
+ try_to_verify_with_unknown_constructor_args(
+ blockchain_created_tx_input,
+ bytecode,
+ blockchain_bytecode_without_whisper,
+ abi
+ )
+
+ result_2 =
+ ConstructorArguments.find_constructor_arguments(address_hash, abi, contract_source_code, contract_name)
+
+ cond do
+ result_1 ->
+ {:ok, %{abi: abi, constructor_arguments: result_1}}
- if result do
- {:ok, %{abi: abi, constructor_arguments: result}}
- else
- {:error, :constructor_arguments}
+ result_2 ->
+ {:ok, %{abi: abi, constructor_arguments: result_2}}
+
+ true ->
+ {:error, :constructor_arguments}
end
has_constructor_with_params?(abi) && empty_constructor_arguments ->
@@ -158,6 +263,12 @@ defmodule Explorer.SmartContract.Verifier do
end
end
+ defp try_to_verify_with_unknown_constructor_args(creation_code, generated_bytecode, trimmed_bytecode, abi) do
+ ["", rest_blockchain] = String.split(creation_code, trimmed_bytecode)
+ ["", rest_generated] = String.split(generated_bytecode, trimmed_bytecode)
+ ConstructorArguments.experimental_find_constructor_args(rest_blockchain, rest_generated, abi)
+ end
+
# 730000000000000000000000000000000000000000 - default library address that returned by the compiler
defp try_library_verification(
"730000000000000000000000000000000000000000" <> bytecode,
@@ -177,6 +288,10 @@ defmodule Explorer.SmartContract.Verifier do
For more information on the swarm hash, check out:
https://solidity.readthedocs.io/en/v0.5.3/metadata.html#encoding-of-the-metadata-hash-in-the-bytecode
"""
+ def extract_bytecode_and_metadata_hash(nil) do
+ %{"metadata_hash" => nil, "bytecode" => nil, "compiler_version" => nil}
+ end
+
def extract_bytecode_and_metadata_hash("0x" <> code) do
%{"metadata_hash" => metadata_hash, "bytecode" => bytecode, "compiler_version" => compiler_version} =
extract_bytecode_and_metadata_hash(code)
@@ -356,4 +471,7 @@ defmodule Explorer.SmartContract.Verifier do
defp parse_boolean("true"), do: true
defp parse_boolean("false"), do: false
+
+ defp parse_boolean(true), do: true
+ defp parse_boolean(false), do: false
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..769364c59ca4 100644
--- a/apps/explorer/lib/explorer/smart_contract/verifier/constructor_arguments.ex
+++ b/apps/explorer/lib/explorer/smart_contract/verifier/constructor_arguments.ex
@@ -220,7 +220,7 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do
)
<<>> ->
- check_func.("")
+ false
<<_::binary-size(2)>> <> rest ->
extract_constructor_arguments(rest, check_func, contract_source_code, contract_name)
@@ -236,6 +236,9 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do
constructor_arguments_parts > 1
end
+ defp split_constructor_arguments_and_extract_check_func(constructor_arguments, nil, nil, nil, _metadata_hash_prefix),
+ do: constructor_arguments
+
defp split_constructor_arguments_and_extract_check_func(
constructor_arguments,
check_func,
@@ -267,6 +270,8 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do
end
defp extract_constructor_arguments_check_func(constructor_arguments, check_func, contract_source_code, contract_name) do
+ check_func_result_before_removing_strings = check_func.(constructor_arguments)
+
constructor_arguments =
remove_require_messages_from_constructor_arguments(contract_source_code, constructor_arguments, contract_name)
@@ -275,13 +280,54 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do
check_func_result = check_func.(filtered_constructor_arguments)
- if check_func_result do
- check_func_result
- else
- extract_constructor_arguments(filtered_constructor_arguments, check_func, contract_source_code, contract_name)
+ cond do
+ check_func_result_before_removing_strings ->
+ check_func_result_before_removing_strings
+
+ check_func_result ->
+ check_func_result
+
+ true ->
+ 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
@@ -332,11 +378,17 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do
|> Chain.contract_creation_input_data()
|> String.replace("0x", "")
+ check_func = parse_constructor_and_return_check_func(abi)
+
+ extract_constructor_arguments(creation_code, check_func, contract_source_code, contract_name)
+ end
+
+ defp parse_constructor_and_return_check_func(abi) do
constructor_abi = Enum.find(abi, fn el -> el["type"] == "constructor" && el["inputs"] != [] end)
input_types = Enum.map(constructor_abi["inputs"], &FunctionSelector.parse_specification_type/1)
- check_func = fn assumed_arguments ->
+ fn assumed_arguments ->
try do
_ =
assumed_arguments
@@ -345,11 +397,10 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do
assumed_arguments
rescue
- _ -> false
+ _ ->
+ false
end
end
-
- extract_constructor_arguments(creation_code, check_func, contract_source_code, contract_name)
end
def extract_require_messages_from_constructor(contract_source_code, _contract_name) do
@@ -460,4 +511,15 @@ defmodule Explorer.SmartContract.Verifier.ConstructorArguments do
nil
end
end
+
+ def experimental_find_constructor_args(rest_creation, rest_generated, abi) do
+ got_args =
+ rest_creation
+ |> String.split(extract_constructor_arguments(rest_generated, nil, nil, nil))
+ |> List.last()
+
+ abi
+ |> parse_constructor_and_return_check_func()
+ |> (& &1.(got_args)).()
+ end
end
diff --git a/apps/explorer/lib/explorer/smart_contract/vyper/code_compiler.ex b/apps/explorer/lib/explorer/smart_contract/vyper/code_compiler.ex
new file mode 100644
index 000000000000..ae932cd52dc7
--- /dev/null
+++ b/apps/explorer/lib/explorer/smart_contract/vyper/code_compiler.ex
@@ -0,0 +1,74 @@
+defmodule Explorer.SmartContract.Vyper.CodeCompiler do
+ @moduledoc """
+ Module responsible to compile the Vyper code of a given Smart Contract.
+ """
+
+ alias Explorer.SmartContract.VyperDownloader
+
+ require Logger
+
+ @spec run(Keyword.t()) :: {:ok, map} | {:error, :compilation | :name}
+ def run(params) do
+ compiler_version = Keyword.fetch!(params, :compiler_version)
+ code = Keyword.fetch!(params, :code)
+
+ path = VyperDownloader.ensure_exists(compiler_version)
+
+ source_file_path = create_source_file(code)
+
+ if path do
+ {response, _status} =
+ System.cmd(
+ path,
+ [
+ "-f",
+ "abi,bytecode",
+ source_file_path
+ ]
+ )
+
+ response_data = String.split(response, "\n")
+ abi_row = response_data |> Enum.at(0)
+ bytecode = response_data |> Enum.at(1)
+
+ case Jason.decode(abi_row) do
+ {:ok, abi} ->
+ {:ok, %{"abi" => abi, "bytecode" => bytecode}}
+
+ {:error, %Jason.DecodeError{}} ->
+ {:error, :compilation}
+ end
+ else
+ {:error, :compilation}
+ end
+ end
+
+ def get_contract_info(contracts, _) when contracts == %{}, do: {:error, :compilation}
+
+ def get_contract_info(contracts, name) do
+ new_versions_name = ":" <> name
+
+ case contracts do
+ %{^new_versions_name => response} ->
+ response
+
+ %{^name => response} ->
+ response
+
+ _ ->
+ {:error, :name}
+ end
+ end
+
+ def parse_error({:error, %{"error" => error}}), do: {:error, [error]}
+ def parse_error({:error, %{"errors" => errors}}), do: {:error, errors}
+ def parse_error({:error, _} = error), do: error
+
+ defp create_source_file(source) do
+ {:ok, path} = Briefly.create()
+
+ File.write!(path, source)
+
+ path
+ end
+end
diff --git a/apps/explorer/lib/explorer/smart_contract/vyper/publisher.ex b/apps/explorer/lib/explorer/smart_contract/vyper/publisher.ex
new file mode 100644
index 000000000000..258f0ea888b4
--- /dev/null
+++ b/apps/explorer/lib/explorer/smart_contract/vyper/publisher.ex
@@ -0,0 +1,70 @@
+defmodule Explorer.SmartContract.Vyper.Publisher do
+ @moduledoc """
+ Module responsible to control Vyper contract verification.
+ """
+
+ alias Explorer.Chain
+ alias Explorer.Chain.SmartContract
+ alias Explorer.SmartContract.CompilerVersion
+ alias Explorer.SmartContract.Vyper.Verifier
+
+ def publish(address_hash, params) do
+ case Verifier.evaluate_authenticity(address_hash, params) do
+ {:ok, %{abi: abi}} ->
+ publish_smart_contract(address_hash, params, abi)
+
+ {:error, error} ->
+ {:error, unverified_smart_contract(address_hash, params, error, nil)}
+ end
+ end
+
+ def publish_smart_contract(address_hash, params, abi) do
+ attrs = address_hash |> attributes(params, abi)
+
+ Chain.create_smart_contract(attrs, attrs.external_libraries, attrs.secondary_sources)
+ end
+
+ defp unverified_smart_contract(address_hash, params, error, error_message) do
+ attrs = attributes(address_hash, params)
+
+ changeset =
+ SmartContract.invalid_contract_changeset(
+ %SmartContract{address_hash: address_hash},
+ attrs,
+ error,
+ error_message
+ )
+
+ %{changeset | action: :insert}
+ end
+
+ defp attributes(address_hash, params, abi \\ %{}) do
+ constructor_arguments = params["constructor_arguments"]
+
+ clean_constructor_arguments =
+ if constructor_arguments != nil && constructor_arguments != "" do
+ constructor_arguments
+ else
+ nil
+ end
+
+ compiler_version = CompilerVersion.get_strict_compiler_version(:vyper, params["compiler_version"])
+
+ %{
+ address_hash: address_hash,
+ name: "Vyper_contract",
+ compiler_version: compiler_version,
+ evm_version: nil,
+ optimization_runs: nil,
+ optimization: false,
+ contract_source_code: params["contract_source_code"],
+ constructor_arguments: clean_constructor_arguments,
+ external_libraries: [],
+ secondary_sources: [],
+ abi: abi,
+ verified_via_sourcify: false,
+ partially_verified: false,
+ is_vyper_contract: true
+ }
+ end
+end
diff --git a/apps/explorer/lib/explorer/smart_contract/publisher_worker.ex b/apps/explorer/lib/explorer/smart_contract/vyper/publisher_worker.ex
similarity index 64%
rename from apps/explorer/lib/explorer/smart_contract/publisher_worker.ex
rename to apps/explorer/lib/explorer/smart_contract/vyper/publisher_worker.ex
index f0c248c8fbfa..65053596cf2d 100644
--- a/apps/explorer/lib/explorer/smart_contract/publisher_worker.ex
+++ b/apps/explorer/lib/explorer/smart_contract/vyper/publisher_worker.ex
@@ -1,4 +1,4 @@
-defmodule Explorer.SmartContract.PublisherWorker do
+defmodule Explorer.SmartContract.Vyper.PublisherWorker do
@moduledoc """
Background smart contract verification worker.
"""
@@ -6,11 +6,11 @@ defmodule Explorer.SmartContract.PublisherWorker do
use Que.Worker, concurrency: 5
alias Explorer.Chain.Events.Publisher, as: EventsPublisher
- alias Explorer.SmartContract.Publisher
+ alias Explorer.SmartContract.Vyper.Publisher
- def perform({address_hash, params, external_libraries, conn}) do
+ def perform({address_hash, params, conn}) do
result =
- case Publisher.publish(address_hash, params, external_libraries) do
+ case Publisher.publish(address_hash, params) do
{:ok, _contract} = result ->
result
diff --git a/apps/explorer/lib/explorer/smart_contract/vyper/verifier.ex b/apps/explorer/lib/explorer/smart_contract/vyper/verifier.ex
new file mode 100644
index 000000000000..1595223a0f6f
--- /dev/null
+++ b/apps/explorer/lib/explorer/smart_contract/vyper/verifier.ex
@@ -0,0 +1,65 @@
+# credo:disable-for-this-file
+defmodule Explorer.SmartContract.Vyper.Verifier do
+ @moduledoc """
+ Module responsible to verify the Smart Contract through Vyper.
+
+ Given a contract source code the bytecode will be generated and matched
+ against the existing Creation Address Bytecode, if it matches the contract is
+ then Verified.
+ """
+
+ alias Explorer.Chain
+ alias Explorer.SmartContract.Vyper.CodeCompiler
+
+ def evaluate_authenticity(_, %{"name" => ""}), do: {:error, :name}
+
+ def evaluate_authenticity(_, %{"contract_source_code" => ""}),
+ do: {:error, :contract_source_code}
+
+ def evaluate_authenticity(address_hash, params) do
+ verify(address_hash, params)
+ end
+
+ defp verify(address_hash, params) do
+ contract_source_code = Map.fetch!(params, "contract_source_code")
+ compiler_version = Map.fetch!(params, "compiler_version")
+ constructor_arguments = Map.get(params, "constructor_arguments", "")
+
+ vyper_output =
+ CodeCompiler.run(
+ compiler_version: compiler_version,
+ code: contract_source_code
+ )
+
+ compare_bytecodes(
+ vyper_output,
+ address_hash,
+ constructor_arguments
+ )
+ end
+
+ defp compare_bytecodes({:error, _}, _, _), do: {:error, :compilation}
+
+ # credo:disable-for-next-line /Complexity/
+ defp compare_bytecodes(
+ {:ok, %{"abi" => abi, "bytecode" => bytecode}},
+ address_hash,
+ arguments_data
+ ) do
+ blockchain_bytecode =
+ case Chain.smart_contract_creation_tx_bytecode(address_hash) do
+ %{init: init, created_contract_code: _created_contract_code} ->
+ init
+
+ _ ->
+ nil
+ end
+ |> String.trim()
+
+ if String.trim(bytecode <> arguments_data) == blockchain_bytecode do
+ {:ok, %{abi: abi}}
+ else
+ {:error, :generated_bytecode}
+ end
+ end
+end
diff --git a/apps/explorer/lib/explorer/smart_contract/vyper_downloader.ex b/apps/explorer/lib/explorer/smart_contract/vyper_downloader.ex
new file mode 100644
index 000000000000..dfb873276c7a
--- /dev/null
+++ b/apps/explorer/lib/explorer/smart_contract/vyper_downloader.ex
@@ -0,0 +1,127 @@
+defmodule Explorer.SmartContract.VyperDownloader do
+ @moduledoc """
+ Checks to see if the requested Vyper compiler version exists, and if not it
+ downloads and stores the file.
+ """
+ use GenServer
+
+ alias Explorer.SmartContract.CompilerVersion
+
+ @latest_compiler_refetch_time :timer.minutes(30)
+
+ def ensure_exists(version) do
+ path = file_path(version)
+
+ if File.exists?(path) && version !== "latest" do
+ path
+ else
+ compiler_versions =
+ case CompilerVersion.fetch_versions(:vyper) do
+ {:ok, compiler_versions} ->
+ compiler_versions
+
+ {:error, _} ->
+ []
+ end
+
+ if version in compiler_versions do
+ GenServer.call(__MODULE__, {:ensure_exists, version}, 60_000)
+ else
+ false
+ end
+ end
+ end
+
+ def start_link(_) do
+ GenServer.start_link(__MODULE__, [], name: __MODULE__)
+ end
+
+ # sobelow_skip ["Traversal"]
+ @impl true
+ def init([]) do
+ File.mkdir(compiler_dir())
+
+ {:ok, []}
+ end
+
+ # sobelow_skip ["Traversal"]
+ @impl true
+ def handle_call({:ensure_exists, version}, _from, state) do
+ path = file_path(version)
+
+ if fetch?(version, path) do
+ temp_path = file_path("#{version}-tmp")
+
+ contents = download(version)
+
+ file = File.open!(temp_path, [:write, :exclusive])
+
+ IO.binwrite(file, contents)
+ File.close(file)
+
+ File.rename(temp_path, path)
+ System.cmd("chmod", ["+x", path])
+ end
+
+ {:reply, path, state}
+ end
+
+ defp fetch?("latest", path) do
+ case File.stat(path) do
+ {:error, :enoent} ->
+ true
+
+ {:ok, %{mtime: mtime}} ->
+ last_modified = NaiveDateTime.from_erl!(mtime)
+ diff = Timex.diff(NaiveDateTime.utc_now(), last_modified, :milliseconds)
+
+ diff > @latest_compiler_refetch_time
+ end
+ end
+
+ defp fetch?(_, path) do
+ not File.exists?(path)
+ end
+
+ defp file_path(version) do
+ Path.join(compiler_dir(), "#{version}")
+ end
+
+ defp compiler_dir do
+ Application.app_dir(:explorer, "priv/vyper_compilers/")
+ end
+
+ defp download(version) do
+ version = CompilerVersion.get_strict_compiler_version(:vyper, version)
+ releases_path = CompilerVersion.vyper_releases_url()
+
+ releases_body =
+ releases_path
+ |> HTTPoison.get!([], timeout: 60_000, recv_timeout: 60_000)
+ |> Map.get(:body)
+ |> Jason.decode!()
+
+ release =
+ releases_body
+ |> Enum.find(fn release ->
+ Map.get(release, "tag_name") == version
+ end)
+
+ release_assets = Map.get(release, "assets")
+
+ download_path =
+ Enum.reduce_while(release_assets, "", fn asset, acc ->
+ browser_download_url = Map.get(asset, "browser_download_url")
+
+ if browser_download_url =~ "linux" do
+ {:halt, browser_download_url}
+ else
+ {:cont, acc}
+ end
+ end)
+
+ download_path
+ |> HTTPoison.get!([], timeout: 60_000, recv_timeout: 60_000, follow_redirect: true, hackney: [force_redirect: true])
+ |> Map.get(:body)
+ end
+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/mix.exs b/apps/explorer/mix.exs
index bb6834499d38..3a5e6bc93cc0 100644
--- a/apps/explorer/mix.exs
+++ b/apps/explorer/mix.exs
@@ -79,7 +79,7 @@ defmodule Explorer.Mixfile do
{:ex_machina, "~> 2.3", only: [:test]},
{:exvcr, "~> 0.10", only: :test},
{:httpoison, "~> 1.6"},
- {:jason, "~> 1.2.2"},
+ {:jason, "~> 1.3"},
{:junit_formatter, ">= 0.0.0", only: [:test], runtime: false},
# Log errors and application output to separate files
{:logger_file_backend, "~> 0.0.10"},
@@ -104,7 +104,7 @@ defmodule Explorer.Mixfile do
# `:spandex` tracing of `:ecto`
{:spandex_ecto, "~> 0.6.2"},
# Attach `:prometheus_ecto` to `:ecto`
- {:telemetry, "~> 0.4.1"},
+ {:telemetry, "~> 0.4.3"},
# `Timex.Duration` for `Explorer.Counters.AverageBlockTime.average_block_time/0`
{:timex, "~> 3.7.1"},
{:con_cache, "~> 1.0"},
diff --git a/apps/explorer/package-lock.json b/apps/explorer/package-lock.json
index 12e5c3040079..d2eb2248d781 100644
--- a/apps/explorer/package-lock.json
+++ b/apps/explorer/package-lock.json
@@ -7,11 +7,11 @@
"name": "blockscout",
"license": "GPL-3.0",
"dependencies": {
- "solc": "^0.8.0"
+ "solc": "0.8.0"
},
"engines": {
- "node": "14.x",
- "npm": "7.x"
+ "node": "16.x",
+ "npm": "8.x"
}
},
"node_modules/balanced-match": {
@@ -44,11 +44,22 @@
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
"node_modules/follow-redirects": {
- "version": "1.13.1",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz",
- "integrity": "sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg==",
+ "version": "1.14.8",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
+ "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
"engines": {
"node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
}
},
"node_modules/fs-extra": {
@@ -82,6 +93,9 @@
},
"engines": {
"node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/graceful-fs": {
@@ -112,7 +126,7 @@
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
"integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
- "dependencies": {
+ "optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
@@ -120,7 +134,7 @@
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz",
"integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=",
- "dependencies": {
+ "optionalDependencies": {
"graceful-fs": "^4.1.9"
}
},
@@ -264,9 +278,9 @@
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
"follow-redirects": {
- "version": "1.13.1",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz",
- "integrity": "sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg=="
+ "version": "1.14.8",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
+ "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA=="
},
"fs-extra": {
"version": "0.30.0",
diff --git a/apps/explorer/package.json b/apps/explorer/package.json
index 5e8bf67fec57..eb8441eed3a1 100644
--- a/apps/explorer/package.json
+++ b/apps/explorer/package.json
@@ -8,11 +8,11 @@
"author": "POA Network",
"license": "GPL-3.0",
"engines": {
- "node": "14.x",
- "npm": "7.x"
+ "node": "16.x",
+ "npm": "8.x"
},
"scripts": {},
"dependencies": {
- "solc": "^0.8.0"
+ "solc": "0.8.0"
}
}
diff --git a/apps/explorer/priv/compile_solc_standard_json_input.js b/apps/explorer/priv/compile_solc_standard_json_input.js
new file mode 100644
index 000000000000..6990e148bfaf
--- /dev/null
+++ b/apps/explorer/priv/compile_solc_standard_json_input.js
@@ -0,0 +1,15 @@
+#!/usr/bin/env node
+
+var inputJSONFilePath = process.argv[2];
+var compilerVersionPath = process.argv[3];
+
+var solc = require('solc')
+var compilerSnapshot = require(compilerVersionPath);
+var solc = solc.setupMethods(compilerSnapshot);
+
+var fs = require('fs');
+var input = fs.readFileSync(inputJSONFilePath, 'utf8');
+
+
+const output = JSON.parse(solc.compile(input))
+console.log(JSON.stringify(output));
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/20210616120552_smart_contracts_add_is_vyper_flag.exs b/apps/explorer/priv/repo/migrations/20210616120552_smart_contracts_add_is_vyper_flag.exs
new file mode 100644
index 000000000000..171a90d4e4a0
--- /dev/null
+++ b/apps/explorer/priv/repo/migrations/20210616120552_smart_contracts_add_is_vyper_flag.exs
@@ -0,0 +1,9 @@
+defmodule Explorer.Repo.Migrations.SmartContractsAddIsVyperFlag do
+ use Ecto.Migration
+
+ def change do
+ alter table(:smart_contracts) do
+ add(:is_vyper_contract, :boolean, 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/priv/repo/migrations/20211115164817_add_check_for_bytecode_actuality.exs b/apps/explorer/priv/repo/migrations/20211115164817_add_check_for_bytecode_actuality.exs
new file mode 100644
index 000000000000..dfb86074be8a
--- /dev/null
+++ b/apps/explorer/priv/repo/migrations/20211115164817_add_check_for_bytecode_actuality.exs
@@ -0,0 +1,13 @@
+defmodule Explorer.Repo.Migrations.AddCheckForBytecodeActuality do
+ use Ecto.Migration
+
+ def change do
+ alter table(:smart_contracts) do
+ add(:is_changed_bytecode, :boolean, default: false)
+ # subtracting 1 day to perform first check
+ add(:bytecode_checked_at, :"timestamp without time zone",
+ default: fragment("(NOW() AT TIME ZONE 'utc') - INTERVAL '1 DAY'")
+ )
+ end
+ end
+end
diff --git a/apps/explorer/priv/repo/migrations/20211203115010_add_contract_verification_status_table.exs b/apps/explorer/priv/repo/migrations/20211203115010_add_contract_verification_status_table.exs
new file mode 100644
index 000000000000..79af9a6c54c6
--- /dev/null
+++ b/apps/explorer/priv/repo/migrations/20211203115010_add_contract_verification_status_table.exs
@@ -0,0 +1,13 @@
+defmodule Explorer.Repo.Migrations.AddContractVerificationStatusTable do
+ use Ecto.Migration
+
+ def change do
+ create table("contract_verification_status", primary_key: false) do
+ add(:uid, :string, size: 64, primary_key: true)
+ add(:status, :int2, null: false)
+ add(:address_hash, :bytea, null: false)
+
+ timestamps()
+ end
+ end
+end
diff --git a/apps/explorer/priv/repo/migrations/20211204184037_address_add_gas_used.exs b/apps/explorer/priv/repo/migrations/20211204184037_address_add_gas_used.exs
new file mode 100644
index 000000000000..0494e0a9e6e7
--- /dev/null
+++ b/apps/explorer/priv/repo/migrations/20211204184037_address_add_gas_used.exs
@@ -0,0 +1,9 @@
+defmodule Explorer.Repo.Migrations.AddressAddGasUsed do
+ use Ecto.Migration
+
+ def change do
+ alter table(:addresses) do
+ add(:gas_used, :integer, null: true)
+ end
+ end
+end
diff --git a/apps/explorer/priv/repo/migrations/20211206071033_modify_address_gas_used_bigint.exs b/apps/explorer/priv/repo/migrations/20211206071033_modify_address_gas_used_bigint.exs
new file mode 100644
index 000000000000..18158defba0b
--- /dev/null
+++ b/apps/explorer/priv/repo/migrations/20211206071033_modify_address_gas_used_bigint.exs
@@ -0,0 +1,15 @@
+defmodule Explorer.Repo.Migrations.ModifyAddressGasUsedBigint do
+ use Ecto.Migration
+
+ def up do
+ alter table(:addresses) do
+ modify(:gas_used, :bigint)
+ end
+ end
+
+ def down do
+ alter table(:addresses) do
+ modify(:gas_used, :integer)
+ end
+ end
+end
diff --git a/apps/explorer/priv/repo/migrations/20211217201759_add_has_error_in_iternal_txs_field_to_transaction.exs b/apps/explorer/priv/repo/migrations/20211217201759_add_has_error_in_iternal_txs_field_to_transaction.exs
new file mode 100644
index 000000000000..424f82e82b1d
--- /dev/null
+++ b/apps/explorer/priv/repo/migrations/20211217201759_add_has_error_in_iternal_txs_field_to_transaction.exs
@@ -0,0 +1,9 @@
+defmodule Explorer.Repo.Migrations.AddHasErrorInInternalTxsFieldToTransaction do
+ use Ecto.Migration
+
+ def change do
+ alter table(:transactions) do
+ add(:has_error_in_internal_txs, :boolean, null: true)
+ end
+ end
+end
diff --git a/apps/explorer/priv/repo/migrations/20220111085751_address_add_counters.exs b/apps/explorer/priv/repo/migrations/20220111085751_address_add_counters.exs
new file mode 100644
index 000000000000..15a7104582d5
--- /dev/null
+++ b/apps/explorer/priv/repo/migrations/20220111085751_address_add_counters.exs
@@ -0,0 +1,10 @@
+defmodule Explorer.Repo.Migrations.AddressAddCounters do
+ use Ecto.Migration
+
+ def change do
+ alter table(:addresses) do
+ add(:transactions_count, :integer, null: true)
+ add(:token_transfers_count, :integer, null: true)
+ end
+ end
+end
diff --git a/apps/explorer/priv/repo_api/migrations/.gitkeep b/apps/explorer/priv/repo_api/migrations/.gitkeep
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/apps/explorer/test/explorer/chain/address/current_token_balance_test.exs b/apps/explorer/test/explorer/chain/address/current_token_balance_test.exs
index e7a366377ee4..648ea2a96b54 100644
--- a/apps/explorer/test/explorer/chain/address/current_token_balance_test.exs
+++ b/apps/explorer/test/explorer/chain/address/current_token_balance_test.exs
@@ -157,7 +157,7 @@ defmodule Explorer.Chain.Address.CurrentTokenBalanceTest do
address.hash
|> CurrentTokenBalance.last_token_balances()
|> Repo.all()
- |> Enum.map(fn {token_balance, _} -> token_balance.address_hash end)
+ |> Enum.map(fn {token_balance, _, _} -> token_balance.address_hash end)
assert token_balances == [current_token_balance.address_hash]
end
@@ -195,7 +195,7 @@ defmodule Explorer.Chain.Address.CurrentTokenBalanceTest do
address.hash
|> CurrentTokenBalance.last_token_balances()
|> Repo.all()
- |> Enum.map(fn {token_balance, _} -> token_balance.address_hash end)
+ |> Enum.map(fn {token_balance, _, _} -> token_balance.address_hash end)
assert token_balances == [current_token_balance_a.address_hash]
end
diff --git a/apps/explorer/test/explorer/chain/address_internal_transaction_csv_exporter_test.exs b/apps/explorer/test/explorer/chain/address_internal_transaction_csv_exporter_test.exs
new file mode 100644
index 000000000000..6ae71ddeabe0
--- /dev/null
+++ b/apps/explorer/test/explorer/chain/address_internal_transaction_csv_exporter_test.exs
@@ -0,0 +1,148 @@
+defmodule Explorer.Chain.AddressInternalTransactionCsvExporterTest do
+ use Explorer.DataCase
+
+ alias Explorer.Chain.{AddressInternalTransactionCsvExporter, Wei}
+
+ describe "export/3" do
+ test "exports address internal transactions to csv" do
+ address = insert(:address)
+
+ transaction =
+ :transaction
+ |> insert()
+ |> with_block()
+
+ internal_transaction =
+ insert(:internal_transaction,
+ index: 1,
+ transaction: transaction,
+ from_address: address,
+ block_number: transaction.block_number,
+ block_hash: transaction.block_hash,
+ block_index: 1,
+ transaction_index: transaction.index
+ )
+
+ from_period = Timex.format!(Timex.shift(Timex.now(), minutes: -1), "%Y-%m-%d", :strftime)
+ to_period = Timex.format!(Timex.now(), "%Y-%m-%d", :strftime)
+
+ [result] =
+ address
+ |> AddressInternalTransactionCsvExporter.export(from_period, to_period)
+ |> Enum.to_list()
+ |> Enum.drop(1)
+ |> Enum.map(fn [
+ transaction_hash,
+ _,
+ index,
+ _,
+ block_number,
+ _,
+ block_hash,
+ _,
+ block_index,
+ _,
+ transaction_index,
+ _,
+ timestamp,
+ _,
+ from_address_hash,
+ _,
+ to_address_hash,
+ _,
+ created_contract_address_hash,
+ _,
+ type,
+ _,
+ call_type,
+ _,
+ gas,
+ _,
+ gas_used,
+ _,
+ value,
+ _,
+ input,
+ _,
+ output,
+ _,
+ error,
+ _
+ ] ->
+ %{
+ transaction_hash: transaction_hash,
+ index: index,
+ block_number: block_number,
+ block_index: block_index,
+ block_hash: block_hash,
+ transaction_index: transaction_index,
+ timestamp: timestamp,
+ from_address_hash: from_address_hash,
+ to_address_hash: to_address_hash,
+ created_contract_address_hash: created_contract_address_hash,
+ type: type,
+ call_type: call_type,
+ gas: gas,
+ gas_used: gas_used,
+ value: value,
+ input: input,
+ output: output,
+ error: error
+ }
+ end)
+
+ assert result.transaction_hash == to_string(internal_transaction.transaction_hash)
+ assert result.index == to_string(internal_transaction.index)
+ assert result.block_number == to_string(internal_transaction.block_number)
+ assert result.block_index == to_string(internal_transaction.block_index)
+ assert result.block_hash == to_string(internal_transaction.block_hash)
+ assert result.transaction_index == to_string(internal_transaction.transaction_index)
+ assert result.timestamp
+ assert result.from_address_hash == to_string(internal_transaction.from_address_hash)
+ assert result.to_address_hash == to_string(internal_transaction.to_address_hash)
+ assert result.created_contract_address_hash == to_string(internal_transaction.created_contract_address_hash)
+ assert result.type == to_string(internal_transaction.type)
+ assert result.call_type == to_string(internal_transaction.call_type)
+ assert result.gas == to_string(internal_transaction.gas)
+ assert result.gas_used == to_string(internal_transaction.gas_used)
+ assert result.value == internal_transaction.value |> Wei.to(:wei) |> to_string()
+ assert result.input == to_string(internal_transaction.input)
+ assert result.output == to_string(internal_transaction.output)
+ assert result.error == to_string(internal_transaction.error)
+ end
+
+ test "fetches all internal transactions" do
+ address = insert(:address)
+
+ 1..200
+ |> Enum.map(fn index ->
+ transaction =
+ :transaction
+ |> insert()
+ |> with_block()
+
+ insert(:internal_transaction,
+ index: index,
+ transaction: transaction,
+ from_address: address,
+ block_number: transaction.block_number,
+ block_hash: transaction.block_hash,
+ block_index: index,
+ transaction_index: transaction.index
+ )
+ end)
+ |> Enum.count()
+
+ from_period = Timex.format!(Timex.shift(Timex.now(), minutes: -1), "%Y-%m-%d", :strftime)
+ to_period = Timex.format!(Timex.now(), "%Y-%m-%d", :strftime)
+
+ result =
+ address
+ |> AddressInternalTransactionCsvExporter.export(from_period, to_period)
+ |> Enum.to_list()
+ |> Enum.drop(1)
+
+ assert Enum.count(result) == 200
+ end
+ end
+end
diff --git a/apps/explorer/test/explorer/chain/address_log_csv_explorer_test.exs b/apps/explorer/test/explorer/chain/address_log_csv_explorer_test.exs
new file mode 100644
index 000000000000..32bc5b4bd796
--- /dev/null
+++ b/apps/explorer/test/explorer/chain/address_log_csv_explorer_test.exs
@@ -0,0 +1,117 @@
+defmodule Explorer.Chain.AddressLogCsvExporterTest do
+ use Explorer.DataCase
+
+ alias Explorer.Chain.AddressLogCsvExporter
+
+ describe "export/3" do
+ test "exports address logs to csv" do
+ address = insert(:address)
+
+ transaction =
+ :transaction
+ |> insert()
+ |> with_block()
+
+ log =
+ insert(:log,
+ address: address,
+ index: 1,
+ transaction: transaction,
+ block: transaction.block,
+ block_number: transaction.block_number,
+ data: "0x12",
+ first_topic: "0x13",
+ second_topic: "0x14",
+ third_topic: "0x15",
+ fourth_topic: "0x16"
+ )
+
+ from_period = Timex.format!(Timex.shift(Timex.now(), minutes: -1), "%Y-%m-%d", :strftime)
+ to_period = Timex.format!(Timex.now(), "%Y-%m-%d", :strftime)
+
+ [result] =
+ address
+ |> AddressLogCsvExporter.export(from_period, to_period)
+ |> Enum.to_list()
+ |> Enum.drop(1)
+ |> Enum.map(fn [
+ transaction_hash,
+ _,
+ index,
+ _,
+ block_number,
+ _,
+ block_hash,
+ _,
+ address,
+ _,
+ data,
+ _,
+ first_topic,
+ _,
+ second_topic,
+ _,
+ third_topic,
+ _,
+ fourth_topic,
+ _
+ ] ->
+ %{
+ transaction_hash: transaction_hash,
+ index: index,
+ block_number: block_number,
+ block_hash: block_hash,
+ address: address,
+ data: data,
+ first_topic: first_topic,
+ second_topic: second_topic,
+ third_topic: third_topic,
+ fourth_topic: fourth_topic
+ }
+ end)
+
+ assert result.transaction_hash == to_string(log.transaction_hash)
+ assert result.index == to_string(log.index)
+ assert result.block_number == to_string(log.block_number)
+ assert result.block_hash == to_string(log.block_hash)
+ assert result.address == String.downcase(to_string(log.address))
+ assert result.data == to_string(log.data)
+ assert result.first_topic == to_string(log.first_topic)
+ assert result.second_topic == to_string(log.second_topic)
+ assert result.third_topic == to_string(log.third_topic)
+ assert result.fourth_topic == to_string(log.fourth_topic)
+ end
+
+ test "fetches all logs" do
+ address = insert(:address)
+
+ 1..200
+ |> Enum.map(fn index ->
+ transaction =
+ :transaction
+ |> insert()
+ |> with_block()
+
+ insert(:log,
+ address: address,
+ index: index,
+ transaction: transaction,
+ block: transaction.block,
+ block_number: transaction.block_number
+ )
+ end)
+ |> Enum.count()
+
+ from_period = Timex.format!(Timex.shift(Timex.now(), minutes: -1), "%Y-%m-%d", :strftime)
+ to_period = Timex.format!(Timex.now(), "%Y-%m-%d", :strftime)
+
+ result =
+ address
+ |> AddressLogCsvExporter.export(from_period, to_period)
+ |> Enum.to_list()
+ |> Enum.drop(1)
+
+ assert Enum.count(result) == 200
+ end
+ end
+end
diff --git a/apps/explorer/test/explorer/chain/address_token_transfer_csv_exporter_test.exs b/apps/explorer/test/explorer/chain/address_token_transfer_csv_exporter_test.exs
new file mode 100644
index 000000000000..99655c832ff8
--- /dev/null
+++ b/apps/explorer/test/explorer/chain/address_token_transfer_csv_exporter_test.exs
@@ -0,0 +1,76 @@
+defmodule Explorer.Chain.AddressTokenTransferCsvExporterTest do
+ use Explorer.DataCase
+
+ alias Explorer.Chain.AddressTokenTransferCsvExporter
+
+ describe "export/3" do
+ test "exports token transfers to csv" do
+ address = insert(:address)
+
+ transaction =
+ :transaction
+ |> insert(from_address: address)
+ |> with_block()
+
+ token_transfer =
+ insert(:token_transfer, transaction: transaction, from_address: address, block_number: transaction.block_number)
+
+ from_period = Timex.format!(Timex.shift(Timex.now(), minutes: -1), "%Y-%m-%d", :strftime)
+ to_period = Timex.format!(Timex.now(), "%Y-%m-%d", :strftime)
+
+ [result] =
+ address
+ |> AddressTokenTransferCsvExporter.export(from_period, to_period)
+ |> Enum.to_list()
+ |> Enum.drop(1)
+ |> Enum.map(fn [
+ tx_hash,
+ _,
+ block_number,
+ _,
+ timestamp,
+ _,
+ from_address,
+ _,
+ to_address,
+ _,
+ token_contract_address,
+ _,
+ type,
+ _,
+ token_symbol,
+ _,
+ tokens_transferred,
+ _,
+ transaction_fee,
+ _,
+ status,
+ _,
+ err_code,
+ _
+ ] ->
+ %{
+ tx_hash: tx_hash,
+ block_number: block_number,
+ timestamp: timestamp,
+ from_address: from_address,
+ to_address: to_address,
+ token_contract_address: token_contract_address,
+ type: type,
+ token_symbol: token_symbol,
+ tokens_transferred: tokens_transferred,
+ transaction_fee: transaction_fee,
+ status: status,
+ err_code: err_code
+ }
+ end)
+
+ assert result.block_number == to_string(transaction.block_number)
+ assert result.tx_hash == to_string(transaction.hash)
+ assert result.from_address == token_transfer.from_address_hash |> to_string() |> String.downcase()
+ assert result.to_address == token_transfer.to_address_hash |> to_string() |> String.downcase()
+ assert result.timestamp == to_string(transaction.block.timestamp)
+ assert result.type == "OUT"
+ end
+ end
+end
diff --git a/apps/explorer/test/explorer/chain/address_transaction_csv_exporter_test.exs b/apps/explorer/test/explorer/chain/address_transaction_csv_exporter_test.exs
new file mode 100644
index 000000000000..684be20f9cfc
--- /dev/null
+++ b/apps/explorer/test/explorer/chain/address_transaction_csv_exporter_test.exs
@@ -0,0 +1,111 @@
+defmodule Explorer.Chain.AddressTransactionCsvExporterTest do
+ use Explorer.DataCase
+
+ alias Explorer.Chain.{AddressTransactionCsvExporter, Wei}
+
+ describe "export/3" do
+ test "exports address transactions to csv" do
+ address = insert(:address)
+
+ transaction =
+ :transaction
+ |> insert(from_address: address)
+ |> with_block()
+ |> Repo.preload(:token_transfers)
+
+ from_period = Timex.format!(Timex.shift(Timex.now(), minutes: -1), "%Y-%m-%d", :strftime)
+ to_period = Timex.format!(Timex.now(), "%Y-%m-%d", :strftime)
+
+ [result] =
+ address
+ |> AddressTransactionCsvExporter.export(from_period, to_period)
+ |> Enum.to_list()
+ |> Enum.drop(1)
+ |> Enum.map(fn [
+ hash,
+ _,
+ block_number,
+ _,
+ timestamp,
+ _,
+ from_address,
+ _,
+ to_address,
+ _,
+ created_address,
+ _,
+ type,
+ _,
+ value,
+ _,
+ fee,
+ _,
+ status,
+ _,
+ error,
+ _,
+ cur_price,
+ _,
+ op_price,
+ _,
+ cl_price,
+ _
+ ] ->
+ %{
+ hash: hash,
+ block_number: block_number,
+ timestamp: timestamp,
+ from_address: from_address,
+ to_address: to_address,
+ created_address: created_address,
+ type: type,
+ value: value,
+ fee: fee,
+ status: status,
+ error: error,
+ current_price: cur_price,
+ opening_price: op_price,
+ closing_price: cl_price
+ }
+ end)
+
+ assert result.block_number == to_string(transaction.block_number)
+ assert result.timestamp
+ assert result.created_address == to_string(transaction.created_contract_address_hash)
+ assert result.from_address == to_string(transaction.from_address)
+ assert result.to_address == to_string(transaction.to_address)
+ assert result.hash == to_string(transaction.hash)
+ assert result.type == "OUT"
+ assert result.value == transaction.value |> Wei.to(:wei) |> to_string()
+ assert result.fee
+ assert result.status == to_string(transaction.status)
+ assert result.error == to_string(transaction.error)
+ assert result.current_price
+ assert result.opening_price
+ assert result.closing_price
+ end
+
+ test "fetches all transactions" do
+ address = insert(:address)
+
+ 1..200
+ |> Enum.map(fn _ ->
+ :transaction
+ |> insert(from_address: address)
+ |> with_block()
+ end)
+ |> Enum.count()
+
+ from_period = Timex.format!(Timex.shift(Timex.now(), minutes: -1), "%Y-%m-%d", :strftime)
+ to_period = Timex.format!(Timex.now(), "%Y-%m-%d", :strftime)
+
+ result =
+ address
+ |> AddressTransactionCsvExporter.export(from_period, to_period)
+ |> Enum.to_list()
+ |> Enum.drop(1)
+
+ assert Enum.count(result) == 200
+ end
+ 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/runner/internal_transactions_test.exs b/apps/explorer/test/explorer/chain/import/runner/internal_transactions_test.exs
index 2d9105ffea8f..6f5b53ac7306 100644
--- a/apps/explorer/test/explorer/chain/import/runner/internal_transactions_test.exs
+++ b/apps/explorer/test/explorer/chain/import/runner/internal_transactions_test.exs
@@ -22,6 +22,74 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactionsTest do
assert :error == Repo.get(Transaction, transaction.hash).status
end
+ test "transaction's has_error_in_internal_txs become true when its internal_transaction (where index != 0) has an error" do
+ transaction = insert(:transaction) |> with_block(status: :ok)
+ insert(:pending_block_operation, block_hash: transaction.block_hash, fetch_internal_transactions: true)
+
+ assert :ok == transaction.status
+ assert nil == transaction.has_error_in_internal_txs
+
+ index = 0
+ error = nil
+
+ internal_transaction_changes = make_internal_transaction_changes(transaction, index, error)
+
+ index = 1
+ error = "Reverted"
+
+ internal_transaction_changes_1 = make_internal_transaction_changes(transaction, index, error)
+
+ assert {:ok, _} = run_internal_transactions([internal_transaction_changes, internal_transaction_changes_1])
+ tx = Repo.get(Transaction, transaction.hash)
+
+ assert :ok == tx.status
+ assert true == tx.has_error_in_internal_txs
+ end
+
+ test "transaction's has_error_in_internal_txs become false when its internal_transaction (where index == 0) has an error" do
+ transaction = insert(:transaction) |> with_block(status: :ok)
+ insert(:pending_block_operation, block_hash: transaction.block_hash, fetch_internal_transactions: true)
+
+ assert :ok == transaction.status
+ assert nil == transaction.has_error_in_internal_txs
+
+ index = 0
+ error = "Reverted"
+
+ internal_transaction_changes = make_internal_transaction_changes(transaction, index, error)
+
+ assert {:ok, _} = run_internal_transactions([internal_transaction_changes])
+ tx = Repo.get(Transaction, transaction.hash)
+
+ assert :error == tx.status
+ assert false == tx.has_error_in_internal_txs
+ end
+
+ test "transaction's has_error_in_internal_txs become false when its internal_transaction has no error" do
+ transaction = insert(:transaction) |> with_block(status: :ok)
+ insert(:pending_block_operation, block_hash: transaction.block_hash, fetch_internal_transactions: true)
+
+ assert :ok == transaction.status
+ assert nil == transaction.has_error_in_internal_txs
+
+ index = 0
+ error = nil
+
+ internal_transaction_changes = make_internal_transaction_changes(transaction, index, error)
+
+ index = 1
+ error = nil
+
+ internal_transaction_changes_1 = make_internal_transaction_changes(transaction, index, error)
+
+ assert {:ok, _} = run_internal_transactions([internal_transaction_changes, internal_transaction_changes_1])
+
+ tx = Repo.get(Transaction, transaction.hash)
+
+ assert :ok == tx.status
+ assert false == tx.has_error_in_internal_txs
+ end
+
test "simple coin transfer's status becomes :error when its internal_transaction has an error" do
transaction = insert(:transaction) |> with_block(status: :ok)
insert(:pending_block_operation, block_hash: transaction.block_hash, fetch_internal_transactions: true)
diff --git a/apps/explorer/test/explorer/chain/import_test.exs b/apps/explorer/test/explorer/chain/import_test.exs
index f96a53652652..9cde0dbb8103 100644
--- a/apps/explorer/test/explorer/chain/import_test.exs
+++ b/apps/explorer/test/explorer/chain/import_test.exs
@@ -375,17 +375,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
@@ -425,13 +428,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
@@ -1564,8 +1569,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
},
@@ -2250,7 +2255,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"
}
]
},
@@ -2260,7 +2266,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/log_test.exs b/apps/explorer/test/explorer/chain/log_test.exs
index f670c83873fd..880bd0274ace 100644
--- a/apps/explorer/test/explorer/chain/log_test.exs
+++ b/apps/explorer/test/explorer/chain/log_test.exs
@@ -1,6 +1,8 @@
defmodule Explorer.Chain.LogTest do
use Explorer.DataCase
+ import Mox
+
alias Ecto.Changeset
alias Explorer.Chain.{Log, SmartContract}
alias Explorer.Repo
@@ -99,6 +101,40 @@ defmodule Explorer.Chain.LogTest do
data: data
)
+ blockchain_get_code_mock()
+
+ EthereumJSONRPC.Mox
+ |> expect(
+ :json_rpc,
+ fn %{
+ id: _id,
+ method: "eth_getStorageAt",
+ params: [
+ _,
+ "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc",
+ "latest"
+ ]
+ },
+ _options ->
+ {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"}
+ end
+ )
+ |> expect(
+ :json_rpc,
+ fn %{
+ id: _id,
+ method: "eth_getStorageAt",
+ params: [
+ _,
+ "0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50",
+ "latest"
+ ]
+ },
+ _options ->
+ {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"}
+ end
+ )
+
assert Log.decode(log, transaction) ==
{:ok, "eb9b3c4c", "WantsPets(string indexed _from_human, uint256 _number, bool indexed _belly)",
[
@@ -167,4 +203,14 @@ defmodule Explorer.Chain.LogTest do
]}
end
end
+
+ defp blockchain_get_code_mock do
+ expect(
+ EthereumJSONRPC.Mox,
+ :json_rpc,
+ fn [%{id: id, method: "eth_getCode", params: [_, _]}], _options ->
+ {:ok, [%{id: id, jsonrpc: "2.0", result: "0x0"}]}
+ end
+ )
+ end
end
diff --git a/apps/explorer/test/explorer/chain/transaction_test.exs b/apps/explorer/test/explorer/chain/transaction_test.exs
index 5fe6b9958c61..8332b70659df 100644
--- a/apps/explorer/test/explorer/chain/transaction_test.exs
+++ b/apps/explorer/test/explorer/chain/transaction_test.exs
@@ -1,6 +1,8 @@
defmodule Explorer.Chain.TransactionTest do
use Explorer.DataCase
+ import Mox
+
alias Ecto.Changeset
alias Explorer.Chain.Transaction
@@ -263,6 +265,38 @@ defmodule Explorer.Chain.TransactionTest do
|> insert()
|> Repo.preload(to_address: :smart_contract)
+ EthereumJSONRPC.Mox
+ |> expect(
+ :json_rpc,
+ fn %{
+ id: _id,
+ method: "eth_getStorageAt",
+ params: [
+ _,
+ "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc",
+ "latest"
+ ]
+ },
+ _options ->
+ {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"}
+ end
+ )
+ |> expect(
+ :json_rpc,
+ fn %{
+ id: _id,
+ method: "eth_getStorageAt",
+ params: [
+ _,
+ "0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50",
+ "latest"
+ ]
+ },
+ _options ->
+ {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"}
+ end
+ )
+
assert Transaction.decoded_input_data(transaction) == {:ok, "60fe47b1", "set(uint256 x)", [{"x", "uint256", 50}]}
end
@@ -283,6 +317,38 @@ defmodule Explorer.Chain.TransactionTest do
|> insert(to_address: contract.address, input: "0x" <> input_data)
|> Repo.preload(to_address: :smart_contract)
+ EthereumJSONRPC.Mox
+ |> expect(
+ :json_rpc,
+ fn %{
+ id: _id,
+ method: "eth_getStorageAt",
+ params: [
+ _,
+ "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc",
+ "latest"
+ ]
+ },
+ _options ->
+ {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"}
+ end
+ )
+ |> expect(
+ :json_rpc,
+ fn %{
+ id: _id,
+ method: "eth_getStorageAt",
+ params: [
+ _,
+ "0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50",
+ "latest"
+ ]
+ },
+ _options ->
+ {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"}
+ end
+ )
+
assert Transaction.decoded_input_data(transaction) == {:ok, "60fe47b1", "set(uint256 x)", [{"x", "uint256", 10}]}
end
end
diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs
index 6f0ba2b898eb..08ea81b6031a 100644
--- a/apps/explorer/test/explorer/chain_test.exs
+++ b/apps/explorer/test/explorer/chain_test.exs
@@ -27,7 +27,7 @@ defmodule Explorer.ChainTest do
Wei
}
- alias Explorer.Chain
+ alias Explorer.{Chain, Etherscan}
alias Explorer.Chain.InternalTransaction.Type
alias Explorer.Chain.Supply.ProofOfAuthority
@@ -1413,11 +1413,11 @@ defmodule Explorer.ChainTest do
insert(:address, fetched_coin_balance: index)
end
- assert "10" = Decimal.to_string(Chain.fetch_sum_coin_total_supply())
+ assert "10" = Decimal.to_string(Etherscan.fetch_sum_coin_total_supply())
end
test "fetches coin total supply when there are no blocks" do
- assert 0 = Chain.fetch_sum_coin_total_supply()
+ assert 0 = Etherscan.fetch_sum_coin_total_supply()
end
end
@@ -2710,7 +2710,7 @@ defmodule Explorer.ChainTest do
transaction_index: transaction.index
)
- %InternalTransaction{transaction_hash: second_transaction_hash, index: second_index} =
+ %InternalTransaction{transaction_hash: transaction_hash_1, index: index_1} =
insert(:internal_transaction,
transaction: transaction,
index: 1,
@@ -2720,13 +2720,23 @@ defmodule Explorer.ChainTest do
transaction_index: transaction.index
)
+ %InternalTransaction{transaction_hash: transaction_hash_2, index: index_2} =
+ insert(:internal_transaction,
+ transaction: transaction,
+ index: 2,
+ block_number: transaction.block_number,
+ block_hash: transaction.block_hash,
+ block_index: 2,
+ transaction_index: transaction.index
+ )
+
result =
transaction.hash
|> Chain.transaction_to_internal_transactions()
|> Enum.map(&{&1.transaction_hash, &1.index})
# excluding of internal transactions with type=call and index=0
- assert [{second_transaction_hash, second_index}] == result
+ assert [{transaction_hash_1, index_1}, {transaction_hash_2, index_2}] == result
end
test "pages by index" do
@@ -3049,11 +3059,11 @@ defmodule Explorer.ChainTest do
end
end
- describe "transaction_to_logs/2" do
+ describe "transaction_to_logs/3" do
test "without logs" do
transaction = insert(:transaction)
- assert [] = Chain.transaction_to_logs(transaction.hash)
+ assert [] = Chain.transaction_to_logs(transaction.hash, false)
end
test "with logs" do
@@ -3065,7 +3075,8 @@ defmodule Explorer.ChainTest do
%Log{transaction_hash: transaction_hash, index: index} =
insert(:log, transaction: transaction, block: transaction.block, block_number: transaction.block_number)
- assert [%Log{transaction_hash: ^transaction_hash, index: ^index}] = Chain.transaction_to_logs(transaction.hash)
+ assert [%Log{transaction_hash: ^transaction_hash, index: ^index}] =
+ Chain.transaction_to_logs(transaction.hash, false)
end
test "with logs can be paginated" do
@@ -3096,7 +3107,7 @@ defmodule Explorer.ChainTest do
assert second_page_indexes ==
transaction.hash
- |> Chain.transaction_to_logs(paging_options: %PagingOptions{key: {log.index}, page_size: 50})
+ |> Chain.transaction_to_logs(false, paging_options: %PagingOptions{key: {log.index}, page_size: 50})
|> Enum.map(& &1.index)
end
@@ -3111,6 +3122,7 @@ defmodule Explorer.ChainTest do
assert [%Log{address: %Address{}, transaction: %Transaction{}}] =
Chain.transaction_to_logs(
transaction.hash,
+ false,
necessity_by_association: %{
address: :optional,
transaction: :optional
@@ -3122,7 +3134,7 @@ defmodule Explorer.ChainTest do
address: %Ecto.Association.NotLoaded{},
transaction: %Ecto.Association.NotLoaded{}
}
- ] = Chain.transaction_to_logs(transaction.hash)
+ ] = Chain.transaction_to_logs(transaction.hash, false)
end
end
@@ -4666,13 +4678,13 @@ defmodule Explorer.ChainTest do
token_balances =
address.hash
|> Chain.fetch_last_token_balances()
- |> Enum.map(fn {token_balance, _} -> token_balance.address_hash end)
+ |> Enum.map(fn {token_balance, _, _} -> token_balance.address_hash end)
assert token_balances == [current_token_balance.address_hash]
end
end
- describe "fetch_token_holders_from_token_hash/2" do
+ describe "fetch_token_holders_from_token_hash/3" do
test "returns the token holders" do
%Token{contract_address_hash: contract_address_hash} = insert(:token)
address_a = insert(:address)
@@ -4695,7 +4707,7 @@ defmodule Explorer.ChainTest do
token_holders_count =
contract_address_hash
- |> Chain.fetch_token_holders_from_token_hash([])
+ |> Chain.fetch_token_holders_from_token_hash(false, [])
|> Enum.count()
assert token_holders_count == 2
@@ -5332,40 +5344,6 @@ defmodule Explorer.ChainTest do
end
end
- describe "extract_db_name/1" do
- test "extracts correct db name" do
- db_url = "postgresql://viktor:@localhost:5432/blockscout-dev-1"
- assert Chain.extract_db_name(db_url) == "blockscout-dev-1"
- end
-
- test "returns empty db name" do
- db_url = ""
- assert Chain.extract_db_name(db_url) == ""
- end
-
- test "returns nil db name" do
- db_url = nil
- assert Chain.extract_db_name(db_url) == ""
- end
- end
-
- describe "extract_db_host/1" do
- test "extracts correct db host" do
- db_url = "postgresql://viktor:@localhost:5432/blockscout-dev-1"
- assert Chain.extract_db_host(db_url) == "localhost"
- end
-
- test "returns empty db name" do
- db_url = ""
- assert Chain.extract_db_host(db_url) == ""
- end
-
- test "returns nil db name" do
- db_url = nil
- assert Chain.extract_db_host(db_url) == ""
- end
- end
-
describe "fetch_first_trace/2" do
test "fetched first trace", %{
json_rpc_named_arguments: json_rpc_named_arguments
@@ -5630,6 +5608,39 @@ defmodule Explorer.ChainTest do
test "combine_proxy_implementation_abi/2 returns [] abi for unverified proxy" do
proxy_contract_address = insert(:contract_address)
+
+ EthereumJSONRPC.Mox
+ |> expect(
+ :json_rpc,
+ fn %{
+ id: _id,
+ method: "eth_getStorageAt",
+ params: [
+ _,
+ "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc",
+ "latest"
+ ]
+ },
+ _options ->
+ {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"}
+ end
+ )
+ |> expect(
+ :json_rpc,
+ fn %{
+ id: _id,
+ method: "eth_getStorageAt",
+ params: [
+ _,
+ "0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50",
+ "latest"
+ ]
+ },
+ _options ->
+ {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"}
+ end
+ )
+
assert Chain.combine_proxy_implementation_abi(proxy_contract_address, []) == []
end
@@ -5679,6 +5690,39 @@ defmodule Explorer.ChainTest do
test "get_implementation_abi_from_proxy/2 returns [] abi for unverified proxy" do
proxy_contract_address = insert(:contract_address)
+
+ EthereumJSONRPC.Mox
+ |> expect(
+ :json_rpc,
+ fn %{
+ id: _id,
+ method: "eth_getStorageAt",
+ params: [
+ _,
+ "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc",
+ "latest"
+ ]
+ },
+ _options ->
+ {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"}
+ end
+ )
+ |> expect(
+ :json_rpc,
+ fn %{
+ id: _id,
+ method: "eth_getStorageAt",
+ params: [
+ _,
+ "0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50",
+ "latest"
+ ]
+ },
+ _options ->
+ {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"}
+ end
+ )
+
assert Chain.combine_proxy_implementation_abi(proxy_contract_address, []) == []
end
@@ -5718,6 +5762,38 @@ defmodule Explorer.ChainTest do
assert implementation_abi == @implementation_abi
end
+ test "get_implementation_abi_from_proxy/2 returns implementation abi in case of EIP-1967 proxy pattern" do
+ proxy_contract_address = insert(:contract_address)
+ insert(:smart_contract, address_hash: proxy_contract_address.hash, abi: [])
+
+ implementation_contract_address = insert(:contract_address)
+ insert(:smart_contract, address_hash: implementation_contract_address.hash, abi: @implementation_abi)
+
+ implementation_contract_address_hash_string =
+ Base.encode16(implementation_contract_address.hash.bytes, case: :lower)
+
+ expect(
+ EthereumJSONRPC.Mox,
+ :json_rpc,
+ fn %{
+ id: _id,
+ method: "eth_getStorageAt",
+ params: [
+ _,
+ "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc",
+ "latest"
+ ]
+ },
+ _options ->
+ {:ok, "0x000000000000000000000000" <> implementation_contract_address_hash_string}
+ end
+ )
+
+ implementation_abi = Chain.get_implementation_abi_from_proxy(proxy_contract_address.hash, [])
+
+ assert implementation_abi == @implementation_abi
+ end
+
test "get_implementation_abi/1 returns empty [] abi if implmentation address is null" do
assert Chain.get_implementation_abi(nil) == []
end
@@ -5745,5 +5821,17 @@ 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
end
diff --git a/apps/explorer/test/explorer/graphql_test.exs b/apps/explorer/test/explorer/graphql_test.exs
index 51b15b7190c9..2d8ffd1a8fe8 100644
--- a/apps/explorer/test/explorer/graphql_test.exs
+++ b/apps/explorer/test/explorer/graphql_test.exs
@@ -13,7 +13,7 @@ defmodule Explorer.GraphQLTest do
|> insert()
|> Map.get(:hash)
|> GraphQL.address_to_transactions_query()
- |> Repo.all()
+ |> Repo.replica().all()
assert result == []
end
@@ -26,7 +26,7 @@ defmodule Explorer.GraphQLTest do
[found_transaction] =
address_hash
|> GraphQL.address_to_transactions_query()
- |> Repo.all()
+ |> Repo.replica().all()
assert found_transaction.hash == transaction.hash
end
@@ -39,7 +39,7 @@ defmodule Explorer.GraphQLTest do
[found_transaction] =
address_hash
|> GraphQL.address_to_transactions_query()
- |> Repo.all()
+ |> Repo.replica().all()
assert found_transaction.hash == transaction.hash
end
@@ -52,7 +52,7 @@ defmodule Explorer.GraphQLTest do
[found_transaction] =
address_hash
|> GraphQL.address_to_transactions_query()
- |> Repo.all()
+ |> Repo.replica().all()
assert found_transaction.hash == transaction.hash
end
@@ -79,7 +79,7 @@ defmodule Explorer.GraphQLTest do
found_transactions =
address_hash
|> GraphQL.address_to_transactions_query()
- |> Repo.all()
+ |> Repo.replica().all()
block_number_and_index_order =
Enum.map(found_transactions, fn transaction ->
@@ -144,7 +144,7 @@ defmodule Explorer.GraphQLTest do
[found_internal_transaction] =
transaction1
|> GraphQL.transaction_to_internal_transactions_query()
- |> Repo.all()
+ |> Repo.replica().all()
assert found_internal_transaction.transaction_hash == transaction1.hash
assert found_internal_transaction.index == internal_transaction.index
@@ -173,7 +173,7 @@ defmodule Explorer.GraphQLTest do
found_internal_transactions =
transaction1
|> GraphQL.transaction_to_internal_transactions_query()
- |> Repo.all()
+ |> Repo.replica().all()
assert length(found_internal_transactions) == 3
@@ -209,7 +209,7 @@ defmodule Explorer.GraphQLTest do
found_internal_transactions =
transaction
|> GraphQL.transaction_to_internal_transactions_query()
- |> Repo.all()
+ |> Repo.replica().all()
index_order = Enum.map(found_internal_transactions, & &1.index)
@@ -259,7 +259,7 @@ defmodule Explorer.GraphQLTest do
|> insert()
|> Map.get(:hash)
|> GraphQL.list_token_transfers_query()
- |> Repo.all()
+ |> Repo.replica().all()
assert result == []
end
@@ -271,7 +271,7 @@ defmodule Explorer.GraphQLTest do
[found_token_transfer] =
token_transfer.token_contract_address_hash
|> GraphQL.list_token_transfers_query()
- |> Repo.all()
+ |> Repo.replica().all()
expected_fields = ~w(
amount
@@ -336,8 +336,8 @@ defmodule Explorer.GraphQLTest do
found_token_transfers =
token_address.hash
|> GraphQL.list_token_transfers_query()
- |> Repo.all()
- |> Repo.preload(:transaction)
+ |> Repo.replica().all()
+ |> Repo.replica().preload(:transaction)
block_number_order = Enum.map(found_token_transfers, & &1.block_number)
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/repo/config_helper_test.exs b/apps/explorer/test/explorer/repo/config_helper_test.exs
new file mode 100644
index 000000000000..fdcec801e955
--- /dev/null
+++ b/apps/explorer/test/explorer/repo/config_helper_test.exs
@@ -0,0 +1,71 @@
+defmodule Explorer.Repo.ConfigHelperTest do
+ use Explorer.DataCase
+
+ alias Explorer.Repo.ConfigHelper
+
+ describe "get_db_config/1" do
+ test "parse params from database url" do
+ database_url = "postgresql://test_username:test_password@127.8.8.1:7777/test_database"
+
+ result = ConfigHelper.get_db_config(%{url: database_url, env_func: fn _ -> nil end})
+
+ assert result[:username] == "test_username"
+ assert result[:password] == "test_password"
+ assert result[:hostname] == "127.8.8.1"
+ assert result[:port] == "7777"
+ assert result[:database] == "test_database"
+ end
+
+ test "get username without password" do
+ database_url = "postgresql://test_username:@127.8.8.1:7777/test_database"
+
+ result = ConfigHelper.get_db_config(%{url: database_url, env_func: fn _ -> nil end})
+
+ assert result[:username] == "test_username"
+ assert result[:password] == ""
+ assert result[:hostname] == "127.8.8.1"
+ assert result[:port] == "7777"
+ assert result[:database] == "test_database"
+ end
+
+ test "get hostname instead of ip" do
+ database_url = "postgresql://test_username:@cooltesthost:7777/test_database"
+
+ result = ConfigHelper.get_db_config(%{url: database_url, env_func: fn _ -> nil end})
+
+ assert result[:username] == "test_username"
+ assert result[:password] == ""
+ assert result[:hostname] == "cooltesthost"
+ assert result[:port] == "7777"
+ assert result[:database] == "test_database"
+ end
+
+ test "overwrite postgrex vars param with database url" do
+ database_url = "postgresql://test_username:@127.8.8.1:7777/test_database"
+
+ vars = %{"PGUSER" => "postgrex_user", "PGPASSWORD" => "postgrex_password"}
+ func = fn v -> vars[v] end
+ result = ConfigHelper.get_db_config(%{url: database_url, env_func: func})
+
+ assert result[:username] == "test_username"
+ assert result[:password] == ""
+ assert result[:hostname] == "127.8.8.1"
+ assert result[:port] == "7777"
+ assert result[:database] == "test_database"
+ end
+
+ test "overwrite database password param with empty DATABASE_URL password" do
+ database_url = "postgresql://test_username:@127.8.8.1:7777/test_database"
+
+ vars = %{"PGUSER" => "postgrex_user", "PGPASSWORD" => "postgrex_password"}
+ func = fn v -> vars[v] end
+ result = ConfigHelper.get_db_config(%{url: database_url, env_func: func})
+
+ assert result[:username] == "test_username"
+ assert result[:password] == ""
+ assert result[:hostname] == "127.8.8.1"
+ assert result[:port] == "7777"
+ assert result[:database] == "test_database"
+ end
+ end
+end
diff --git a/apps/explorer/test/explorer/smart_contract/solidity/compiler_version_test.exs b/apps/explorer/test/explorer/smart_contract/compiler_version_test.exs
similarity index 80%
rename from apps/explorer/test/explorer/smart_contract/solidity/compiler_version_test.exs
rename to apps/explorer/test/explorer/smart_contract/compiler_version_test.exs
index 6e36da725528..02e396d866c5 100644
--- a/apps/explorer/test/explorer/smart_contract/solidity/compiler_version_test.exs
+++ b/apps/explorer/test/explorer/smart_contract/compiler_version_test.exs
@@ -1,12 +1,12 @@
-defmodule Explorer.SmartContract.Solidity.CompilerVersionTest do
+defmodule Explorer.SmartContract.CompilerVersionTest do
use ExUnit.Case
- doctest Explorer.SmartContract.Solidity.CompilerVersion
+ doctest Explorer.SmartContract.CompilerVersion
- alias Explorer.SmartContract.Solidity.CompilerVersion
+ alias Explorer.SmartContract.CompilerVersion
alias Plug.Conn
- describe "fetch_versions" do
+ describe "fetch_versions/1" do
setup do
bypass = Bypass.open()
@@ -23,7 +23,7 @@ defmodule Explorer.SmartContract.Solidity.CompilerVersionTest do
Conn.resp(conn, 200, solc_bin_versions())
end)
- assert {:ok, versions} = CompilerVersion.fetch_versions()
+ assert {:ok, versions} = CompilerVersion.fetch_versions(:solc)
assert Enum.any?(versions, fn item -> item == "v0.4.9+commit.364da425" end) == true
end
@@ -35,7 +35,7 @@ defmodule Explorer.SmartContract.Solidity.CompilerVersionTest do
Conn.resp(conn, 200, solc_bin_versions())
end)
- assert {:ok, versions} = CompilerVersion.fetch_versions()
+ assert {:ok, versions} = CompilerVersion.fetch_versions(:solc)
assert List.first(versions) == "latest"
end
@@ -44,13 +44,13 @@ defmodule Explorer.SmartContract.Solidity.CompilerVersionTest do
Conn.resp(conn, 400, ~S({"error": "bad request"}))
end)
- assert {:error, "bad request"} = CompilerVersion.fetch_versions()
+ assert {:error, "bad request"} = CompilerVersion.fetch_versions(:solc)
end
test "returns error when there is server error", %{bypass: bypass} do
Bypass.down(bypass)
- assert {:error, :econnrefused} = CompilerVersion.fetch_versions()
+ assert {:error, :econnrefused} = CompilerVersion.fetch_versions(:solc)
end
end
diff --git a/apps/explorer/test/explorer/smart_contract/reader_test.exs b/apps/explorer/test/explorer/smart_contract/reader_test.exs
index 3b9fb2d05362..87ba283eb430 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
@@ -365,6 +366,41 @@ defmodule Explorer.SmartContract.ReaderTest do
end
end
+ describe "get_abi_with_method_id" do
+ test "add method_id to the ABI method" do
+ method = %{
+ "constant" => true,
+ "inputs" => [%{"name" => "_message", "type" => "bytes32"}],
+ "name" => "numMessagesSigned",
+ "outputs" => [%{"name" => "", "type" => "uint256"}],
+ "payable" => false,
+ "stateMutability" => "view",
+ "type" => "function"
+ }
+
+ abi = [method]
+ method_with_id = Map.put(method, "method_id", "0cbf0601")
+ assert [method_with_id] = Reader.get_abi_with_method_id(abi)
+ end
+
+ test "do not crash in some corner cases" do
+ abi = [
+ %{"payable" => true, "stateMutability" => "payable", "type" => "fallback"},
+ %{
+ "anonymous" => false,
+ "inputs" => [
+ %{"indexed" => false, "name" => "recipient", "type" => "address"},
+ %{"indexed" => false, "name" => "value", "type" => "uint256"}
+ ],
+ "name" => "UserRequestForSignature",
+ "type" => "event"
+ }
+ ]
+
+ assert abi = Reader.get_abi_with_method_id(abi)
+ end
+ end
+
defp blockchain_get_function_mock do
expect(
EthereumJSONRPC.Mox,
diff --git a/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs b/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs
index 780ad94c8d17..2bf98044c47b 100644
--- a/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs
+++ b/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs
@@ -14,7 +14,9 @@ defmodule Explorer.SmartContract.Solidity.CodeCompilerTest do
describe "run/2" do
setup do
- {:ok, contract_code_info: Factory.contract_code_info()}
+ {:ok,
+ contract_code_info: Factory.contract_code_info(),
+ contract_code_info_modern_compilator: Factory.contract_code_info_modern_compilator()}
end
test "compiles the latest solidity version", %{contract_code_info: contract_code_info} do
@@ -35,14 +37,16 @@ defmodule Explorer.SmartContract.Solidity.CodeCompilerTest do
}} = response
end
- test "compiles a optimized smart contract", %{contract_code_info: contract_code_info} do
+ test "compiles a optimized smart contract", %{
+ contract_code_info_modern_compilator: contract_code_info_modern_compilator
+ } do
optimize = true
response =
CodeCompiler.run(
- name: contract_code_info.name,
- compiler_version: contract_code_info.version,
- code: contract_code_info.source_code,
+ name: contract_code_info_modern_compilator.name,
+ compiler_version: contract_code_info_modern_compilator.version,
+ code: contract_code_info_modern_compilator.source_code,
optimize: optimize,
evm_version: "byzantium"
)
@@ -55,14 +59,16 @@ defmodule Explorer.SmartContract.Solidity.CodeCompilerTest do
}} = response
end
- test "compiles smart contract with default evm version", %{contract_code_info: contract_code_info} do
+ test "compiles smart contract with default evm version", %{
+ contract_code_info_modern_compilator: contract_code_info_modern_compilator
+ } do
optimize = true
response =
CodeCompiler.run(
- name: contract_code_info.name,
- compiler_version: contract_code_info.version,
- code: contract_code_info.source_code,
+ name: contract_code_info_modern_compilator.name,
+ compiler_version: contract_code_info_modern_compilator.version,
+ code: contract_code_info_modern_compilator.source_code,
optimize: optimize,
evm_version: "default"
)
@@ -296,7 +302,7 @@ defmodule Explorer.SmartContract.Solidity.CodeCompilerTest do
path = File.cwd!() <> "/test/support/fixture/smart_contract/large_smart_contract.sol"
contract = File.read!(path)
- assert {:ok, %{"abi" => abi}} =
+ assert {:ok, %{"abi" => _abi}} =
CodeCompiler.run(
name: "HomeWorkDeployer",
compiler_version: "v0.5.9+commit.e560f70d",
diff --git a/apps/explorer/test/explorer/smart_contract/publisher_test.exs b/apps/explorer/test/explorer/smart_contract/solidity/publisher_test.exs
similarity index 98%
rename from apps/explorer/test/explorer/smart_contract/publisher_test.exs
rename to apps/explorer/test/explorer/smart_contract/solidity/publisher_test.exs
index 368c4d3a8731..35ec6c3ca2e2 100644
--- a/apps/explorer/test/explorer/smart_contract/publisher_test.exs
+++ b/apps/explorer/test/explorer/smart_contract/solidity/publisher_test.exs
@@ -1,15 +1,15 @@
-defmodule Explorer.SmartContract.PublisherTest do
+defmodule Explorer.SmartContract.Solidity.PublisherTest do
use ExUnit.Case, async: true
use Explorer.DataCase
- doctest Explorer.SmartContract.Publisher
+ doctest Explorer.SmartContract.Solidity.Publisher
@moduletag timeout: :infinity
alias Explorer.Chain.{ContractMethod, SmartContract}
alias Explorer.{Factory, Repo}
- alias Explorer.SmartContract.Publisher
+ alias Explorer.SmartContract.Solidity.Publisher
describe "publish/2" do
test "with valid data creates a smart_contract" do
@@ -86,7 +86,7 @@ defmodule Explorer.SmartContract.PublisherTest do
}
response = Publisher.publish(contract_address.hash, valid_attrs)
- assert {:ok, %SmartContract{} = smart_contract} = response
+ assert {:ok, %SmartContract{} = _smart_contract} = response
Enum.each(contract_code_info.abi, fn selector ->
[parsed] = ABI.parse_specification([selector])
@@ -174,7 +174,7 @@ defmodule Explorer.SmartContract.PublisherTest do
end)
response = Publisher.publish(contract_address.hash, params, external_libraries_form_params)
- assert {:ok, %SmartContract{} = smart_contract} = response
+ assert {:ok, %SmartContract{} = _smart_contract} = response
end
end
end
diff --git a/apps/explorer/test/explorer/smart_contract/solidity/verifier_test.exs b/apps/explorer/test/explorer/smart_contract/solidity/verifier_test.exs
new file mode 100644
index 000000000000..4056d075f22d
--- /dev/null
+++ b/apps/explorer/test/explorer/smart_contract/solidity/verifier_test.exs
@@ -0,0 +1,950 @@
+defmodule Explorer.SmartContract.Solidity.VerifierTest do
+ use ExUnit.Case, async: true
+ use Explorer.DataCase
+
+ @moduletag timeout: :infinity
+
+ doctest Explorer.SmartContract.Solidity.Verifier
+
+ alias Explorer.SmartContract.Solidity.Verifier
+ alias Explorer.Factory
+
+ @code_0_4 """
+ pragma solidity ^0.4.0;
+ contract Incrementer {
+ event Incremented(address indexed sender, uint256 newValue);
+ uint256 public value;
+ address public lastSender;
+ constructor(uint256 initialValue) public {
+ value = initialValue;
+ lastSender = msg.sender;
+ }
+ function inc(uint256 delta) public {
+ value = value + delta;
+ lastSender = msg.sender;
+ }
+ }
+ """
+
+ @code_0_5 """
+ pragma solidity ^0.5.0;
+ contract Incrementer {
+ event Incremented(address indexed sender, uint256 newValue);
+ uint256 public value;
+ address public lastSender;
+ constructor(uint256 initialValue) public {
+ value = initialValue;
+ lastSender = msg.sender;
+ }
+ function inc(uint256 delta) public {
+ value = value + delta;
+ lastSender = msg.sender;
+ }
+ }
+ """
+
+ @code_0_6 """
+ pragma solidity ^0.6.0;
+ contract Incrementer {
+ event Incremented(address indexed sender, uint256 newValue);
+ uint256 public value;
+ address public lastSender;
+ constructor(uint256 initialValue) public {
+ value = initialValue;
+ lastSender = msg.sender;
+ }
+ function inc(uint256 delta) public {
+ value = value + delta;
+ lastSender = msg.sender;
+ }
+ }
+ """
+
+ describe "evaluate_authenticity/2" do
+ setup do
+ {:ok, contract_code_info: Factory.contract_code_info()}
+ end
+
+ test "verifies the generated bytecode against bytecode retrieved from the blockchain", %{
+ contract_code_info: contract_code_info
+ } do
+ contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode)
+ insert(:transaction, created_contract_address_hash: contract_address.hash, input: contract_code_info.tx_input)
+
+ params = %{
+ "contract_source_code" => contract_code_info.source_code,
+ "compiler_version" => contract_code_info.version,
+ "name" => contract_code_info.name,
+ "optimization" => contract_code_info.optimized
+ }
+
+ assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
+ assert abi != nil
+ end
+
+ test "verifies the generated bytecode with external libraries" do
+ contract_data =
+ "#{File.cwd!()}/test/support/fixture/smart_contract/contract_with_lib.json"
+ |> File.read!()
+ |> Jason.decode!()
+ |> List.first()
+
+ compiler_version = contract_data["compiler_version"]
+ external_libraries = contract_data["external_libraries"]
+ name = contract_data["name"]
+ optimize = contract_data["optimize"]
+ contract = contract_data["contract"]
+ expected_bytecode = contract_data["expected_bytecode"]
+ tx_input = contract_data["tx_input"]
+
+ contract_address = insert(:contract_address, contract_code: "0x" <> expected_bytecode)
+ insert(:transaction, created_contract_address_hash: contract_address.hash, input: "0x" <> tx_input)
+
+ params = %{
+ "contract_source_code" => contract,
+ "compiler_version" => compiler_version,
+ "name" => name,
+ "optimization" => optimize,
+ "external_libraries" => external_libraries
+ }
+
+ assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
+ assert abi != nil
+ end
+
+ test "verifies smart contract with new `whisper` metadata (bzz0 => bzz1) in solidity 0.5.11" do
+ contract_data =
+ "#{File.cwd!()}/test/support/fixture/smart_contract/solidity_5.11_new_whisper_metadata.json"
+ |> File.read!()
+ |> Jason.decode!()
+
+ compiler_version = contract_data["compiler_version"]
+ name = contract_data["name"]
+ optimize = false
+ contract = contract_data["contract"]
+ expected_bytecode = contract_data["bytecode"]
+ evm_version = contract_data["evm_version"]
+ input = contract_data["input"]
+
+ contract_address = insert(:contract_address, contract_code: "0x" <> expected_bytecode)
+
+ :transaction
+ |> insert(
+ created_contract_address_hash: contract_address.hash,
+ input: "0x" <> input
+ )
+
+ params = %{
+ "contract_source_code" => contract,
+ "compiler_version" => compiler_version,
+ "evm_version" => evm_version,
+ "name" => name,
+ "optimization" => optimize
+ }
+
+ assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
+ assert abi != nil
+ end
+
+ test "verifies smart contract with constructor arguments", %{
+ contract_code_info: contract_code_info
+ } do
+ contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode)
+
+ constructor_arguments = "0102030405"
+
+ params = %{
+ "contract_source_code" => contract_code_info.source_code,
+ "compiler_version" => contract_code_info.version,
+ "name" => contract_code_info.name,
+ "optimization" => contract_code_info.optimized,
+ "constructor_arguments" => constructor_arguments
+ }
+
+ :transaction
+ |> insert(
+ created_contract_address_hash: contract_address.hash,
+ input: contract_code_info.tx_input <> constructor_arguments
+ )
+ |> with_block()
+
+ assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
+ assert abi != nil
+ end
+
+ test "tries to compile with the latest evm version if wrong evm version was provided" do
+ bytecode =
+ "0x6060604052341561000f57600080fd5b604051602080610223833981016040528080519060200190919050505b8060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b505b61019d806100866000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063256fec88146100545780633fa4f245146100a9578063812600df146100d2575b600080fd5b341561005f57600080fd5b6100676100f5565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100b457600080fd5b6100bc61011b565b6040518082815260200191505060405180910390f35b34156100dd57600080fd5b6100f36004808035906020019091905050610121565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b505600a165627a7a723058201de7017582ff17d45730bc9aedeac5b399399b71b188f42a164609c1b6f7f4760029"
+
+ constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a"
+
+ contract_address = insert(:contract_address, contract_code: bytecode)
+
+ :transaction
+ |> insert(
+ created_contract_address_hash: contract_address.hash,
+ input: bytecode <> constructor_arguments
+ )
+ |> with_block()
+
+ code = """
+ pragma solidity ^0.4.15;
+ contract Incrementer {
+ event Incremented(address indexed sender, uint256 newValue);
+ uint256 public value;
+ address public lastSender;
+ function Incrementer(uint256 initialValue) {
+ value = initialValue;
+ lastSender = msg.sender;
+ }
+ function inc(uint256 delta) {
+ value = value + delta;
+ lastSender = msg.sender;
+ }
+ }
+ """
+
+ params = %{
+ "contract_source_code" => code,
+ "compiler_version" => "v0.4.15+commit.bbb8e64f",
+ "evm_version" => "homestead",
+ "name" => "Incrementer",
+ "optimization" => false,
+ "constructor_arguments" => constructor_arguments
+ }
+
+ assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
+ assert abi != nil
+ end
+
+ test "verifies a library" do
+ bytecode =
+ "0x610102610026600b82828239805160001a60731461001957fe5b30600052607381538281f3fe730000000000000000000000000000000000000000301460806040526004361060335760003560e01c8063c2985578146038575b600080fd5b603e60b0565b6040805160208082528351818301528351919283929083019185019080838360005b8381101560765781810151838201526020016060565b50505050905090810190601f16801560a25780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b604080518082019091526003815262666f6f60e81b60208201529056fea265627a7a7231582079c18e1f9cf2812147d15e5d44f16ff748f8b7349d32dc9db50300a3ffbd3a9664736f6c634300050b0032"
+
+ contract_address = insert(:contract_address, contract_code: bytecode)
+ insert(:transaction, created_contract_address_hash: contract_address.hash, input: bytecode)
+
+ code = """
+ pragma solidity 0.5.11;
+
+ library Foo {
+ function foo() external pure returns (string memory) {
+ return "foo";
+ }
+ }
+ """
+
+ params = %{
+ "contract_source_code" => code,
+ "compiler_version" => "v0.5.11+commit.c082d0b4",
+ "evm_version" => "default",
+ "name" => "Foo",
+ "optimization" => true
+ }
+
+ assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
+ assert abi != nil
+ end
+
+ test "verifies smart contract compiled with Solidity 0.5.9 (includes new metadata in bytecode) with constructor args" do
+ path = File.cwd!() <> "/test/support/fixture/smart_contract/solidity_0.5.9_smart_contract.sol"
+ contract = File.read!(path)
+
+ constructor_arguments =
+ "00000000000000000000000000000000000000000000003635c9adc5dea000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000a54657374546f6b656e32000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006546f6b656e320000000000000000000000000000000000000000000000000000"
+
+ bytecode =
+ "0x608060405234801561001057600080fd5b50600436106100a95760003560e01c80633177029f116100715780633177029f1461025f57806354fd4d50146102c557806370a082311461034857806395d89b41146103a0578063a9059cbb14610423578063dd62ed3e14610489576100a9565b806306fdde03146100ae578063095ea7b31461013157806318160ddd1461019757806323b872dd146101b5578063313ce5671461023b575b600080fd5b6100b6610501565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f65780820151818401526020810190506100db565b50505050905090810190601f1680156101235780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61017d6004803603604081101561014757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061059f565b604051808215151515815260200191505060405180910390f35b61019f610691565b6040518082815260200191505060405180910390f35b610221600480360360608110156101cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610696565b604051808215151515815260200191505060405180910390f35b61024361090f565b604051808260ff1660ff16815260200191505060405180910390f35b6102ab6004803603604081101561027557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610922565b604051808215151515815260200191505060405180910390f35b6102cd610a14565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561030d5780820151818401526020810190506102f2565b50505050905090810190601f16801561033a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61038a6004803603602081101561035e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ab2565b6040518082815260200191505060405180910390f35b6103a8610afa565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103e85780820151818401526020810190506103cd565b50505050905090810190601f1680156104155780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61046f6004803603604081101561043957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610b98565b604051808215151515815260200191505060405180910390f35b6104eb6004803603604081101561049f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cfe565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105975780601f1061056c57610100808354040283529160200191610597565b820191906000526020600020905b81548152906001019060200180831161057a57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b600090565b6000816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610762575081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410155b801561076e5750600082115b1561090357816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610908565b600090505b9392505050565b600460009054906101000a900460ff1681565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60068054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610aaa5780601f10610a7f57610100808354040283529160200191610aaa565b820191906000526020600020905b815481529060010190602001808311610a8d57829003601f168201915b505050505081565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b905780601f10610b6557610100808354040283529160200191610b90565b820191906000526020600020905b815481529060010190602001808311610b7357829003601f168201915b505050505081565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610be85750600082115b15610cf357816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610cf8565b600090505b92915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490509291505056fea265627a7a72305820fe0ba5210ac95870683c2cb054304b04565703bd16c7d7e956df694c9643c6d264736f6c63430005090032"
+
+ input =
+ "0x60806040526040518060400160405280600381526020017f302e3100000000000000000000000000000000000000000000000000000000008152506006908051906020019062000051929190620001e2565b503480156200005f57600080fd5b506040516200105b3803806200105b833981810160405260808110156200008557600080fd5b81019080805190602001909291908051640100000000811115620000a857600080fd5b82810190506020810184811115620000bf57600080fd5b8151856001820283011164010000000082111715620000dd57600080fd5b50509291906020018051906020019092919080516401000000008111156200010457600080fd5b828101905060208101848111156200011b57600080fd5b81518560018202830111640100000000821117156200013957600080fd5b5050929190505050836000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550836002819055508260039080519060200190620001a3929190620001e2565b5081600460006101000a81548160ff021916908360ff1602179055508060059080519060200190620001d7929190620001e2565b505050505062000291565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200022557805160ff191683800117855562000256565b8280016001018555821562000256579182015b828111156200025557825182559160200191906001019062000238565b5b50905062000265919062000269565b5090565b6200028e91905b808211156200028a57600081600090555060010162000270565b5090565b90565b610dba80620002a16000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c80633177029f116100715780633177029f1461025f57806354fd4d50146102c557806370a082311461034857806395d89b41146103a0578063a9059cbb14610423578063dd62ed3e14610489576100a9565b806306fdde03146100ae578063095ea7b31461013157806318160ddd1461019757806323b872dd146101b5578063313ce5671461023b575b600080fd5b6100b6610501565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f65780820151818401526020810190506100db565b50505050905090810190601f1680156101235780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61017d6004803603604081101561014757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061059f565b604051808215151515815260200191505060405180910390f35b61019f610691565b6040518082815260200191505060405180910390f35b610221600480360360608110156101cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610696565b604051808215151515815260200191505060405180910390f35b61024361090f565b604051808260ff1660ff16815260200191505060405180910390f35b6102ab6004803603604081101561027557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610922565b604051808215151515815260200191505060405180910390f35b6102cd610a14565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561030d5780820151818401526020810190506102f2565b50505050905090810190601f16801561033a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61038a6004803603602081101561035e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ab2565b6040518082815260200191505060405180910390f35b6103a8610afa565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103e85780820151818401526020810190506103cd565b50505050905090810190601f1680156104155780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61046f6004803603604081101561043957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610b98565b604051808215151515815260200191505060405180910390f35b6104eb6004803603604081101561049f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cfe565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105975780601f1061056c57610100808354040283529160200191610597565b820191906000526020600020905b81548152906001019060200180831161057a57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b600090565b6000816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610762575081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410155b801561076e5750600082115b1561090357816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610908565b600090505b9392505050565b600460009054906101000a900460ff1681565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60068054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610aaa5780601f10610a7f57610100808354040283529160200191610aaa565b820191906000526020600020905b815481529060010190602001808311610a8d57829003601f168201915b505050505081565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b905780601f10610b6557610100808354040283529160200191610b90565b820191906000526020600020905b815481529060010190602001808311610b7357829003601f168201915b505050505081565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610be85750600082115b15610cf357816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610cf8565b600090505b92915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490509291505056fea265627a7a72305820c63b805fe76ce64d7ba5aedfa99bbfa5b13cc8c2fabad31b2364f8ae421cd40c64736f6c63430005090032"
+
+ contract_address = insert(:contract_address, contract_code: bytecode)
+
+ :transaction
+ |> insert(
+ created_contract_address_hash: contract_address.hash,
+ input: input <> constructor_arguments
+ )
+ |> with_block()
+
+ params = %{
+ "contract_source_code" => contract,
+ "compiler_version" => "v0.5.9+commit.e560f70d",
+ "evm_version" => "petersburg",
+ "name" => "TestToken",
+ "optimization" => false,
+ "constructor_arguments" => constructor_arguments
+ }
+
+ assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
+ assert abi != nil
+ end
+
+ # flaky test
+ # test "returns error when bytecode doesn't match", %{contract_code_info: contract_code_info} do
+ # contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode)
+ # insert(:transaction, created_contract_address_hash: contract_address.hash)
+
+ # different_code = "pragma solidity ^0.4.24; contract SimpleStorage {}"
+
+ # params = %{
+ # "contract_source_code" => different_code,
+ # "compiler_version" => contract_code_info.version,
+ # "evm_version" => "default",
+ # "name" => contract_code_info.name,
+ # "optimization" => contract_code_info.optimized
+ # }
+
+ # response = Verifier.evaluate_authenticity(contract_address.hash, params)
+
+ # assert {:error, :generated_bytecode} = response
+ # end
+
+ # flaky test
+ # test "returns error when contract has constructor arguments and they were not provided" do
+ # path = File.cwd!() <> "/test/support/fixture/smart_contract/solidity_0.5.9_smart_contract.sol"
+ # contract = File.read!(path)
+
+ # constructor_arguments = ""
+
+ # bytecode =
+ # "0x608060405234801561001057600080fd5b50600436106100a95760003560e01c80633177029f116100715780633177029f1461025f57806354fd4d50146102c557806370a082311461034857806395d89b41146103a0578063a9059cbb14610423578063dd62ed3e14610489576100a9565b806306fdde03146100ae578063095ea7b31461013157806318160ddd1461019757806323b872dd146101b5578063313ce5671461023b575b600080fd5b6100b6610501565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f65780820151818401526020810190506100db565b50505050905090810190601f1680156101235780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61017d6004803603604081101561014757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061059f565b604051808215151515815260200191505060405180910390f35b61019f610691565b6040518082815260200191505060405180910390f35b610221600480360360608110156101cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610696565b604051808215151515815260200191505060405180910390f35b61024361090f565b604051808260ff1660ff16815260200191505060405180910390f35b6102ab6004803603604081101561027557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610922565b604051808215151515815260200191505060405180910390f35b6102cd610a14565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561030d5780820151818401526020810190506102f2565b50505050905090810190601f16801561033a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61038a6004803603602081101561035e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ab2565b6040518082815260200191505060405180910390f35b6103a8610afa565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103e85780820151818401526020810190506103cd565b50505050905090810190601f1680156104155780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61046f6004803603604081101561043957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610b98565b604051808215151515815260200191505060405180910390f35b6104eb6004803603604081101561049f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cfe565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105975780601f1061056c57610100808354040283529160200191610597565b820191906000526020600020905b81548152906001019060200180831161057a57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b600090565b6000816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610762575081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410155b801561076e5750600082115b1561090357816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610908565b600090505b9392505050565b600460009054906101000a900460ff1681565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60068054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610aaa5780601f10610a7f57610100808354040283529160200191610aaa565b820191906000526020600020905b815481529060010190602001808311610a8d57829003601f168201915b505050505081565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b905780601f10610b6557610100808354040283529160200191610b90565b820191906000526020600020905b815481529060010190602001808311610b7357829003601f168201915b505050505081565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610be85750600082115b15610cf357816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610cf8565b600090505b92915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490509291505056fea265627a7a72305820fe0ba5210ac95870683c2cb054304b04565703bd16c7d7e956df694c9643c6d264736f6c63430005090032"
+
+ # contract_address = insert(:contract_address, contract_code: bytecode)
+
+ # :transaction
+ # |> insert(
+ # created_contract_address_hash: contract_address.hash,
+ # input: bytecode <> constructor_arguments
+ # )
+ # |> with_block()
+
+ # params = %{
+ # "contract_source_code" => contract,
+ # "compiler_version" => "v0.5.9+commit.e560f70d",
+ # "evm_version" => "petersburg",
+ # "name" => "TestToken",
+ # "optimization" => false,
+ # "constructor_arguments" => constructor_arguments
+ # }
+
+ # assert {:error, :generated_bytecode} = Verifier.evaluate_authenticity(contract_address.hash, params)
+ # end
+
+ test "returns error when there is a compilation problem", %{contract_code_info: contract_code_info} do
+ contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode)
+
+ params = %{
+ "contract_source_code" => "pragma solidity ^0.4.24; contract SimpleStorage { ",
+ "compiler_version" => contract_code_info.version,
+ "name" => contract_code_info.name,
+ "optimization" => contract_code_info.optimized
+ }
+
+ assert {:error, :compilation, "Function, variable, struct or modifier declaration expected."} =
+ Verifier.evaluate_authenticity(contract_address.hash, params)
+ end
+ end
+
+ describe "extract_bytecode_and_metadata_hash/1" do
+ test "extracts the bytecode from the hash" do
+ code =
+ "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a723058203c381c1b48b38d050c54d7ef296ecd411040e19420dfec94772b9c49ae106a0b0029"
+
+ swarm_source = "3c381c1b48b38d050c54d7ef296ecd411040e19420dfec94772b9c49ae106a0b"
+
+ bytecode =
+ "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600"
+
+ %{"metadata_hash" => _metadata_hash, "bytecode" => bytecode_from_code, "compiler_version" => _compiler_version} =
+ Verifier.extract_bytecode_and_metadata_hash(code)
+
+ assert bytecode == bytecode_from_code
+ assert bytecode != code
+ assert String.contains?(code, bytecode) == true
+ assert String.contains?(bytecode, "0029") == false
+ assert String.contains?(bytecode, swarm_source) == false
+ end
+
+ test "extracts everything to the left of the swarm hash" do
+ code =
+ "0x608060405234801561001057600080fd5b5060df80610010029f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a723058203c381c1b48b38d050c54d7ef296ecd411040e19420dfec94772b9c49ae106a0b0029"
+
+ swarm_source = "3c381c1b48b38d050c54d7ef296ecd411040e19420dfec94772b9c49ae106a0b"
+
+ bytecode =
+ "0x608060405234801561001057600080fd5b5060df80610010029f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600"
+
+ %{"metadata_hash" => _metadata_hash, "bytecode" => bytecode_from_code, "compiler_version" => _compiler_version} =
+ Verifier.extract_bytecode_and_metadata_hash(code)
+
+ assert bytecode == bytecode_from_code
+ assert bytecode != code
+ assert String.contains?(code, bytecode) == true
+ assert String.contains?(bytecode, "0029") == true
+ assert String.contains?(bytecode, swarm_source) == false
+ end
+
+ # https://solidity.readthedocs.io/en/v0.6.6/contracts.html?highlight=immutable#constant-and-immutable-state-variables
+ test "verifies smart-contract with `immutable` assignment" do
+ bytecode =
+ "0x6080604052348015600f57600080fd5b506004361060285760003560e01c8063fb49908514602d575b600080fd5b605060048036036020811015604157600080fd5b50356001600160a01b03166064565b604080519115158252519081900360200190f35b7f0000000000000000000000000000000000000000000000056b3977a93ae7c2006001600160a01b038216311191905056fea2646970667358221220b4fbf35809f2d1b85699a897ebb75d00c8c26b29b72decc53db18ddbd853352164736f6c63430006070033"
+
+ tx_input =
+ "0x60e06040523360601b60c05234801561001757600080fd5b5060405161013f38038061013f8339818101604052604081101561003a57600080fd5b50805160209091015160808290526001600160a01b03163160a081905260c05160601c60cc6100736000395080606652505060cc6000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063fb49908514602d575b600080fd5b605060048036036020811015604157600080fd5b50356001600160a01b03166064565b604080519115158252519081900360200190f35b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b038216311191905056fea2646970667358221220b4fbf35809f2d1b85699a897ebb75d00c8c26b29b72decc53db18ddbd853352164736f6c63430006070033000000000000000000000000000000000000000000000000000000000000000100000000000000000000000023602745048d3b8d0a7f953ad444da4cd237ac83"
+
+ contract_address = insert(:contract_address, contract_code: bytecode)
+ insert(:transaction, created_contract_address_hash: contract_address.hash, input: tx_input)
+
+ code = """
+ pragma solidity >0.6.4 <0.7.0;
+
+ contract C {
+ uint constant X = 32**22 + 8;
+ string constant TEXT = "abc";
+ bytes32 constant MY_HASH = keccak256("abc");
+ uint immutable decimals;
+ uint immutable maxBalance;
+ address immutable owner = msg.sender;
+
+ constructor(uint _decimals, address _reference) public {
+ decimals = _decimals;
+ // Assignments to immutables can even access the environment.
+ maxBalance = _reference.balance;
+ }
+
+ function isBalanceTooHigh(address _other) public view returns (bool) {
+ return _other.balance > maxBalance;
+ }
+ }
+ """
+
+ constructor_arguments =
+ "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000023602745048d3b8d0a7f953ad444da4cd237ac83"
+
+ params = %{
+ "contract_source_code" => code,
+ "compiler_version" => "v0.6.7+commit.b8d736ae",
+ "evm_version" => "default",
+ "name" => "C",
+ "optimization" => true,
+ "constructor_arguments" => constructor_arguments
+ }
+
+ assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
+ assert abi != nil
+ end
+
+ test "verifies smart-contract created from another contract" do
+ path = File.cwd!() <> "/test/support/fixture/smart_contract/contract_from_factory.sol"
+ contract = File.read!(path)
+
+ constructor_arguments = "4e1477bdc40fc2458bf646f96f269502658277779fdf0f4080fe798a2d45bc37"
+
+ bytecode =
+ "0x608060405260043610603e5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416638052474d81146043575b600080fd5b348015604e57600080fd5b5060556067565b60408051918252519081900360200190f35b600054815600a165627a7a72305820a1a0ec90e133c3064fab0ae82aa02a020224ea39d2b5421b6788f800bdde02f60029"
+
+ init =
+ "0x608060405234801561001057600080fd5b506040516020806100cc83398101604052516000556099806100336000396000f300608060405260043610603e5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416638052474d81146043575b600080fd5b348015604e57600080fd5b5060556067565b60408051918252519081900360200190f35b600054815600a165627a7a72305820a1a0ec90e133c3064fab0ae82aa02a020224ea39d2b5421b6788f800bdde02f600294e1477bdc40fc2458bf646f96f269502658277779fdf0f4080fe798a2d45bc37"
+
+ contract_address = insert(:contract_address, contract_code: bytecode)
+
+ transaction_success_details = [
+ status: :ok
+ ]
+
+ transaction =
+ :transaction
+ |> insert()
+ |> with_block(transaction_success_details)
+
+ :internal_transaction
+ |> insert(
+ created_contract_address_hash: contract_address.hash,
+ init: init,
+ type: "create",
+ created_contract_code: bytecode,
+ input: nil,
+ transaction_hash: transaction.hash,
+ index: 0,
+ block_hash: transaction.block_hash,
+ block_index: 0
+ )
+
+ params = %{
+ "contract_source_code" => contract,
+ "compiler_version" => "v0.4.26+commit.4563c3fc",
+ "evm_version" => "default",
+ "name" => "ContractFromFactory",
+ "optimization" => true,
+ "constructor_arguments" => constructor_arguments
+ }
+
+ assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
+ assert abi != nil
+ end
+
+ test "verifies smart-contract created from another contract using successful tx" do
+ path = File.cwd!() <> "/test/support/fixture/smart_contract/contract_from_factory.sol"
+ contract = File.read!(path)
+
+ constructor_arguments = "4e1477bdc40fc2458bf646f96f269502658277779fdf0f4080fe798a2d45bc37"
+
+ bytecode =
+ "0x608060405260043610603e5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416638052474d81146043575b600080fd5b348015604e57600080fd5b5060556067565b60408051918252519081900360200190f35b600054815600a165627a7a72305820a1a0ec90e133c3064fab0ae82aa02a020224ea39d2b5421b6788f800bdde02f60029"
+
+ init =
+ "0x608060405234801561001057600080fd5b506040516020806100cc83398101604052516000556099806100336000396000f300608060405260043610603e5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416638052474d81146043575b600080fd5b348015604e57600080fd5b5060556067565b60408051918252519081900360200190f35b600054815600a165627a7a72305820a1a0ec90e133c3064fab0ae82aa02a020224ea39d2b5421b6788f800bdde02f600294e1477bdc40fc2458bf646f96f269502658277779fdf0f4080fe798a2d45bc37"
+
+ contract_address = insert(:contract_address, contract_code: bytecode)
+
+ transaction_success_details = [
+ status: :ok
+ ]
+
+ transaction_success =
+ :transaction
+ |> insert()
+ |> with_block(transaction_success_details)
+
+ transaction_failure_details = [
+ status: :error
+ ]
+
+ transaction_failure =
+ :transaction
+ |> insert()
+ |> with_block(transaction_failure_details)
+
+ :internal_transaction
+ |> insert(
+ created_contract_address_hash: contract_address.hash,
+ init: init,
+ type: "create",
+ created_contract_code: bytecode,
+ input: nil,
+ transaction_hash: transaction_success.hash,
+ index: 0,
+ block_hash: transaction_success.block_hash,
+ block_index: 0
+ )
+
+ :internal_transaction
+ |> insert(
+ created_contract_address_hash: contract_address.hash,
+ init: init,
+ type: "create",
+ created_contract_code: bytecode,
+ input: nil,
+ transaction_hash: transaction_failure.hash,
+ index: 0,
+ block_hash: transaction_failure.block_hash,
+ block_index: 0
+ )
+
+ params = %{
+ "contract_source_code" => contract,
+ "compiler_version" => "v0.4.26+commit.4563c3fc",
+ "evm_version" => "default",
+ "name" => "ContractFromFactory",
+ "optimization" => true,
+ "constructor_arguments" => constructor_arguments
+ }
+
+ assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
+ assert abi != nil
+ end
+ end
+
+ describe "compiler version tests" do
+ # flaky test
+ # test "verification is failed if wrong version of compiler" do
+ # bytecode_0_5_10 =
+ # "0x608060405234801561001057600080fd5b506040516102453803806102458339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101a98061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72305820fb47165501c50aae8ccb0394b15f4302606e0ba55eb6d59fe12eca19ba494d5e64736f6c634300050a0032"
+
+ # constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a"
+ # contract_address = insert(:contract_address, contract_code: bytecode_0_5_10)
+ # bytecode_construtor_arguments = "#{bytecode_0_5_10}#{constructor_arguments}"
+
+ # :transaction
+ # |> insert(
+ # created_contract_address_hash: contract_address.hash,
+ # input: bytecode_construtor_arguments
+ # )
+ # |> with_block()
+
+ # params = %{
+ # "contract_source_code" => @code_0_5,
+ # "compiler_version" => "v0.5.11+commit.c082d0b4",
+ # "evm_version" => "homestead",
+ # "name" => "Incrementer",
+ # "optimization" => false,
+ # "constructor_arguments" => constructor_arguments
+ # }
+
+ # response = Verifier.evaluate_authenticity(contract_address.hash, params)
+ # assert {:error, :compiler_version} = response
+ # end
+
+ test "verification is successful if proper version of compiler" do
+ bytecode_0_5_10 =
+ "0x608060405234801561001057600080fd5b506040516102453803806102458339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101a98061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72305820fb47165501c50aae8ccb0394b15f4302606e0ba55eb6d59fe12eca19ba494d5e64736f6c634300050a0032"
+
+ constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a"
+ contract_address = insert(:contract_address, contract_code: bytecode_0_5_10)
+ bytecode_construtor_arguments = "#{bytecode_0_5_10}#{constructor_arguments}"
+
+ :transaction
+ |> insert(
+ created_contract_address_hash: contract_address.hash,
+ input: bytecode_construtor_arguments
+ )
+ |> with_block()
+
+ params = %{
+ "contract_source_code" => @code_0_5,
+ "compiler_version" => "v0.5.10+commit.5a6ea5b1",
+ "evm_version" => "homestead",
+ "name" => "Incrementer",
+ "optimization" => false,
+ "constructor_arguments" => constructor_arguments
+ }
+
+ assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
+ assert abi != nil
+ end
+ end
+
+ describe "verification with nightly builds" do
+ test "verification is successful if proper nightly version of compiler ~0.4" do
+ bytecode_v0_4_24_nightly_2018_4_26_commit_ef2111a2 =
+ "0x608060405234801561001057600080fd5b5060405160208061023d833981018060405281019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101b28061008b6000396000f300608060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063256fec881461005c5780633fa4f245146100b3578063812600df146100de575b600080fd5b34801561006857600080fd5b5061007161010b565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156100bf57600080fd5b506100c8610131565b6040518082815260200191505060405180910390f35b3480156100ea57600080fd5b5061010960048036038101908080359060200190929190505050610137565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505600a165627a7a723058202d622d653be0a507f7ac0bc89d8934ccdbaf5e127abd603c3864a462149885070029"
+
+ constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a"
+ contract_address = insert(:contract_address, contract_code: bytecode_v0_4_24_nightly_2018_4_26_commit_ef2111a2)
+ bytecode_construtor_arguments = "#{bytecode_v0_4_24_nightly_2018_4_26_commit_ef2111a2}#{constructor_arguments}"
+
+ :transaction
+ |> insert(
+ created_contract_address_hash: contract_address.hash,
+ input: bytecode_construtor_arguments
+ )
+ |> with_block()
+
+ params = %{
+ "contract_source_code" => @code_0_4,
+ "compiler_version" => "v0.4.24-nightly.2018.4.26+commit.ef2111a2",
+ "evm_version" => "homestead",
+ "name" => "Incrementer",
+ "optimization" => false,
+ "constructor_arguments" => constructor_arguments
+ }
+
+ assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
+ assert abi != nil
+ end
+
+ test "verification is successful if proper nightly version of compiler ~0.5.10" do
+ bytecode_0_5_10_nightly_2019_6_4_commit_95e6b2e4 =
+ "0x608060405234801561001057600080fd5b5060405161026a38038061026a8339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101ce8061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a723058208d4e3fa9b2179a8384e617e388dde334be1b44e7b11b42ab964ab1050e7cedca64736f6c637827302e352e31302d6e696768746c792e323031392e362e342b636f6d6d69742e39356536623265340057"
+
+ constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a"
+ contract_address = insert(:contract_address, contract_code: bytecode_0_5_10_nightly_2019_6_4_commit_95e6b2e4)
+ bytecode_construtor_arguments = "#{bytecode_0_5_10_nightly_2019_6_4_commit_95e6b2e4}#{constructor_arguments}"
+
+ :transaction
+ |> insert(
+ created_contract_address_hash: contract_address.hash,
+ input: bytecode_construtor_arguments
+ )
+ |> with_block()
+
+ params = %{
+ "contract_source_code" => @code_0_5,
+ "compiler_version" => "v0.5.10-nightly.2019.6.4+commit.95e6b2e4",
+ "evm_version" => "homestead",
+ "name" => "Incrementer",
+ "optimization" => false,
+ "constructor_arguments" => constructor_arguments
+ }
+
+ assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
+ assert abi != nil
+ end
+
+ test "verification is successful if proper nightly version of compiler ~0.5.11" do
+ bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753 =
+ "0x608060405234801561001057600080fd5b5060405161026b38038061026b8339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101cf8061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72305820f7420b8c3b16d83ce728d8c279f0f887c4dcd7bfcd38c484acc9cdb82fde785764736f6c637828302e352e31312d6e696768746c792e323031392e362e32352b636f6d6d69742e31636338343735330058"
+
+ constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a"
+ contract_address = insert(:contract_address, contract_code: bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753)
+ bytecode_construtor_arguments = "#{bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753}#{constructor_arguments}"
+
+ :transaction
+ |> insert(
+ created_contract_address_hash: contract_address.hash,
+ input: bytecode_construtor_arguments
+ )
+ |> with_block()
+
+ params = %{
+ "contract_source_code" => @code_0_5,
+ "compiler_version" => "v0.5.11-nightly.2019.6.25+commit.1cc84753",
+ "evm_version" => "homestead",
+ "name" => "Incrementer",
+ "optimization" => false,
+ "constructor_arguments" => constructor_arguments
+ }
+
+ assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
+ assert abi != nil
+ end
+
+ test "verification is successful if proper nightly version of compiler ~0.5.14" do
+ bytecode_0_5_14_nightly_2019_12_10_commit_45aa7a88 =
+ "0x608060405234801561001057600080fd5b5060405161026c38038061026c8339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101d08061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72315820ec5a7ce04b1c2f97a3d3e61ae1b5cb06585e81c504542fd9668a8ead654da72764736f6c637829302e352e31342d6e696768746c792e323031392e31322e31302b636f6d6d69742e34356161376138380059"
+
+ constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a"
+ contract_address = insert(:contract_address, contract_code: bytecode_0_5_14_nightly_2019_12_10_commit_45aa7a88)
+ bytecode_construtor_arguments = "#{bytecode_0_5_14_nightly_2019_12_10_commit_45aa7a88}#{constructor_arguments}"
+
+ :transaction
+ |> insert(
+ created_contract_address_hash: contract_address.hash,
+ input: bytecode_construtor_arguments
+ )
+ |> with_block()
+
+ params = %{
+ "contract_source_code" => @code_0_5,
+ "compiler_version" => "v0.5.14-nightly.2019.12.10+commit.45aa7a88",
+ "evm_version" => "homestead",
+ "name" => "Incrementer",
+ "optimization" => false,
+ "constructor_arguments" => constructor_arguments
+ }
+
+ assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
+ assert abi != nil
+ end
+
+ test "verification is successful if proper nightly version of compiler ~0.6.0" do
+ bytecode_0_6_1_nightly_2020_1_2_commit_d082b9b8 =
+ "0x608060405234801561001057600080fd5b5060405161026a38038061026a8339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101ce8061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea264697066735822122029b5dde5889a195ed02cebb1a638ae3754be34464b9a2bc8b48b6286636031fb64736f6c637826302e362e312d6e696768746c792e323032302e312e322b636f6d6d69742e64303832623962380057"
+
+ constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a"
+ contract_address = insert(:contract_address, contract_code: bytecode_0_6_1_nightly_2020_1_2_commit_d082b9b8)
+ bytecode_construtor_arguments = "#{bytecode_0_6_1_nightly_2020_1_2_commit_d082b9b8}#{constructor_arguments}"
+
+ :transaction
+ |> insert(
+ created_contract_address_hash: contract_address.hash,
+ input: bytecode_construtor_arguments
+ )
+ |> with_block()
+
+ params = %{
+ "contract_source_code" => @code_0_6,
+ "compiler_version" => "v0.6.1-nightly.2020.1.2+commit.d082b9b8",
+ "evm_version" => "homestead",
+ "name" => "Incrementer",
+ "optimization" => false,
+ "constructor_arguments" => constructor_arguments
+ }
+
+ assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
+ assert abi != nil
+ end
+
+ test "verification is failed if wrong nightly version of compiler ~0.5.11" do
+ bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753 =
+ "0x608060405234801561001057600080fd5b5060405161026b38038061026b8339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101cf8061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72305820f5be0e6763c440be759726643bdd4b03370e9f1b58fd803ab18b0b4f2aa58b7664736f6c637828302e352e31312d6e696768746c792e323031392e362e32352b636f6d6d69742e31636338343735330058"
+
+ constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a"
+ contract_address = insert(:contract_address, contract_code: bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753)
+ bytecode_construtor_arguments = "#{bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753}#{constructor_arguments}"
+
+ :transaction
+ |> insert(
+ created_contract_address_hash: contract_address.hash,
+ input: bytecode_construtor_arguments
+ )
+ |> with_block(status: :ok)
+
+ params = %{
+ "contract_source_code" => @code_0_5,
+ "compiler_version" => "v0.5.11-nightly.2019.8.10+commit.f5f2bbb2",
+ "evm_version" => "homestead",
+ "name" => "Incrementer",
+ "optimization" => false,
+ "constructor_arguments" => constructor_arguments
+ }
+
+ response = Verifier.evaluate_authenticity(contract_address.hash, params)
+ assert {:error, :compiler_version} = response
+ end
+ end
+
+ describe "regression tests for https://github.com/blockscout/blockscout/pull/5166" do
+ test "issue 5114" do
+ contract_source_code =
+ "#{File.cwd!()}/test/support/fixture/smart_contract/issue_5114.sol"
+ |> File.read!()
+
+ bytecode =
+ "0x608060405260405162000f4038038062000f408339810160408190526200002691620004d4565b82816200005560017f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbd62000603565b60008051602062000ef9833981519152146200008157634e487b7160e01b600052600160045260246000fd5b6200008f82826000620000ff565b50620000bf905060017fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610462000603565b60008051602062000ed983398151915214620000eb57634e487b7160e01b600052600160045260246000fd5b620000f68262000170565b5050506200066c565b6200010a83620001cb565b6040516001600160a01b038416907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a26000825111806200014c5750805b156200016b576200016983836200029360201b6200026c1760201c565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6200019b620002c2565b604080516001600160a01b03928316815291841660208301520160405180910390a1620001c881620002fb565b50565b620001e1816200038b60201b620002981760201c565b620002495760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b60648201526084015b60405180910390fd5b806200027260008051602062000ef983398151915260001b6200039560201b620002141760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b6060620002bb838360405180606001604052806027815260200162000f196027913962000398565b9392505050565b6000620002ec60008051602062000ed983398151915260001b6200039560201b620002141760201c565b546001600160a01b0316905090565b6001600160a01b038116620003625760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b606482015260840162000240565b806200027260008051602062000ed983398151915260001b6200039560201b620002141760201c565b803b15155b919050565b90565b6060620003a5846200038b565b620004025760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b606482015260840162000240565b600080856001600160a01b0316856040516200041f9190620005b0565b600060405180830381855af49150503d80600081146200045c576040519150601f19603f3d011682016040523d82523d6000602084013e62000461565b606091505b509092509050620004748282866200047e565b9695505050505050565b606083156200048f575081620002bb565b825115620004a05782518084602001fd5b8160405162461bcd60e51b8152600401620002409190620005ce565b80516001600160a01b03811681146200039057600080fd5b600080600060608486031215620004e9578283fd5b620004f484620004bc565b92506200050460208501620004bc565b60408501519092506001600160401b038082111562000521578283fd5b818601915086601f83011262000535578283fd5b8151818111156200054a576200054a62000656565b604051601f8201601f19908116603f0116810190838211818310171562000575576200057562000656565b816040528281528960208487010111156200058e578586fd5b620005a183602083016020880162000627565b80955050505050509250925092565b60008251620005c481846020870162000627565b9190910192915050565b6000602082528251806020840152620005ef81604085016020870162000627565b601f01601f19169190910160400192915050565b6000828210156200062257634e487b7160e01b81526011600452602481fd5b500390565b60005b83811015620006445781810151838201526020016200062a565b83811115620001695750506000910152565b634e487b7160e01b600052604160045260246000fd5b61085d806200067c6000396000f3fe60806040526004361061004e5760003560e01c80633659cfe6146100655780634f1ef286146100855780635c60da1b146100985780638f283970146100c9578063f851a440146100e95761005d565b3661005d5761005b6100fe565b005b61005b6100fe565b34801561007157600080fd5b5061005b6100803660046106ed565b610118565b61005b610093366004610707565b610164565b3480156100a457600080fd5b506100ad6101da565b6040516001600160a01b03909116815260200160405180910390f35b3480156100d557600080fd5b5061005b6100e43660046106ed565b610217565b3480156100f557600080fd5b506100ad610241565b6101066102a2565b610116610111610346565b610355565b565b610120610379565b6001600160a01b0316336001600160a01b0316141561015957610154816040518060200160405280600081525060006103ac565b610161565b6101616100fe565b50565b61016c610379565b6001600160a01b0316336001600160a01b031614156101cd576101c88383838080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250600192506103ac915050565b6101d5565b6101d56100fe565b505050565b60006101e4610379565b6001600160a01b0316336001600160a01b0316141561020c57610205610346565b9050610214565b6102146100fe565b90565b61021f610379565b6001600160a01b0316336001600160a01b03161415610159576101548161040b565b600061024b610379565b6001600160a01b0316336001600160a01b0316141561020c57610205610379565b606061029183836040518060600160405280602781526020016108016027913961045f565b9392505050565b803b15155b919050565b6102aa610379565b6001600160a01b0316336001600160a01b031614156103415760405162461bcd60e51b815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267606482015261195d60f21b608482015260a4015b60405180910390fd5b610116565b600061035061053a565b905090565b3660008037600080366000845af43d6000803e808015610374573d6000f35b3d6000fd5b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b546001600160a01b0316905090565b6103b583610562565b6040516001600160a01b038416907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a26000825111806103f65750805b156101d557610405838361026c565b50505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f610434610379565b604080516001600160a01b03928316815291841660208301520160405180910390a161016181610611565b606061046a84610298565b6104c55760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b6064820152608401610338565b600080856001600160a01b0316856040516104e09190610785565b600060405180830381855af49150503d806000811461051b576040519150601f19603f3d011682016040523d82523d6000602084013e610520565b606091505b509150915061053082828661069d565b9695505050505050565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc61039d565b61056b81610298565b6105cd5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401610338565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5b80546001600160a01b0319166001600160a01b039290921691909117905550565b6001600160a01b0381166106765760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b6064820152608401610338565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61036105f0565b606083156106ac575081610291565b8251156106bc5782518084602001fd5b8160405162461bcd60e51b815260040161033891906107a1565b80356001600160a01b038116811461029d57600080fd5b6000602082840312156106fe578081fd5b610291826106d6565b60008060006040848603121561071b578182fd5b610724846106d6565b9250602084013567ffffffffffffffff80821115610740578384fd5b818601915086601f830112610753578384fd5b813581811115610761578485fd5b876020828501011115610772578485fd5b6020830194508093505050509250925092565b600082516107978184602087016107d4565b9190910192915050565b60006020825282518060208401526107c08160408501602087016107d4565b601f01601f19169190910160400192915050565b60005b838110156107ef5781810151838201526020016107d7565b83811115610405575050600091015256fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122016ea36e15be10f9560025e0ec9401e2e9110cb5ec41d110b4a0e391838c1f19b64736f6c63430008020033b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564"
+
+ contract_creation_code =
+ "0x608060405260405162000f4038038062000f408339810160408190526200002691620004d4565b82816200005560017f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbd62000603565b60008051602062000ef9833981519152146200008157634e487b7160e01b600052600160045260246000fd5b6200008f82826000620000ff565b50620000bf905060017fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610462000603565b60008051602062000ed983398151915214620000eb57634e487b7160e01b600052600160045260246000fd5b620000f68262000170565b5050506200066c565b6200010a83620001cb565b6040516001600160a01b038416907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a26000825111806200014c5750805b156200016b576200016983836200029360201b6200026c1760201c565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6200019b620002c2565b604080516001600160a01b03928316815291841660208301520160405180910390a1620001c881620002fb565b50565b620001e1816200038b60201b620002981760201c565b620002495760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b60648201526084015b60405180910390fd5b806200027260008051602062000ef983398151915260001b6200039560201b620002141760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b6060620002bb838360405180606001604052806027815260200162000f196027913962000398565b9392505050565b6000620002ec60008051602062000ed983398151915260001b6200039560201b620002141760201c565b546001600160a01b0316905090565b6001600160a01b038116620003625760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b606482015260840162000240565b806200027260008051602062000ed983398151915260001b6200039560201b620002141760201c565b803b15155b919050565b90565b6060620003a5846200038b565b620004025760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b606482015260840162000240565b600080856001600160a01b0316856040516200041f9190620005b0565b600060405180830381855af49150503d80600081146200045c576040519150601f19603f3d011682016040523d82523d6000602084013e62000461565b606091505b509092509050620004748282866200047e565b9695505050505050565b606083156200048f575081620002bb565b825115620004a05782518084602001fd5b8160405162461bcd60e51b8152600401620002409190620005ce565b80516001600160a01b03811681146200039057600080fd5b600080600060608486031215620004e9578283fd5b620004f484620004bc565b92506200050460208501620004bc565b60408501519092506001600160401b038082111562000521578283fd5b818601915086601f83011262000535578283fd5b8151818111156200054a576200054a62000656565b604051601f8201601f19908116603f0116810190838211818310171562000575576200057562000656565b816040528281528960208487010111156200058e578586fd5b620005a183602083016020880162000627565b80955050505050509250925092565b60008251620005c481846020870162000627565b9190910192915050565b6000602082528251806020840152620005ef81604085016020870162000627565b601f01601f19169190910160400192915050565b6000828210156200062257634e487b7160e01b81526011600452602481fd5b500390565b60005b83811015620006445781810151838201526020016200062a565b83811115620001695750506000910152565b634e487b7160e01b600052604160045260246000fd5b61085d806200067c6000396000f3fe60806040526004361061004e5760003560e01c80633659cfe6146100655780634f1ef286146100855780635c60da1b146100985780638f283970146100c9578063f851a440146100e95761005d565b3661005d5761005b6100fe565b005b61005b6100fe565b34801561007157600080fd5b5061005b6100803660046106ed565b610118565b61005b610093366004610707565b610164565b3480156100a457600080fd5b506100ad6101da565b6040516001600160a01b03909116815260200160405180910390f35b3480156100d557600080fd5b5061005b6100e43660046106ed565b610217565b3480156100f557600080fd5b506100ad610241565b6101066102a2565b610116610111610346565b610355565b565b610120610379565b6001600160a01b0316336001600160a01b0316141561015957610154816040518060200160405280600081525060006103ac565b610161565b6101616100fe565b50565b61016c610379565b6001600160a01b0316336001600160a01b031614156101cd576101c88383838080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250600192506103ac915050565b6101d5565b6101d56100fe565b505050565b60006101e4610379565b6001600160a01b0316336001600160a01b0316141561020c57610205610346565b9050610214565b6102146100fe565b90565b61021f610379565b6001600160a01b0316336001600160a01b03161415610159576101548161040b565b600061024b610379565b6001600160a01b0316336001600160a01b0316141561020c57610205610379565b606061029183836040518060600160405280602781526020016108016027913961045f565b9392505050565b803b15155b919050565b6102aa610379565b6001600160a01b0316336001600160a01b031614156103415760405162461bcd60e51b815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267606482015261195d60f21b608482015260a4015b60405180910390fd5b610116565b600061035061053a565b905090565b3660008037600080366000845af43d6000803e808015610374573d6000f35b3d6000fd5b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b546001600160a01b0316905090565b6103b583610562565b6040516001600160a01b038416907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a26000825111806103f65750805b156101d557610405838361026c565b50505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f610434610379565b604080516001600160a01b03928316815291841660208301520160405180910390a161016181610611565b606061046a84610298565b6104c55760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b6064820152608401610338565b600080856001600160a01b0316856040516104e09190610785565b600060405180830381855af49150503d806000811461051b576040519150601f19603f3d011682016040523d82523d6000602084013e610520565b606091505b509150915061053082828661069d565b9695505050505050565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc61039d565b61056b81610298565b6105cd5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401610338565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5b80546001600160a01b0319166001600160a01b039290921691909117905550565b6001600160a01b0381166106765760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b6064820152608401610338565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61036105f0565b606083156106ac575081610291565b8251156106bc5782518084602001fd5b8160405162461bcd60e51b815260040161033891906107a1565b80356001600160a01b038116811461029d57600080fd5b6000602082840312156106fe578081fd5b610291826106d6565b60008060006040848603121561071b578182fd5b610724846106d6565b9250602084013567ffffffffffffffff80821115610740578384fd5b818601915086601f830112610753578384fd5b813581811115610761578485fd5b876020828501011115610772578485fd5b6020830194508093505050509250925092565b600082516107978184602087016107d4565b9190910192915050565b60006020825282518060208401526107c08160408501602087016107d4565b601f01601f19169190910160400192915050565b60005b838110156107ef5781810151838201526020016107d7565b83811115610405575050600091015256fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122016ea36e15be10f9560025e0ec9401e2e9110cb5ec41d110b4a0e391838c1f19b64736f6c63430008020033b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c65640000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa3000000000000000000000000e7f1725e7734ce288f8367e1bb143e90bb3f051200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000"
+
+ contract_address = insert(:contract_address, contract_code: bytecode)
+
+ :transaction
+ |> insert(
+ created_contract_address_hash: contract_address.hash,
+ input: contract_creation_code
+ )
+ |> with_block(status: :ok)
+
+ params = %{
+ "contract_source_code" => contract_source_code,
+ "compiler_version" => "v0.8.2+commit.661d1103",
+ "evm_version" => "default",
+ "name" => "TransparentUpgradeableProxy",
+ "optimization" => true,
+ "optimization_runs" => 200,
+ "autodetect_constructor_args" => true
+ }
+
+ assert {:ok,
+ %{
+ abi: abi,
+ constructor_arguments:
+ "0000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa3000000000000000000000000e7f1725e7734ce288f8367e1bb143e90bb3f051200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000"
+ }} = Verifier.evaluate_authenticity(contract_address.hash, params)
+
+ assert abi != nil
+ end
+
+ test "issue 5127" do
+ contract_source_code =
+ "#{File.cwd!()}/test/support/fixture/smart_contract/issue_5127.sol"
+ |> File.read!()
+
+ bytecode =
+ "0x60806040523480156200001157600080fd5b5060405162001bfc38038062001bfc8339810160408190526200003491620002d6565b6040805180820182526009808252682ca2a9902a37b5b2b760b91b6020808401918252845180860190955260038086526259455360e81b91860191909152600080546001600160a01b03808c16610100026001600160a81b03199092169190911790915560018054828b166001600160a01b03199182161790915560028054928a169290911691909117905585905582519293926012928992899289928992620000de9262000213565b508551620000f490600a90602089019062000213565b5050600b805460ff90951660ff199095169490941790935550620001209350339250889150506200012b565b505050505062000398565b6001600160a01b038216620001865760405162461bcd60e51b815260206004820152601f60248201527f4b415032303a206d696e7420746f20746865207a65726f206164647265737300604482015260640160405180910390fd5b80600860008282546200019a919062000334565b90915550506001600160a01b03821660009081526006602052604081208054839290620001c990849062000334565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b82805462000221906200035b565b90600052602060002090601f01602090048101928262000245576000855562000290565b82601f106200026057805160ff191683800117855562000290565b8280016001018555821562000290579182015b828111156200029057825182559160200191906001019062000273565b506200029e929150620002a2565b5090565b5b808211156200029e5760008155600101620002a3565b80516001600160a01b0381168114620002d157600080fd5b919050565b600080600080600060a08688031215620002ef57600080fd5b855194506200030160208701620002b9565b93506200031160408701620002b9565b92506200032160608701620002b9565b9150608086015190509295509295909350565b600082198211156200035657634e487b7160e01b600052601160045260246000fd5b500190565b600181811c908216806200037057607f821691505b602082108114156200039257634e487b7160e01b600052602260045260246000fd5b50919050565b61185480620003a86000396000f3fe608060405234801561001057600080fd5b50600436106101e55760003560e01c80638456cb591161010f578063bddae40e116100a2578063dd62ed3e11610071578063dd62ed3e14610420578063de9c2a6b14610459578063f71c559c14610466578063f9f92be41461047957600080fd5b8063bddae40e146103cf578063c2f92192146103e2578063d864e740146103f5578063da72c1e81461040d57600080fd5b80639cfe42da116100de5780639cfe42da1461038e578063a1d5ec4b146103a1578063a457c2d7146103a9578063a9059cbb146103bc57600080fd5b80638456cb59146103405780638e39103c1461034857806390d6b45f1461035b57806395d89b411461038657600080fd5b8063394b652b1161018757806359e026f71161015657806359e026f7146102e65780635c975abb146102f9578063704b6c021461030457806370a082311461031757600080fd5b8063394b652b146102a357806339509351146102b85780633f4ba83a146102cb578063483a83df146102d357600080fd5b806318160ddd116101c357806318160ddd146102515780631ae878d31461026857806323b872dd14610271578063313ce5671461028457600080fd5b806306fdde03146101ea578063095ea7b3146102085780631714d7f31461022b575b600080fd5b6101f261049c565b6040516101ff9190611574565b60405180910390f35b61021b6102163660046115aa565b61052a565b60405190151581526020016101ff565b6101f2604051806040016040528060078152602001667975656d6d616960c81b81525081565b61025a60085481565b6040519081526020016101ff565b61025a60035481565b61021b61027f3660046115d4565b610540565b600b546102919060ff1681565b60405160ff90911681526020016101ff565b6102b66102b1366004611610565b610651565b005b61021b6102c63660046115aa565b61068c565b6102b66106c8565b6102b66102e1366004611629565b610701565b61021b6102f43660046115d4565b610739565b60005460ff1661021b565b6102b6610312366004611629565b61094f565b61025a610325366004611629565b6001600160a01b031660009081526006602052604090205490565b6102b6610a1b565b6102b6610356366004611629565b610a52565b60025461036e906001600160a01b031681565b6040516001600160a01b0390911681526020016101ff565b6101f2610a8a565b6102b661039c366004611629565b610a97565b6102b6610acf565b61021b6103b73660046115aa565b610b06565b61021b6103ca3660046115aa565b610b95565b6102b66103dd366004611629565b610bf4565b60015461036e906001600160a01b031681565b60005461036e9061010090046001600160a01b031681565b61021b61041b3660046115d4565b610c2c565b61025a61042e366004611644565b6001600160a01b03918216600090815260076020908152604080832093909416825291909152205490565b60045461021b9060ff1681565b61021b6104743660046115d4565b610ddb565b61021b610487366004611629565b60056020526000908152604090205460ff1681565b600980546104a990611677565b80601f01602080910402602001604051908101604052809291908181526020018280546104d590611677565b80156105225780601f106104f757610100808354040283529160200191610522565b820191906000526020600020905b81548152906001019060200180831161050557829003601f168201915b505050505081565b6000610537338484610ede565b50600192915050565b6000805460ff161561056d5760405162461bcd60e51b8152600401610564906116b2565b60405180910390fd5b6001600160a01b038416600090815260056020526040902054849060ff16156105a85760405162461bcd60e51b8152600401610564906116cd565b6105b3858585611002565b6001600160a01b0385166000908152600760209081526040808320338452909152902054838110156106385760405162461bcd60e51b815260206004820152602860248201527f4b415032303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b6064820152608401610564565b6106458633868403610ede565b50600195945050505050565b60005461010090046001600160a01b031633146106805760405162461bcd60e51b815260040161056490611704565b610689816111d1565b50565b3360008181526007602090815260408083206001600160a01b038716845290915281205490916105379185906106c3908690611751565b610ede565b60005461010090046001600160a01b031633146106f75760405162461bcd60e51b815260040161056490611704565b6106ff611217565b565b60005461010090046001600160a01b031633146107305760405162461bcd60e51b815260040161056490611704565b61068981611298565b6000805460ff161561075d5760405162461bcd60e51b8152600401610564906116b2565b60015460408051808201825260078152667975656d6d616960c81b602082015290516302b98ccf60e41b81526001600160a01b0390921691632b98ccf0916107aa91339190600401611769565b602060405180830381865afa1580156107c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107eb9190611795565b6108075760405162461bcd60e51b8152600401610564906117b7565b6003546002546040516306f19a8d60e21b81526001600160a01b03878116600483015290911690631bc66a3490602401602060405180830381865afa158015610854573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061087891906117ee565b101580156108f657506003546002546040516306f19a8d60e21b81526001600160a01b03868116600483015290911690631bc66a34906024015b602060405180830381865afa1580156108cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108f391906117ee565b10155b61093a5760405162461bcd60e51b81526020600482015260156024820152744f6e6c7920696e7465726e616c20707572706f736560581b6044820152606401610564565b610945848484611002565b5060019392505050565b60015460408051808201825260078152667975656d6d616960c81b602082015290516302b98ccf60e41b81526001600160a01b0390921691632b98ccf09161099c91339190600401611769565b602060405180830381865afa1580156109b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109dd9190611795565b6109f95760405162461bcd60e51b8152600401610564906117b7565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b60005461010090046001600160a01b03163314610a4a5760405162461bcd60e51b815260040161056490611704565b6106ff6112f2565b60005461010090046001600160a01b03163314610a815760405162461bcd60e51b815260040161056490611704565b6106898161134a565b600a80546104a990611677565b60005461010090046001600160a01b03163314610ac65760405162461bcd60e51b815260040161056490611704565b61068981611400565b60005461010090046001600160a01b03163314610afe5760405162461bcd60e51b815260040161056490611704565b6106ff61148a565b3360009081526007602090815260408083206001600160a01b038616845290915281205482811015610b885760405162461bcd60e51b815260206004820152602560248201527f4b415032303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610564565b6109453385858403610ede565b6000805460ff1615610bb95760405162461bcd60e51b8152600401610564906116b2565b3360008181526005602052604090205460ff1615610be95760405162461bcd60e51b8152600401610564906116cd565b610945338585611002565b60005461010090046001600160a01b03163314610c235760405162461bcd60e51b815260040161056490611704565b610689816114c2565b6000805461010090046001600160a01b03163314610c5c5760405162461bcd60e51b815260040161056490611704565b6001600160a01b038416600090815260066020526040902054821115610cd25760405162461bcd60e51b815260206004820152602560248201527f4b415032303a207472616e7366657220616d6f756e74206578636565642062616044820152646c616e636560d81b6064820152608401610564565b6001600160a01b038316610d285760405162461bcd60e51b815260206004820152601f60248201527f4b415032303a207472616e7366657220746f207a65726f2061646472657373006044820152606401610564565b6001600160a01b03841660009081526006602052604081208054849290610d50908490611807565b90915550506001600160a01b03831660009081526006602052604081208054849290610d7d908490611751565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610dc991815260200190565b60405180910390a35060019392505050565b6000805460ff1615610dff5760405162461bcd60e51b8152600401610564906116b2565b60015460408051808201825260078152667975656d6d616960c81b602082015290516302b98ccf60e41b81526001600160a01b0390921691632b98ccf091610e4c91339190600401611769565b602060405180830381865afa158015610e69573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e8d9190611795565b610ea95760405162461bcd60e51b8152600401610564906117b7565b6003546002546040516306f19a8d60e21b81526001600160a01b03878116600483015290911690631bc66a34906024016108b2565b6001600160a01b038316610f405760405162461bcd60e51b8152602060048201526024808201527f4b415032303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610564565b6001600160a01b038216610fa15760405162461bcd60e51b815260206004820152602260248201527f4b415032303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610564565b6001600160a01b0383811660008181526007602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b6001600160a01b0383166110665760405162461bcd60e51b815260206004820152602560248201527f4b415032303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610564565b6001600160a01b0382166110c85760405162461bcd60e51b815260206004820152602360248201527f4b415032303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610564565b6001600160a01b038316600090815260066020526040902054818110156111405760405162461bcd60e51b815260206004820152602660248201527f4b415032303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610564565b6001600160a01b03808516600090815260066020526040808220858503905591851681529081208054849290611177908490611751565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516111c391815260200190565b60405180910390a350505050565b600380549082905560408051828152602081018490527f4bb7c3aa2e207c70c9f2b8b0d81e076d62b704e041cdedb61959edd1814912f491015b60405180910390a15050565b60005460ff1661124e5760405162461bcd60e51b815260206004820152600260248201526104e560f41b6044820152606401610564565b6000805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b600280546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f192570e0851c3af9ff6a477c94534e97444b3893085cf6ac37fb7e7ec335f01e910161120b565b60005460ff16156113155760405162461bcd60e51b8152600401610564906116b2565b6000805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25861127b3390565b6001600160a01b038116600090815260056020526040902054819060ff166113b45760405162461bcd60e51b815260206004820152601b60248201527f41646472657373206973206e6f7420696e20626c61636b6c69737400000000006044820152606401610564565b6001600160a01b038216600081815260056020526040808220805460ff19169055513392917f6690dc53a3b1d37db94233f7c004408862ea909761dc5760b0e925276754f87591a35050565b6001600160a01b038116600090815260056020526040902054819060ff161561143b5760405162461bcd60e51b8152600401610564906116cd565b6001600160a01b038216600081815260056020526040808220805460ff19166001179055513392917fef674dcdab521405fef2bf4b5d2c6a6434e3ab02bc5a94fb89dd035704b83b0991a35050565b6004805460ff191660011790556040517fa5881517cf4ae3e7f6bcd00c68314e59f3ce78b5606d1b08253addc3c957e43b90600090a1565b60008054610100600160a81b0319166101006001600160a01b0384811682810293909317938490556040805193845291909304909216602082015282917f129aa2e3e7b369511a5c100a66e80b6c6231b4e60460799e7c1ed36e14121568910161120b565b6000815180845260005b8181101561154d57602081850181015186830182015201611531565b8181111561155f576000602083870101525b50601f01601f19169290920160200192915050565b6020815260006115876020830184611527565b9392505050565b80356001600160a01b03811681146115a557600080fd5b919050565b600080604083850312156115bd57600080fd5b6115c68361158e565b946020939093013593505050565b6000806000606084860312156115e957600080fd5b6115f28461158e565b92506116006020850161158e565b9150604084013590509250925092565b60006020828403121561162257600080fd5b5035919050565b60006020828403121561163b57600080fd5b6115878261158e565b6000806040838503121561165757600080fd5b6116608361158e565b915061166e6020840161158e565b90509250929050565b600181811c9082168061168b57607f821691505b602082108114156116ac57634e487b7160e01b600052602260045260246000fd5b50919050565b6020808252600190820152600560fc1b604082015260600190565b60208082526017908201527f4164647265737320697320696e20626c61636b6c697374000000000000000000604082015260600190565b60208082526019908201527f52657374726963746564206f6e6c7920636f6d6d697474656500000000000000604082015260600190565b634e487b7160e01b600052601160045260246000fd5b600082198211156117645761176461173b565b500190565b6001600160a01b038316815260406020820181905260009061178d90830184611527565b949350505050565b6000602082840312156117a757600080fd5b8151801515811461158757600080fd5b6020808252601b908201527f52657374726963746564206f6e6c792073757065722061646d696e0000000000604082015260600190565b60006020828403121561180057600080fd5b5051919050565b6000828210156118195761181961173b565b50039056fea2646970667358221220f9aa231f0e2c136b376cc26c179eea8f7aae62c23797e6e17b4ca4462f9ad1af64736f6c634300080b0033"
+
+ contract_creation_code =
+ "0x60806040523480156200001157600080fd5b5060405162001bfc38038062001bfc8339810160408190526200003491620002d6565b6040805180820182526009808252682ca2a9902a37b5b2b760b91b6020808401918252845180860190955260038086526259455360e81b91860191909152600080546001600160a01b03808c16610100026001600160a81b03199092169190911790915560018054828b166001600160a01b03199182161790915560028054928a169290911691909117905585905582519293926012928992899289928992620000de9262000213565b508551620000f490600a90602089019062000213565b5050600b805460ff90951660ff199095169490941790935550620001209350339250889150506200012b565b505050505062000398565b6001600160a01b038216620001865760405162461bcd60e51b815260206004820152601f60248201527f4b415032303a206d696e7420746f20746865207a65726f206164647265737300604482015260640160405180910390fd5b80600860008282546200019a919062000334565b90915550506001600160a01b03821660009081526006602052604081208054839290620001c990849062000334565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b82805462000221906200035b565b90600052602060002090601f01602090048101928262000245576000855562000290565b82601f106200026057805160ff191683800117855562000290565b8280016001018555821562000290579182015b828111156200029057825182559160200191906001019062000273565b506200029e929150620002a2565b5090565b5b808211156200029e5760008155600101620002a3565b80516001600160a01b0381168114620002d157600080fd5b919050565b600080600080600060a08688031215620002ef57600080fd5b855194506200030160208701620002b9565b93506200031160408701620002b9565b92506200032160608701620002b9565b9150608086015190509295509295909350565b600082198211156200035657634e487b7160e01b600052601160045260246000fd5b500190565b600181811c908216806200037057607f821691505b602082108114156200039257634e487b7160e01b600052602260045260246000fd5b50919050565b61185480620003a86000396000f3fe608060405234801561001057600080fd5b50600436106101e55760003560e01c80638456cb591161010f578063bddae40e116100a2578063dd62ed3e11610071578063dd62ed3e14610420578063de9c2a6b14610459578063f71c559c14610466578063f9f92be41461047957600080fd5b8063bddae40e146103cf578063c2f92192146103e2578063d864e740146103f5578063da72c1e81461040d57600080fd5b80639cfe42da116100de5780639cfe42da1461038e578063a1d5ec4b146103a1578063a457c2d7146103a9578063a9059cbb146103bc57600080fd5b80638456cb59146103405780638e39103c1461034857806390d6b45f1461035b57806395d89b411461038657600080fd5b8063394b652b1161018757806359e026f71161015657806359e026f7146102e65780635c975abb146102f9578063704b6c021461030457806370a082311461031757600080fd5b8063394b652b146102a357806339509351146102b85780633f4ba83a146102cb578063483a83df146102d357600080fd5b806318160ddd116101c357806318160ddd146102515780631ae878d31461026857806323b872dd14610271578063313ce5671461028457600080fd5b806306fdde03146101ea578063095ea7b3146102085780631714d7f31461022b575b600080fd5b6101f261049c565b6040516101ff9190611574565b60405180910390f35b61021b6102163660046115aa565b61052a565b60405190151581526020016101ff565b6101f2604051806040016040528060078152602001667975656d6d616960c81b81525081565b61025a60085481565b6040519081526020016101ff565b61025a60035481565b61021b61027f3660046115d4565b610540565b600b546102919060ff1681565b60405160ff90911681526020016101ff565b6102b66102b1366004611610565b610651565b005b61021b6102c63660046115aa565b61068c565b6102b66106c8565b6102b66102e1366004611629565b610701565b61021b6102f43660046115d4565b610739565b60005460ff1661021b565b6102b6610312366004611629565b61094f565b61025a610325366004611629565b6001600160a01b031660009081526006602052604090205490565b6102b6610a1b565b6102b6610356366004611629565b610a52565b60025461036e906001600160a01b031681565b6040516001600160a01b0390911681526020016101ff565b6101f2610a8a565b6102b661039c366004611629565b610a97565b6102b6610acf565b61021b6103b73660046115aa565b610b06565b61021b6103ca3660046115aa565b610b95565b6102b66103dd366004611629565b610bf4565b60015461036e906001600160a01b031681565b60005461036e9061010090046001600160a01b031681565b61021b61041b3660046115d4565b610c2c565b61025a61042e366004611644565b6001600160a01b03918216600090815260076020908152604080832093909416825291909152205490565b60045461021b9060ff1681565b61021b6104743660046115d4565b610ddb565b61021b610487366004611629565b60056020526000908152604090205460ff1681565b600980546104a990611677565b80601f01602080910402602001604051908101604052809291908181526020018280546104d590611677565b80156105225780601f106104f757610100808354040283529160200191610522565b820191906000526020600020905b81548152906001019060200180831161050557829003601f168201915b505050505081565b6000610537338484610ede565b50600192915050565b6000805460ff161561056d5760405162461bcd60e51b8152600401610564906116b2565b60405180910390fd5b6001600160a01b038416600090815260056020526040902054849060ff16156105a85760405162461bcd60e51b8152600401610564906116cd565b6105b3858585611002565b6001600160a01b0385166000908152600760209081526040808320338452909152902054838110156106385760405162461bcd60e51b815260206004820152602860248201527f4b415032303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b6064820152608401610564565b6106458633868403610ede565b50600195945050505050565b60005461010090046001600160a01b031633146106805760405162461bcd60e51b815260040161056490611704565b610689816111d1565b50565b3360008181526007602090815260408083206001600160a01b038716845290915281205490916105379185906106c3908690611751565b610ede565b60005461010090046001600160a01b031633146106f75760405162461bcd60e51b815260040161056490611704565b6106ff611217565b565b60005461010090046001600160a01b031633146107305760405162461bcd60e51b815260040161056490611704565b61068981611298565b6000805460ff161561075d5760405162461bcd60e51b8152600401610564906116b2565b60015460408051808201825260078152667975656d6d616960c81b602082015290516302b98ccf60e41b81526001600160a01b0390921691632b98ccf0916107aa91339190600401611769565b602060405180830381865afa1580156107c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107eb9190611795565b6108075760405162461bcd60e51b8152600401610564906117b7565b6003546002546040516306f19a8d60e21b81526001600160a01b03878116600483015290911690631bc66a3490602401602060405180830381865afa158015610854573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061087891906117ee565b101580156108f657506003546002546040516306f19a8d60e21b81526001600160a01b03868116600483015290911690631bc66a34906024015b602060405180830381865afa1580156108cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108f391906117ee565b10155b61093a5760405162461bcd60e51b81526020600482015260156024820152744f6e6c7920696e7465726e616c20707572706f736560581b6044820152606401610564565b610945848484611002565b5060019392505050565b60015460408051808201825260078152667975656d6d616960c81b602082015290516302b98ccf60e41b81526001600160a01b0390921691632b98ccf09161099c91339190600401611769565b602060405180830381865afa1580156109b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109dd9190611795565b6109f95760405162461bcd60e51b8152600401610564906117b7565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b60005461010090046001600160a01b03163314610a4a5760405162461bcd60e51b815260040161056490611704565b6106ff6112f2565b60005461010090046001600160a01b03163314610a815760405162461bcd60e51b815260040161056490611704565b6106898161134a565b600a80546104a990611677565b60005461010090046001600160a01b03163314610ac65760405162461bcd60e51b815260040161056490611704565b61068981611400565b60005461010090046001600160a01b03163314610afe5760405162461bcd60e51b815260040161056490611704565b6106ff61148a565b3360009081526007602090815260408083206001600160a01b038616845290915281205482811015610b885760405162461bcd60e51b815260206004820152602560248201527f4b415032303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610564565b6109453385858403610ede565b6000805460ff1615610bb95760405162461bcd60e51b8152600401610564906116b2565b3360008181526005602052604090205460ff1615610be95760405162461bcd60e51b8152600401610564906116cd565b610945338585611002565b60005461010090046001600160a01b03163314610c235760405162461bcd60e51b815260040161056490611704565b610689816114c2565b6000805461010090046001600160a01b03163314610c5c5760405162461bcd60e51b815260040161056490611704565b6001600160a01b038416600090815260066020526040902054821115610cd25760405162461bcd60e51b815260206004820152602560248201527f4b415032303a207472616e7366657220616d6f756e74206578636565642062616044820152646c616e636560d81b6064820152608401610564565b6001600160a01b038316610d285760405162461bcd60e51b815260206004820152601f60248201527f4b415032303a207472616e7366657220746f207a65726f2061646472657373006044820152606401610564565b6001600160a01b03841660009081526006602052604081208054849290610d50908490611807565b90915550506001600160a01b03831660009081526006602052604081208054849290610d7d908490611751565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610dc991815260200190565b60405180910390a35060019392505050565b6000805460ff1615610dff5760405162461bcd60e51b8152600401610564906116b2565b60015460408051808201825260078152667975656d6d616960c81b602082015290516302b98ccf60e41b81526001600160a01b0390921691632b98ccf091610e4c91339190600401611769565b602060405180830381865afa158015610e69573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e8d9190611795565b610ea95760405162461bcd60e51b8152600401610564906117b7565b6003546002546040516306f19a8d60e21b81526001600160a01b03878116600483015290911690631bc66a34906024016108b2565b6001600160a01b038316610f405760405162461bcd60e51b8152602060048201526024808201527f4b415032303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610564565b6001600160a01b038216610fa15760405162461bcd60e51b815260206004820152602260248201527f4b415032303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610564565b6001600160a01b0383811660008181526007602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b6001600160a01b0383166110665760405162461bcd60e51b815260206004820152602560248201527f4b415032303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610564565b6001600160a01b0382166110c85760405162461bcd60e51b815260206004820152602360248201527f4b415032303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610564565b6001600160a01b038316600090815260066020526040902054818110156111405760405162461bcd60e51b815260206004820152602660248201527f4b415032303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610564565b6001600160a01b03808516600090815260066020526040808220858503905591851681529081208054849290611177908490611751565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516111c391815260200190565b60405180910390a350505050565b600380549082905560408051828152602081018490527f4bb7c3aa2e207c70c9f2b8b0d81e076d62b704e041cdedb61959edd1814912f491015b60405180910390a15050565b60005460ff1661124e5760405162461bcd60e51b815260206004820152600260248201526104e560f41b6044820152606401610564565b6000805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b600280546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f192570e0851c3af9ff6a477c94534e97444b3893085cf6ac37fb7e7ec335f01e910161120b565b60005460ff16156113155760405162461bcd60e51b8152600401610564906116b2565b6000805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25861127b3390565b6001600160a01b038116600090815260056020526040902054819060ff166113b45760405162461bcd60e51b815260206004820152601b60248201527f41646472657373206973206e6f7420696e20626c61636b6c69737400000000006044820152606401610564565b6001600160a01b038216600081815260056020526040808220805460ff19169055513392917f6690dc53a3b1d37db94233f7c004408862ea909761dc5760b0e925276754f87591a35050565b6001600160a01b038116600090815260056020526040902054819060ff161561143b5760405162461bcd60e51b8152600401610564906116cd565b6001600160a01b038216600081815260056020526040808220805460ff19166001179055513392917fef674dcdab521405fef2bf4b5d2c6a6434e3ab02bc5a94fb89dd035704b83b0991a35050565b6004805460ff191660011790556040517fa5881517cf4ae3e7f6bcd00c68314e59f3ce78b5606d1b08253addc3c957e43b90600090a1565b60008054610100600160a81b0319166101006001600160a01b0384811682810293909317938490556040805193845291909304909216602082015282917f129aa2e3e7b369511a5c100a66e80b6c6231b4e60460799e7c1ed36e14121568910161120b565b6000815180845260005b8181101561154d57602081850181015186830182015201611531565b8181111561155f576000602083870101525b50601f01601f19169290920160200192915050565b6020815260006115876020830184611527565b9392505050565b80356001600160a01b03811681146115a557600080fd5b919050565b600080604083850312156115bd57600080fd5b6115c68361158e565b946020939093013593505050565b6000806000606084860312156115e957600080fd5b6115f28461158e565b92506116006020850161158e565b9150604084013590509250925092565b60006020828403121561162257600080fd5b5035919050565b60006020828403121561163b57600080fd5b6115878261158e565b6000806040838503121561165757600080fd5b6116608361158e565b915061166e6020840161158e565b90509250929050565b600181811c9082168061168b57607f821691505b602082108114156116ac57634e487b7160e01b600052602260045260246000fd5b50919050565b6020808252600190820152600560fc1b604082015260600190565b60208082526017908201527f4164647265737320697320696e20626c61636b6c697374000000000000000000604082015260600190565b60208082526019908201527f52657374726963746564206f6e6c7920636f6d6d697474656500000000000000604082015260600190565b634e487b7160e01b600052601160045260246000fd5b600082198211156117645761176461173b565b500190565b6001600160a01b038316815260406020820181905260009061178d90830184611527565b949350505050565b6000602082840312156117a757600080fd5b8151801515811461158757600080fd5b6020808252601b908201527f52657374726963746564206f6e6c792073757065722061646d696e0000000000604082015260600190565b60006020828403121561180057600080fd5b5051919050565b6000828210156118195761181961173b565b50039056fea2646970667358221220f9aa231f0e2c136b376cc26c179eea8f7aae62c23797e6e17b4ca4462f9ad1af64736f6c634300080b0033000000000000000000000000000000000000000000084595161401484a00000000000000000000000000000073d8f731ec0d3945d807a904bf93954b82b0d594000000000000000000000000c5333c0d3cf6fc8f84f3ccb0d5a73dbda2eceb500000000000000000000000002c8abd9c61d4e973ca8db5545c54c90e44a2445c0000000000000000000000000000000000000000000000000000000000000004"
+
+ contract_address = insert(:contract_address, contract_code: bytecode)
+
+ :transaction
+ |> insert(
+ created_contract_address_hash: contract_address.hash,
+ input: contract_creation_code
+ )
+ |> with_block(status: :ok)
+
+ params = %{
+ "contract_source_code" => contract_source_code,
+ "compiler_version" => "v0.8.11+commit.d7f03943",
+ "evm_version" => "default",
+ "name" => "YESToken",
+ "optimization" => true,
+ "optimization_runs" => 200,
+ "autodetect_constructor_args" => true
+ }
+
+ assert {:ok,
+ %{
+ abi: abi,
+ constructor_arguments:
+ "000000000000000000000000000000000000000000084595161401484a00000000000000000000000000000073d8f731ec0d3945d807a904bf93954b82b0d594000000000000000000000000c5333c0d3cf6fc8f84f3ccb0d5a73dbda2eceb500000000000000000000000002c8abd9c61d4e973ca8db5545c54c90e44a2445c0000000000000000000000000000000000000000000000000000000000000004"
+ }} = Verifier.evaluate_authenticity(contract_address.hash, params)
+
+ assert abi != nil
+ end
+
+ test "issue 3082" do
+ contract_source_code =
+ "#{File.cwd!()}/test/support/fixture/smart_contract/issue_3082.sol"
+ |> File.read!()
+
+ bytecode =
+ "0x608060405234801561001057600080fd5b50600436106101e55760003560e01c80638d3ce69d1161010f578063b2cec8ea116100a2578063cbecb28811610071578063cbecb2881461050a578063f049f33414610512578063f2fde38b14610532578063fc0c546a14610558576101e5565b8063b2cec8ea146104a2578063b99ef521146104c2578063bac883ef146104e2578063bd4b2be914610502576101e5565b8063983c44d6116100de578063983c44d6146103b75780639972444f146103bf578063a3df582a146103df578063a4c0ed36146103e7576101e5565b80638d3ce69d146103635780638d3df3461461036b5780638da5cb5b1461038b5780638f32d59b146103af576101e5565b8063392e53cd11610187578063622c5e4511610156578063622c5e451461032b578063715018a6146103335780637af7c0401461033b5780638129fc1c1461035b576101e5565b8063392e53cd146102b757806359791d6d146102bf5780635f26622f146102df578063604f21771461030b576101e5565b80630f10e06f116101c35780630f10e06f146102695780632755731e14610289578063375a4cab14610291578063381a4113146102af576101e5565b8063014a969a146101ea578063047fc9aa1461021e57806304a3922014610238575b600080fd5b61020a6004803603602081101561020057600080fd5b503560ff16610560565b604080519115158252519081900360200190f35b610226610575565b60408051918252519081900360200190f35b6102676004803603604081101561024e57600080fd5b50803560ff1690602001356001600160a01b0316610584565b005b6102266004803603602081101561027f57600080fd5b503560ff166106d0565b6102266106e2565b6102996106e8565b6040805160ff9092168252519081900360200190f35b61020a6106ed565b61020a6106f6565b610226600480360360208110156102d557600080fd5b503560ff16610704565b610267600480360360408110156102f557600080fd5b506001600160a01b038135169060200135610716565b6102266004803603602081101561032157600080fd5b503560ff16610fc4565b610299610fd6565b610267610fdb565b61020a6004803603602081101561035157600080fd5b503560ff16611073565b610267611088565b6102996111de565b6102266004803603602081101561038157600080fd5b503560ff166111e3565b6103936111f5565b604080516001600160a01b039092168252519081900360200190f35b61020a611204565b610299611215565b610393600480360360208110156103d557600080fd5b503560ff1661121a565b610226611235565b61020a600480360360608110156103fd57600080fd5b6001600160a01b038235169160208101359181019060608101604082013564010000000081111561042d57600080fd5b82018360208201111561043f57600080fd5b8035906020019184600183028401116401000000008311171561046157600080fd5b91908080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092955061123b945050505050565b610226600480360360208110156104b857600080fd5b503560ff16611274565b610226600480360360208110156104d857600080fd5b503560ff16611286565b610267600480360360208110156104f857600080fd5b503560ff16611298565b610299611652565b610299611657565b6102266004803603602081101561052857600080fd5b503560ff1661165c565b6102676004803603602081101561054857600080fd5b50356001600160a01b031661166e565b6103936116d3565b600b6020526000908152604090205460ff1681565b6a070fe2cd68c25ff4f0000081565b60ff821660011480610599575060ff82166005145b6105d7576040805162461bcd60e51b815260206004820152600a6024820152691ddc9bdb99c81c1bdbdb60b21b604482015290519081900360640190fd5b60ff82166000908152600260205260409020546001600160a01b03163314610637576040805162461bcd60e51b815260206004820152600e60248201526d1b9bdd08185d5d1a1bdc9a5e995960921b604482015290519081900360640190fd5b610640816116e2565b60ff82166000818152600260209081526040918290205482516001600160a01b0391821681529085169181019190915281517f09a67390cbf7986a6bb0fcb44307b4844ba258bca3f38599d2b344824df4ba1d929181900390910190a260ff91909116600090815260026020526040902080546001600160a01b0319166001600160a01b03909216919091179055565b60056020526000908152604090205481565b600d5481565b600581565b600e5460ff1681565b600e54610100900460ff1681565b60086020526000908152604090205481565b61071e611204565b61076f576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b600e5460ff16156107c7576040805162461bcd60e51b815260206004820152601760248201527f616c7265616479207072652d696e697469616c697a6564000000000000000000604482015290519081900360640190fd5b600180546001600160a01b0319166001600160a01b038481169190911791829055604080516370a0823160e01b81523060048201529051600093909216916370a0823191602480820192602092909190829003018186803b15801561082b57600080fd5b505afa15801561083f573d6000803e3d6000fd5b505050506040513d602081101561085557600080fd5b505190506a070fe2cd68c25ff4f0000081146108b1576040805162461bcd60e51b815260206004820152601660248201527577726f6e6720636f6e74726163742062616c616e636560501b604482015290519081900360640190fd5b6108b961172f565b600d55600e805460ff191660011790556003600090815260026020527f88601476d11616a71c5be67555bd1dff4b1cbf21533d2669b768b61518cfe1c3546040805163189acdbd60e31b81526001600160a01b0387811660048301529151919092169263c4d66de8926024808201939182900301818387803b15801561093e57600080fd5b505af1158015610952573d6000803e3d6000fd5b50506004600081815260026020527fee60d0579bcffd98e668647d59fec1ff86a7fb340ce572e844f234ae73a6918f546040805163189acdbd60e31b81526001600160a01b038a811695820195909552905193909116945063c4d66de893506024808201939182900301818387803b1580156109cd57600080fd5b505af11580156109e1573d6000803e3d6000fd5b5050600360008181526020919091527fcbc4e5fb02c3d1de23a9f1e014b4d2ee5aeaea9505df5e855c9210bf472495af54909250610a3a9150606490610a2e90601963ffffffff61173316565b9063ffffffff61179516565b600154600260009081527f679795a0195a1b76cdebb7c51d74e058aee92919b8c3389af86ef24535e8a28c54600360209081527fc3a24b0501bd2c13a7e57f2db4369ec4c223447539fc0724a9d55ac4a06ebd4d546040805163238a3fe160e01b81526001600160a01b039485166004820152602481019290925251959650919093169363238a3fe19360448084019491938390030190829087803b158015610ae257600080fd5b505af1158015610af6573d6000803e3d6000fd5b505050506040513d6020811015610b0c57600080fd5b505060015460036000908152600260209081527f88601476d11616a71c5be67555bd1dff4b1cbf21533d2669b768b61518cfe1c3546040805163a9059cbb60e01b81526001600160a01b039283166004820152602481018790529051919094169363a9059cbb9360448083019493928390030190829087803b158015610b9157600080fd5b505af1158015610ba5573d6000803e3d6000fd5b505050506040513d6020811015610bbb57600080fd5b50506006600090815260036020527fc69056f16cbaa3c616b828e333ab7d3a32310765507f8f58359e99ebb7a885f354610bfb908563ffffffff6117ff16565b60015460066000908152600260209081527f59dd4b18488d12f51eda69757a0ed42a2010c14b564330cc74a06895e60c077b546040805163238a3fe160e01b81526001600160a01b03928316600482015260248101879052905195965093169363238a3fe193604480820194918390030190829087803b158015610c7e57600080fd5b505af1158015610c92573d6000803e3d6000fd5b505050506040513d6020811015610ca857600080fd5b50508315610d35576001546040805163238a3fe160e01b81526000600482018190526024820188905291516001600160a01b039093169263238a3fe192604480840193602093929083900390910190829087803b158015610d0857600080fd5b505af1158015610d1c573d6000803e3d6000fd5b505050506040513d6020811015610d3257600080fd5b50505b60026000527fc3a24b0501bd2c13a7e57f2db4369ec4c223447539fc0724a9d55ac4a06ebd4d5460046020527f91da3fd0782e51c6b3986e9e672fd566868e71f3dbc2d6c2cd6fbb3e361af2a754610d929163ffffffff6117ff16565b60046020527f91da3fd0782e51c6b3986e9e672fd566868e71f3dbc2d6c2cd6fbb3e361af2a75560036000527f2e174c10e159ea99b867ce3205125c24a42d128804e4070ed6fcc8cc98166aa054610df0908363ffffffff6117ff16565b7f2e174c10e159ea99b867ce3205125c24a42d128804e4070ed6fcc8cc98166aa05560066000527fc69056f16cbaa3c616b828e333ab7d3a32310765507f8f58359e99ebb7a885f35460046020527fc59312466997bb42aaaf719ece141047820e6b34531e1670dc1852a453648f0f54610e6f9163ffffffff6117ff16565b6006600052600460209081527fc59312466997bb42aaaf719ece141047820e6b34531e1670dc1852a453648f0f91909155604080516001600160a01b0388168152339281019290925280517f20e0b9d27e138a83ff1b3f687932144f5e913aa93855ac36c2611ec1dfae704e9281900390910190a160026000819052600360209081527fc3a24b0501bd2c13a7e57f2db4369ec4c223447539fc0724a9d55ac4a06ebd4d546040805191825233928201929092528151600080516020611b49833981519152929181900390910190a2604080518381523360208201528151600392600080516020611b49833981519152928290030190a2604080518281523360208201528151600692600080516020611b49833981519152928290030190a28315610fbd57604080518581523360208201528151600092600080516020611b49833981519152928290030190a25b5050505050565b60036020526000908152604090205481565b600481565b610fe3611204565b611034576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6040805162461bcd60e51b815260206004820152600f60248201526e1b9bdd081a5b5c1b195b595b9d1959608a1b604482015290519081900360640190fd5b600a6020526000908152604090205460ff1681565b600e5460ff166110d5576040805162461bcd60e51b81526020600482015260136024820152721b9bdd081c1c994b5a5b9a5d1a585b1a5e9959606a1b604482015290519081900360640190fd5b600e54610100900460ff1615611128576040805162461bcd60e51b8152602060048201526013602482015272185b1c9958591e481a5b9a5d1a585b1a5e9959606a1b604482015290519081900360640190fd5b6276a700611146600d5461113a61172f565b9063ffffffff6117ff16565b101561118f57611154611204565b61118f5760405162461bcd60e51b8152600401808060200182810382526027815260200180611b226027913960400191505060405180910390fd5b61119761172f565b600c55600e805461ff0019166101001790556040805133815290517f908408e307fc569b417f6cbec5d5a06f44a0a505ac0479b47d421a4b2fd6a1e69181900360200190a1565b600381565b60066020526000908152604090205481565b6000546001600160a01b031690565b6000546001600160a01b0316331490565b600181565b6002602052600090815260409020546001600160a01b031681565b600c5481565b600060405162461bcd60e51b815260040180806020018281038252602e815260200180611bb0602e913960400191505060405180910390fd5b60096020526000908152604090205481565b60046020526000908152604090205481565b600e54610100900460ff166112e6576040805162461bcd60e51b815260206004820152600f60248201526e1b9bdd081a5b9a5d1a585b1a5e9959608a1b604482015290519081900360640190fd5b60ff8116600090815260056020526040902054600c54829161130e919063ffffffff61185c16565b61131661172f565b10158015611336575060ff8082166000908152600b602052604090205416155b6113715760405162461bcd60e51b8152600401808060200182810382526029815260200180611bde6029913960400191505060405180910390fd5b60ff821660031480611386575060ff82166004145b80611394575060ff82166001145b806113a2575060ff82166005145b6113e0576040805162461bcd60e51b815260206004820152600a6024820152691ddc9bdb99c81c1bdbdb60b21b604482015290519081900360640190fd5b60ff8083166000908152600a6020526040812054909116611426575060ff8216600090815260096020908152604080832054600a909252909120805460ff191660011790555b6000611431846118b6565b60ff85166000908152600860205260409020549091506114689061145b908363ffffffff61173316565b839063ffffffff61185c16565b9150600082116114bf576040805162461bcd60e51b815260206004820152601960248201527f6e6f20696e7374616c6c6d656e747320617661696c61626c6500000000000000604482015290519081900360640190fd5b60006114cc8584846119a0565b90506114de838263ffffffff61185c16565b925060ff8516600314806114f5575060ff85166004145b156115905760015460ff8616600090815260026020908152604080832054815163a9059cbb60e01b81526001600160a01b03918216600482015260248101899052915194169363a9059cbb93604480840194938390030190829087803b15801561155e57600080fd5b505af1158015611572573d6000803e3d6000fd5b505050506040513d602081101561158857600080fd5b506116219050565b60015460ff8616600090815260026020908152604080832054815163238a3fe160e01b81526001600160a01b03918216600482015260248101899052915194169363238a3fe193604480840194938390030190829087803b1580156115f457600080fd5b505af1158015611608573d6000803e3d6000fd5b505050506040513d602081101561161e57600080fd5b50505b60408051848152336020820152815160ff881692600080516020611b49833981519152928290030190a25050505050565b600681565b600281565b60076020526000908152604090205481565b611676611204565b6116c7576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6116d081611a5d565b50565b6001546001600160a01b031681565b6001600160a01b0381166116d0576040805162461bcd60e51b815260206004820152600f60248201526e696e76616c6964206164647265737360881b604482015290519081900360640190fd5b4290565b6000826117425750600061178f565b8282028284828161174f57fe5b041461178c5760405162461bcd60e51b8152600401808060200182810382526021815260200180611b8f6021913960400191505060405180910390fd5b90505b92915050565b60008082116117eb576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b60008284816117f657fe5b04949350505050565b600082821115611856576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b60008282018381101561178c576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b60ff811660009081526007602052604081205481906118de906201518063ffffffff61173316565b60ff8416600090815260056020526040812054600c54929350909161191a91849161190e9163ffffffff61185c16565b9063ffffffff61185c16565b905061192f62015180610a2e8361113a61172f565b60ff851660009081526006602090815260408083205460079092529091205491945090611962908563ffffffff61185c16565b11156119995760ff84166000908152600760209081526040808320546006909252909120546119969163ffffffff6117ff16565b92505b5050919050565b60ff831660009081526004602052604081205481906119c5908563ffffffff6117ff16565b60ff86166000908152600460209081526040808320939093556007905220546119f4908463ffffffff61185c16565b60ff861660009081526007602081815260408084208590556006825290922054915211611a555760ff851660009081526004602052604090205415611a4c575060ff8416600090815260046020526040812080549190555b611a5585611afd565b949350505050565b6001600160a01b038116611aa25760405162461bcd60e51b8152600401808060200182810382526026815260200180611b696026913960400191505060405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b60ff166000908152600b60205260409020805460ff19166001179055565b3b15159056fe666f72206e6f77206f6e6c79206f776e65722063616e2063616c6c2074686973206d6574686f64a8b65b82b2ff2a955e75c1bfa6a0e92aafb764156295da77a0a4c714f3895c724f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7773656e64696e6720746f6b656e7320746f207468697320636f6e7472616374206973206e6f7420616c6c6f776564696e7374616c6c6d656e747320617265206e6f742061637469766520666f72207468697320706f6f6ca265627a7a723058208fc7fe0d6f40178495c4e655627ac8a6bc837eb89686042a07161f078a2ea04464736f6c634300050a0032"
+
+ contract_creation_code =
+ "0x6080604052600e805461ffff191690553480156200001c57600080fd5b5060405162002ac338038062002ac3833981810160405260c08110156200004257600080fd5b50805160208201516040808401516060850151608086015160a090960151600080546001600160a01b031916331780825594519697959693959294929391926001600160a01b0316917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3620000cf846001600160a01b031662000aa160201b62001b1b1760201c565b8015620000f65750620000f6836001600160a01b031662000aa160201b62001b1b1760201c565b6200016257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f6e6f74206120636f6e7472616374206164647265737300000000000000000000604482015290519081900360640190fd5b62000176866001600160e01b0362000aa716565b6200018a856001600160e01b0362000aa716565b6200019e826001600160e01b0362000aa716565b620001b2816001600160e01b0362000aa716565b7fe90b7bceb6e7df5418fb78d8ee546e97c83a08bbccc01a0644d599ccd2a7c2e080546001600160a01b03199081166001600160a01b03898116919091179092557f679795a0195a1b76cdebb7c51d74e058aee92919b8c3389af86ef24535e8a28c805482168884161790557f88601476d11616a71c5be67555bd1dff4b1cbf21533d2669b768b61518cfe1c380548216878416179081905560008051602062002a42833981519152805483168785161790557fb98b78633099fa36ed8b8680c4f8092689e1e04080eb9cbb077ca38a14d7e384805483168685161790557f59dd4b18488d12f51eda69757a0ed42a2010c14b564330cc74a06895e60c077b8054909216848416179091556a034f086f3b33b684000000600080516020620029e2833981519152556954b40b1f852bda000000600080516020620029c283398151915255600360005260026020908152604080517f5fd6632000000000000000000000000000000000000000000000000000000000815290519290931692635fd663209260048083019392829003018186803b1580156200035257600080fd5b505afa15801562000367573d6000803e3d6000fd5b505050506040513d60208110156200037e57600080fd5b505160008051602062002aa383398151915255600460008190526002602090815260008051602062002a4283398151915254604080517f5fd6632000000000000000000000000000000000000000000000000000000000815290516001600160a01b0390921693635fd6632093828201939092909190829003018186803b1580156200040957600080fd5b505afa1580156200041e573d6000803e3d6000fd5b505050506040513d60208110156200043557600080fd5b50516003602090815260008051602062002a2283398151915282905569940785b073a9e904000060008051602062002a8383398151915281905569ace68dbebd988d50000060008051602062002a0283398151915281905560008051602062002aa383398151915254600080516020620029c2833981519152546001600052600080516020620029e2833981519152546a070fe2cd68c25ff4f00000966200050e969495620004fa95909486949293859391928492906200185c62000b20821b17901c565b62000b2060201b6200185c1790919060201c565b146200057b57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f77726f6e672073756d206f6620706f6f6c73207374616b657300000000000000604482015290519081900360640190fd5b600080516020620029e2833981519152547fabd6e7cb50984ff9c2f3e18a2660c3353dadf4e3291deeb275dae2cd1e44fe05819055600080516020620029c2833981519152547f91da3fd0782e51c6b3986e9e672fd566868e71f3dbc2d6c2cd6fbb3e361af2a75560008051602062002aa3833981519152547f2e174c10e159ea99b867ce3205125c24a42d128804e4070ed6fcc8cc98166aa05560008051602062002a22833981519152547f1a1e6821cde7d0159c0d293177871e09677b4e42307c7db3ba94f8648a5a050f5560008051602062002a83833981519152547f04cde762ef08b6b6c5ded8e8c4c0b3f4e5c9ad7342c88fcc93681b4588b73f055560008051602062002a02833981519152547fc59312466997bb42aaaf719ece141047820e6b34531e1670dc1852a453648f0f55600160005260036020908152620006ef91606491620006db91906014906200173362000b9e821b17901c565b62000c1660201b620017951790919060201c565b7f92e85d02570a8092d09a6e3a57665bc3815a2699a4074001bf1ccabf660f5a365560036000819052602090815260008051602062002aa3833981519152546200074d91606491620006db91600a906200173362000b9e821b17901c565b7fc575c31fea594a6eb97c8e9d3f9caee4c16218c6ef37e923234c0fe9014a61e75560046000526003602090815260008051602062002a2283398151915254620007ab91606491620006db916014906200173362000b9e821b17901c565b7f8dc18c4ccfd75f5c815b63770fa542fd953e8fef7e0e44bbdd4913470ce7e9cb5560056000526003602090815260008051602062002a83833981519152546200080991606491620006db916014906200173362000b9e821b17901c565b7f74b05292d1d4b2b48b65261b07099d24244bcb069f138d9a6bfdcf776becac4c556301baf8007f1471eb6eb2c5e789fc3de43f8ce62938c7d1836ec861730447e2ada8fd81017b556224ea007fa9bc9a3a348c357ba16b37005d7e6b3236198c0e939f4af8c5f19b8deeb8ebc055626ebe007f3eec716f11ba9e820c81ca75eb978ffb45831ef8b7a53e5e422c26008e1ca6d58190557f458b30c2d72bfd2c6317304a4594ecbafe5f729d3111b65fdc3a33bd48e5432d5560066020526101507f3e5fec24aa4dc4e5aee2e025e51e1392c72a2500577559fae9665c6d52bd6a315560e07f75f96ab15d697e93042dc45b5c896c4b27e89bb6eaf39475c5c371cb2513f7d25560fc7fc5069e24aaadb2addc3e52e868fcf3f4f8acf5a87e24300992fd4540c2a87eed81905560056000527fbfd358e93f18da3ed276c3afdbdba00b8f0b6008a03476a6a86bd6320ee6938b556200097260016001600160e01b0362000c9c16565b7fad67d757c34507f157cacfa2e3153e9f260a2244f30428821be7be64587ac55f5560036000819052602081815260008051602062002aa383398151915254620009e69291620009d791606491620006db91906023906200173362000b9e821b17901c565b6001600160e01b0362000cc516565b600360005260086020527f625b35f5e76f098dd7c3a05b10e2e5e78a4a01228d60c3b143426cdf36d264555562000a2760046001600160e01b0362000c9c16565b600460005260086020527f9321edea6e3be4df59a344b401fab4f888b556fda1f954244cff9204bad624b85562000a6860056001600160e01b0362000c9c16565b600560005260086020527f91238f30f286c9a1c6e901c4eda3b214c381c846e3dbe48df95c21488e8e1fdb555062000d77945050505050565b3b151590565b6001600160a01b03811662000b1d57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f696e76616c696420616464726573730000000000000000000000000000000000604482015290519081900360640190fd5b50565b60008282018381101562000b9557604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b90505b92915050565b60008262000baf5750600062000b98565b8282028284828162000bbd57fe5b041462000b95576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602181526020018062002a626021913960400191505060405180910390fd5b600080821162000c8757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b600082848162000c9357fe5b04949350505050565b60ff811660009081526009602052604081205462000b989083906001600160e01b0362000cc516565b60ff8216600090815260066020908152604080832054600383529083205462000b9592620006db91908690620017ff62000cff821b17901c565b60008282111562000d7157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b611c3b8062000d876000396000f3fe608060405234801561001057600080fd5b50600436106101e55760003560e01c80638d3ce69d1161010f578063b2cec8ea116100a2578063cbecb28811610071578063cbecb2881461050a578063f049f33414610512578063f2fde38b14610532578063fc0c546a14610558576101e5565b8063b2cec8ea146104a2578063b99ef521146104c2578063bac883ef146104e2578063bd4b2be914610502576101e5565b8063983c44d6116100de578063983c44d6146103b75780639972444f146103bf578063a3df582a146103df578063a4c0ed36146103e7576101e5565b80638d3ce69d146103635780638d3df3461461036b5780638da5cb5b1461038b5780638f32d59b146103af576101e5565b8063392e53cd11610187578063622c5e4511610156578063622c5e451461032b578063715018a6146103335780637af7c0401461033b5780638129fc1c1461035b576101e5565b8063392e53cd146102b757806359791d6d146102bf5780635f26622f146102df578063604f21771461030b576101e5565b80630f10e06f116101c35780630f10e06f146102695780632755731e14610289578063375a4cab14610291578063381a4113146102af576101e5565b8063014a969a146101ea578063047fc9aa1461021e57806304a3922014610238575b600080fd5b61020a6004803603602081101561020057600080fd5b503560ff16610560565b604080519115158252519081900360200190f35b610226610575565b60408051918252519081900360200190f35b6102676004803603604081101561024e57600080fd5b50803560ff1690602001356001600160a01b0316610584565b005b6102266004803603602081101561027f57600080fd5b503560ff166106d0565b6102266106e2565b6102996106e8565b6040805160ff9092168252519081900360200190f35b61020a6106ed565b61020a6106f6565b610226600480360360208110156102d557600080fd5b503560ff16610704565b610267600480360360408110156102f557600080fd5b506001600160a01b038135169060200135610716565b6102266004803603602081101561032157600080fd5b503560ff16610fc4565b610299610fd6565b610267610fdb565b61020a6004803603602081101561035157600080fd5b503560ff16611073565b610267611088565b6102996111de565b6102266004803603602081101561038157600080fd5b503560ff166111e3565b6103936111f5565b604080516001600160a01b039092168252519081900360200190f35b61020a611204565b610299611215565b610393600480360360208110156103d557600080fd5b503560ff1661121a565b610226611235565b61020a600480360360608110156103fd57600080fd5b6001600160a01b038235169160208101359181019060608101604082013564010000000081111561042d57600080fd5b82018360208201111561043f57600080fd5b8035906020019184600183028401116401000000008311171561046157600080fd5b91908080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092955061123b945050505050565b610226600480360360208110156104b857600080fd5b503560ff16611274565b610226600480360360208110156104d857600080fd5b503560ff16611286565b610267600480360360208110156104f857600080fd5b503560ff16611298565b610299611652565b610299611657565b6102266004803603602081101561052857600080fd5b503560ff1661165c565b6102676004803603602081101561054857600080fd5b50356001600160a01b031661166e565b6103936116d3565b600b6020526000908152604090205460ff1681565b6a070fe2cd68c25ff4f0000081565b60ff821660011480610599575060ff82166005145b6105d7576040805162461bcd60e51b815260206004820152600a6024820152691ddc9bdb99c81c1bdbdb60b21b604482015290519081900360640190fd5b60ff82166000908152600260205260409020546001600160a01b03163314610637576040805162461bcd60e51b815260206004820152600e60248201526d1b9bdd08185d5d1a1bdc9a5e995960921b604482015290519081900360640190fd5b610640816116e2565b60ff82166000818152600260209081526040918290205482516001600160a01b0391821681529085169181019190915281517f09a67390cbf7986a6bb0fcb44307b4844ba258bca3f38599d2b344824df4ba1d929181900390910190a260ff91909116600090815260026020526040902080546001600160a01b0319166001600160a01b03909216919091179055565b60056020526000908152604090205481565b600d5481565b600581565b600e5460ff1681565b600e54610100900460ff1681565b60086020526000908152604090205481565b61071e611204565b61076f576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b600e5460ff16156107c7576040805162461bcd60e51b815260206004820152601760248201527f616c7265616479207072652d696e697469616c697a6564000000000000000000604482015290519081900360640190fd5b600180546001600160a01b0319166001600160a01b038481169190911791829055604080516370a0823160e01b81523060048201529051600093909216916370a0823191602480820192602092909190829003018186803b15801561082b57600080fd5b505afa15801561083f573d6000803e3d6000fd5b505050506040513d602081101561085557600080fd5b505190506a070fe2cd68c25ff4f0000081146108b1576040805162461bcd60e51b815260206004820152601660248201527577726f6e6720636f6e74726163742062616c616e636560501b604482015290519081900360640190fd5b6108b961172f565b600d55600e805460ff191660011790556003600090815260026020527f88601476d11616a71c5be67555bd1dff4b1cbf21533d2669b768b61518cfe1c3546040805163189acdbd60e31b81526001600160a01b0387811660048301529151919092169263c4d66de8926024808201939182900301818387803b15801561093e57600080fd5b505af1158015610952573d6000803e3d6000fd5b50506004600081815260026020527fee60d0579bcffd98e668647d59fec1ff86a7fb340ce572e844f234ae73a6918f546040805163189acdbd60e31b81526001600160a01b038a811695820195909552905193909116945063c4d66de893506024808201939182900301818387803b1580156109cd57600080fd5b505af11580156109e1573d6000803e3d6000fd5b5050600360008181526020919091527fcbc4e5fb02c3d1de23a9f1e014b4d2ee5aeaea9505df5e855c9210bf472495af54909250610a3a9150606490610a2e90601963ffffffff61173316565b9063ffffffff61179516565b600154600260009081527f679795a0195a1b76cdebb7c51d74e058aee92919b8c3389af86ef24535e8a28c54600360209081527fc3a24b0501bd2c13a7e57f2db4369ec4c223447539fc0724a9d55ac4a06ebd4d546040805163238a3fe160e01b81526001600160a01b039485166004820152602481019290925251959650919093169363238a3fe19360448084019491938390030190829087803b158015610ae257600080fd5b505af1158015610af6573d6000803e3d6000fd5b505050506040513d6020811015610b0c57600080fd5b505060015460036000908152600260209081527f88601476d11616a71c5be67555bd1dff4b1cbf21533d2669b768b61518cfe1c3546040805163a9059cbb60e01b81526001600160a01b039283166004820152602481018790529051919094169363a9059cbb9360448083019493928390030190829087803b158015610b9157600080fd5b505af1158015610ba5573d6000803e3d6000fd5b505050506040513d6020811015610bbb57600080fd5b50506006600090815260036020527fc69056f16cbaa3c616b828e333ab7d3a32310765507f8f58359e99ebb7a885f354610bfb908563ffffffff6117ff16565b60015460066000908152600260209081527f59dd4b18488d12f51eda69757a0ed42a2010c14b564330cc74a06895e60c077b546040805163238a3fe160e01b81526001600160a01b03928316600482015260248101879052905195965093169363238a3fe193604480820194918390030190829087803b158015610c7e57600080fd5b505af1158015610c92573d6000803e3d6000fd5b505050506040513d6020811015610ca857600080fd5b50508315610d35576001546040805163238a3fe160e01b81526000600482018190526024820188905291516001600160a01b039093169263238a3fe192604480840193602093929083900390910190829087803b158015610d0857600080fd5b505af1158015610d1c573d6000803e3d6000fd5b505050506040513d6020811015610d3257600080fd5b50505b60026000527fc3a24b0501bd2c13a7e57f2db4369ec4c223447539fc0724a9d55ac4a06ebd4d5460046020527f91da3fd0782e51c6b3986e9e672fd566868e71f3dbc2d6c2cd6fbb3e361af2a754610d929163ffffffff6117ff16565b60046020527f91da3fd0782e51c6b3986e9e672fd566868e71f3dbc2d6c2cd6fbb3e361af2a75560036000527f2e174c10e159ea99b867ce3205125c24a42d128804e4070ed6fcc8cc98166aa054610df0908363ffffffff6117ff16565b7f2e174c10e159ea99b867ce3205125c24a42d128804e4070ed6fcc8cc98166aa05560066000527fc69056f16cbaa3c616b828e333ab7d3a32310765507f8f58359e99ebb7a885f35460046020527fc59312466997bb42aaaf719ece141047820e6b34531e1670dc1852a453648f0f54610e6f9163ffffffff6117ff16565b6006600052600460209081527fc59312466997bb42aaaf719ece141047820e6b34531e1670dc1852a453648f0f91909155604080516001600160a01b0388168152339281019290925280517f20e0b9d27e138a83ff1b3f687932144f5e913aa93855ac36c2611ec1dfae704e9281900390910190a160026000819052600360209081527fc3a24b0501bd2c13a7e57f2db4369ec4c223447539fc0724a9d55ac4a06ebd4d546040805191825233928201929092528151600080516020611b49833981519152929181900390910190a2604080518381523360208201528151600392600080516020611b49833981519152928290030190a2604080518281523360208201528151600692600080516020611b49833981519152928290030190a28315610fbd57604080518581523360208201528151600092600080516020611b49833981519152928290030190a25b5050505050565b60036020526000908152604090205481565b600481565b610fe3611204565b611034576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6040805162461bcd60e51b815260206004820152600f60248201526e1b9bdd081a5b5c1b195b595b9d1959608a1b604482015290519081900360640190fd5b600a6020526000908152604090205460ff1681565b600e5460ff166110d5576040805162461bcd60e51b81526020600482015260136024820152721b9bdd081c1c994b5a5b9a5d1a585b1a5e9959606a1b604482015290519081900360640190fd5b600e54610100900460ff1615611128576040805162461bcd60e51b8152602060048201526013602482015272185b1c9958591e481a5b9a5d1a585b1a5e9959606a1b604482015290519081900360640190fd5b6276a700611146600d5461113a61172f565b9063ffffffff6117ff16565b101561118f57611154611204565b61118f5760405162461bcd60e51b8152600401808060200182810382526027815260200180611b226027913960400191505060405180910390fd5b61119761172f565b600c55600e805461ff0019166101001790556040805133815290517f908408e307fc569b417f6cbec5d5a06f44a0a505ac0479b47d421a4b2fd6a1e69181900360200190a1565b600381565b60066020526000908152604090205481565b6000546001600160a01b031690565b6000546001600160a01b0316331490565b600181565b6002602052600090815260409020546001600160a01b031681565b600c5481565b600060405162461bcd60e51b815260040180806020018281038252602e815260200180611bb0602e913960400191505060405180910390fd5b60096020526000908152604090205481565b60046020526000908152604090205481565b600e54610100900460ff166112e6576040805162461bcd60e51b815260206004820152600f60248201526e1b9bdd081a5b9a5d1a585b1a5e9959608a1b604482015290519081900360640190fd5b60ff8116600090815260056020526040902054600c54829161130e919063ffffffff61185c16565b61131661172f565b10158015611336575060ff8082166000908152600b602052604090205416155b6113715760405162461bcd60e51b8152600401808060200182810382526029815260200180611bde6029913960400191505060405180910390fd5b60ff821660031480611386575060ff82166004145b80611394575060ff82166001145b806113a2575060ff82166005145b6113e0576040805162461bcd60e51b815260206004820152600a6024820152691ddc9bdb99c81c1bdbdb60b21b604482015290519081900360640190fd5b60ff8083166000908152600a6020526040812054909116611426575060ff8216600090815260096020908152604080832054600a909252909120805460ff191660011790555b6000611431846118b6565b60ff85166000908152600860205260409020549091506114689061145b908363ffffffff61173316565b839063ffffffff61185c16565b9150600082116114bf576040805162461bcd60e51b815260206004820152601960248201527f6e6f20696e7374616c6c6d656e747320617661696c61626c6500000000000000604482015290519081900360640190fd5b60006114cc8584846119a0565b90506114de838263ffffffff61185c16565b925060ff8516600314806114f5575060ff85166004145b156115905760015460ff8616600090815260026020908152604080832054815163a9059cbb60e01b81526001600160a01b03918216600482015260248101899052915194169363a9059cbb93604480840194938390030190829087803b15801561155e57600080fd5b505af1158015611572573d6000803e3d6000fd5b505050506040513d602081101561158857600080fd5b506116219050565b60015460ff8616600090815260026020908152604080832054815163238a3fe160e01b81526001600160a01b03918216600482015260248101899052915194169363238a3fe193604480840194938390030190829087803b1580156115f457600080fd5b505af1158015611608573d6000803e3d6000fd5b505050506040513d602081101561161e57600080fd5b50505b60408051848152336020820152815160ff881692600080516020611b49833981519152928290030190a25050505050565b600681565b600281565b60076020526000908152604090205481565b611676611204565b6116c7576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6116d081611a5d565b50565b6001546001600160a01b031681565b6001600160a01b0381166116d0576040805162461bcd60e51b815260206004820152600f60248201526e696e76616c6964206164647265737360881b604482015290519081900360640190fd5b4290565b6000826117425750600061178f565b8282028284828161174f57fe5b041461178c5760405162461bcd60e51b8152600401808060200182810382526021815260200180611b8f6021913960400191505060405180910390fd5b90505b92915050565b60008082116117eb576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b60008284816117f657fe5b04949350505050565b600082821115611856576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b60008282018381101561178c576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b60ff811660009081526007602052604081205481906118de906201518063ffffffff61173316565b60ff8416600090815260056020526040812054600c54929350909161191a91849161190e9163ffffffff61185c16565b9063ffffffff61185c16565b905061192f62015180610a2e8361113a61172f565b60ff851660009081526006602090815260408083205460079092529091205491945090611962908563ffffffff61185c16565b11156119995760ff84166000908152600760209081526040808320546006909252909120546119969163ffffffff6117ff16565b92505b5050919050565b60ff831660009081526004602052604081205481906119c5908563ffffffff6117ff16565b60ff86166000908152600460209081526040808320939093556007905220546119f4908463ffffffff61185c16565b60ff861660009081526007602081815260408084208590556006825290922054915211611a555760ff851660009081526004602052604090205415611a4c575060ff8416600090815260046020526040812080549190555b611a5585611afd565b949350505050565b6001600160a01b038116611aa25760405162461bcd60e51b8152600401808060200182810382526026815260200180611b696026913960400191505060405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b60ff166000908152600b60205260409020805460ff19166001179055565b3b15159056fe666f72206e6f77206f6e6c79206f776e65722063616e2063616c6c2074686973206d6574686f64a8b65b82b2ff2a955e75c1bfa6a0e92aafb764156295da77a0a4c714f3895c724f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7773656e64696e6720746f6b656e7320746f207468697320636f6e7472616374206973206e6f7420616c6c6f776564696e7374616c6c6d656e747320617265206e6f742061637469766520666f72207468697320706f6f6ca265627a7a723058208fc7fe0d6f40178495c4e655627ac8a6bc837eb89686042a07161f078a2ea04464736f6c634300050a0032c3a24b0501bd2c13a7e57f2db4369ec4c223447539fc0724a9d55ac4a06ebd4da15bc60c955c405d20d9149c709e2460f1c2d9a497496a7f46004d1772c3054cc69056f16cbaa3c616b828e333ab7d3a32310765507f8f58359e99ebb7a885f383ec6a1f0257b830b5e016457c9cf1435391bf56cc98f369a58a54fe93772465ee60d0579bcffd98e668647d59fec1ff86a7fb340ce572e844f234ae73a6918f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77405aad32e1adbac89bb7f176e338b8fc6e994ca210c9bb7bdca249b465942250cbc4e5fb02c3d1de23a9f1e014b4d2ee5aeaea9505df5e855c9210bf472495af00000000000000000000000086edd0c110d1fc7f8a5e1108623b3b1b4e3740f90000000000000000000000000df05adac0159e215111696339ad4998e5871b3d0000000000000000000000003cfe51b61e25750ab1426b0072e5d0cc5c30aafa0000000000000000000000000218b706898d234b85d2494df21eb0677eaea91800000000000000000000000086edd0c110d1fc7f8a5e1108623b3b1b4e3740f90000000000000000000000000df05adac0159e215111696339ad4998e5871b3d"
+
+ contract_address = insert(:contract_address, contract_code: bytecode)
+
+ :transaction
+ |> insert(
+ created_contract_address_hash: contract_address.hash,
+ input: contract_creation_code
+ )
+ |> with_block(status: :ok)
+
+ params = %{
+ "contract_source_code" => contract_source_code,
+ "compiler_version" => "v0.5.10+commit.5a6ea5b1",
+ "evm_version" => "default",
+ "name" => "Distribution",
+ "optimization" => true,
+ "optimization_runs" => 200,
+ "autodetect_constructor_args" => true
+ }
+
+ assert {:ok,
+ %{
+ abi: abi,
+ constructor_arguments:
+ "00000000000000000000000086edd0c110d1fc7f8a5e1108623b3b1b4e3740f90000000000000000000000000df05adac0159e215111696339ad4998e5871b3d0000000000000000000000003cfe51b61e25750ab1426b0072e5d0cc5c30aafa0000000000000000000000000218b706898d234b85d2494df21eb0677eaea91800000000000000000000000086edd0c110d1fc7f8a5e1108623b3b1b4e3740f90000000000000000000000000df05adac0159e215111696339ad4998e5871b3d"
+ }} = Verifier.evaluate_authenticity(contract_address.hash, params)
+
+ assert abi != nil
+ end
+
+ test "another failed constructor args verification" do
+ contract_source_code =
+ "#{File.cwd!()}/test/support/fixture/smart_contract/issue_with_constructor_args.sol"
+ |> File.read!()
+
+ bytecode =
+ "0x60806040523661001357610011610017565b005b6100115b61002761002261005e565b610096565b565b606061004e838360405180606001604052806027815260200161024c602791396100ba565b9392505050565b3b151590565b90565b60006100917f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b3660008037600080366000845af43d6000803e8080156100b5573d6000f35b3d6000fd5b6060833b61011e5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b60648201526084015b60405180910390fd5b600080856001600160a01b03168560405161013991906101cc565b600060405180830381855af49150503d8060008114610174576040519150601f19603f3d011682016040523d82523d6000602084013e610179565b606091505b5091509150610189828286610193565b9695505050505050565b606083156101a257508161004e565b8251156101b25782518084602001fd5b8160405162461bcd60e51b815260040161011591906101e8565b600082516101de81846020870161021b565b9190910192915050565b600060208252825180602084015261020781604085016020870161021b565b601f01601f19169190910160400192915050565b60005b8381101561023657818101518382015260200161021e565b83811115610245576000848401525b5050505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a26469706673582212209b8470f06e8a3960c912103fc2be177edaad69584ee3c7d2809ee737e79408e764736f6c63430008020033"
+
+ contract_creation_code =
+ "0x6080604052604051610772380380610772833981016040819052610022916102f7565b61004d60017f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbd61040f565b60008051602061072b8339815191521461007757634e487b7160e01b600052600160045260246000fd5b6100838282600061008a565b5050610474565b610093836100f4565b6040516001600160a01b038416907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a26000825111806100d45750805b156100ef576100ed83836101b460201b6100291760201c565b505b505050565b610107816101e060201b6100551760201c565b61016e5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b60648201526084015b60405180910390fd5b8061019360008051602061072b83398151915260001b6101e660201b61005b1760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b60606101d9838360405180606001604052806027815260200161074b602791396101e9565b9392505050565b3b151590565b90565b6060833b6102485760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b6064820152608401610165565b600080856001600160a01b03168560405161026391906103c0565b600060405180830381855af49150503d806000811461029e576040519150601f19603f3d011682016040523d82523d6000602084013e6102a3565b606091505b5090925090506102b48282866102be565b9695505050505050565b606083156102cd5750816101d9565b8251156102dd5782518084602001fd5b8160405162461bcd60e51b815260040161016591906103dc565b60008060408385031215610309578182fd5b82516001600160a01b038116811461031f578283fd5b60208401519092506001600160401b038082111561033b578283fd5b818501915085601f83011261034e578283fd5b8151818111156103605761036061045e565b604051601f8201601f19908116603f011681019083821181831017156103885761038861045e565b816040528281528860208487010111156103a0578586fd5b6103b1836020830160208801610432565b80955050505050509250929050565b600082516103d2818460208701610432565b9190910192915050565b60006020825282518060208401526103fb816040850160208701610432565b601f01601f19169190910160400192915050565b60008282101561042d57634e487b7160e01b81526011600452602481fd5b500390565b60005b8381101561044d578181015183820152602001610435565b838111156100ed5750506000910152565b634e487b7160e01b600052604160045260246000fd5b6102a8806104836000396000f3fe60806040523661001357610011610017565b005b6100115b61002761002261005e565b610096565b565b606061004e838360405180606001604052806027815260200161024c602791396100ba565b9392505050565b3b151590565b90565b60006100917f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b3660008037600080366000845af43d6000803e8080156100b5573d6000f35b3d6000fd5b6060833b61011e5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b60648201526084015b60405180910390fd5b600080856001600160a01b03168560405161013991906101cc565b600060405180830381855af49150503d8060008114610174576040519150601f19603f3d011682016040523d82523d6000602084013e610179565b606091505b5091509150610189828286610193565b9695505050505050565b606083156101a257508161004e565b8251156101b25782518084602001fd5b8160405162461bcd60e51b815260040161011591906101e8565b600082516101de81846020870161021b565b9190910192915050565b600060208252825180602084015261020781604085016020870161021b565b601f01601f19169190910160400192915050565b60005b8381101561023657818101518382015260200161021e565b83811115610245576000848401525b5050505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a26469706673582212209b8470f06e8a3960c912103fc2be177edaad69584ee3c7d2809ee737e79408e764736f6c63430008020033360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564000000000000000000000000569e7b559a3af9b2350b8db2afd8977b8bd0517200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000024c4d66de8000000000000000000000000217373ab5e0082b2ce622169672eca6f4462319c00000000000000000000000000000000000000000000000000000000"
+
+ contract_address = insert(:contract_address, contract_code: bytecode)
+
+ :transaction
+ |> insert(
+ created_contract_address_hash: contract_address.hash,
+ input: contract_creation_code
+ )
+ |> with_block(status: :ok)
+
+ params = %{
+ "contract_source_code" => contract_source_code,
+ "compiler_version" => "v0.8.2+commit.661d1103",
+ "evm_version" => "default",
+ "name" => "ERC1967Proxy",
+ "optimization" => true,
+ "optimization_runs" => 200,
+ "autodetect_constructor_args" => true
+ }
+
+ assert {:ok,
+ %{
+ abi: abi,
+ constructor_arguments:
+ "000000000000000000000000569e7b559a3af9b2350b8db2afd8977b8bd0517200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000024c4d66de8000000000000000000000000217373ab5e0082b2ce622169672eca6f4462319c00000000000000000000000000000000000000000000000000000000"
+ }} = Verifier.evaluate_authenticity(contract_address.hash, params)
+
+ assert abi != nil
+ end
+ end
+end
diff --git a/apps/explorer/test/explorer/smart_contract/verifier_test.exs b/apps/explorer/test/explorer/smart_contract/verifier_test.exs
deleted file mode 100644
index ac170a6cf527..000000000000
--- a/apps/explorer/test/explorer/smart_contract/verifier_test.exs
+++ /dev/null
@@ -1,788 +0,0 @@
-defmodule Explorer.SmartContract.VerifierTest do
- use ExUnit.Case, async: true
- use Explorer.DataCase
-
- @moduletag timeout: :infinity
-
- doctest Explorer.SmartContract.Verifier
-
- alias Explorer.SmartContract.Verifier
- alias Explorer.Factory
-
- @code_0_4 """
- pragma solidity ^0.4.0;
- contract Incrementer {
- event Incremented(address indexed sender, uint256 newValue);
- uint256 public value;
- address public lastSender;
- constructor(uint256 initialValue) public {
- value = initialValue;
- lastSender = msg.sender;
- }
- function inc(uint256 delta) public {
- value = value + delta;
- lastSender = msg.sender;
- }
- }
- """
-
- @code_0_5 """
- pragma solidity ^0.5.0;
- contract Incrementer {
- event Incremented(address indexed sender, uint256 newValue);
- uint256 public value;
- address public lastSender;
- constructor(uint256 initialValue) public {
- value = initialValue;
- lastSender = msg.sender;
- }
- function inc(uint256 delta) public {
- value = value + delta;
- lastSender = msg.sender;
- }
- }
- """
-
- @code_0_6 """
- pragma solidity ^0.6.0;
- contract Incrementer {
- event Incremented(address indexed sender, uint256 newValue);
- uint256 public value;
- address public lastSender;
- constructor(uint256 initialValue) public {
- value = initialValue;
- lastSender = msg.sender;
- }
- function inc(uint256 delta) public {
- value = value + delta;
- lastSender = msg.sender;
- }
- }
- """
-
- describe "evaluate_authenticity/2" do
- setup do
- {:ok, contract_code_info: Factory.contract_code_info()}
- end
-
- test "verifies the generated bytecode against bytecode retrieved from the blockchain", %{
- contract_code_info: contract_code_info
- } do
- contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode)
- insert(:transaction, created_contract_address_hash: contract_address.hash, input: contract_code_info.tx_input)
-
- params = %{
- "contract_source_code" => contract_code_info.source_code,
- "compiler_version" => contract_code_info.version,
- "name" => contract_code_info.name,
- "optimization" => contract_code_info.optimized
- }
-
- assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
- assert abi != nil
- end
-
- test "verifies the generated bytecode with external libraries" do
- contract_data =
- "#{File.cwd!()}/test/support/fixture/smart_contract/contract_with_lib.json"
- |> File.read!()
- |> Jason.decode!()
- |> List.first()
-
- compiler_version = contract_data["compiler_version"]
- external_libraries = contract_data["external_libraries"]
- name = contract_data["name"]
- optimize = contract_data["optimize"]
- contract = contract_data["contract"]
- expected_bytecode = contract_data["expected_bytecode"]
- tx_input = contract_data["tx_input"]
-
- contract_address = insert(:contract_address, contract_code: "0x" <> expected_bytecode)
- insert(:transaction, created_contract_address_hash: contract_address.hash, input: "0x" <> tx_input)
-
- params = %{
- "contract_source_code" => contract,
- "compiler_version" => compiler_version,
- "name" => name,
- "optimization" => optimize,
- "external_libraries" => external_libraries
- }
-
- assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
- assert abi != nil
- end
-
- test "verifies smart contract with new `whisper` metadata (bzz0 => bzz1) in solidity 0.5.11" do
- contract_data =
- "#{File.cwd!()}/test/support/fixture/smart_contract/solidity_5.11_new_whisper_metadata.json"
- |> File.read!()
- |> Jason.decode!()
-
- compiler_version = contract_data["compiler_version"]
- name = contract_data["name"]
- optimize = false
- contract = contract_data["contract"]
- expected_bytecode = contract_data["bytecode"]
- evm_version = contract_data["evm_version"]
- input = contract_data["input"]
-
- contract_address = insert(:contract_address, contract_code: "0x" <> expected_bytecode)
-
- :transaction
- |> insert(
- created_contract_address_hash: contract_address.hash,
- input: "0x" <> input
- )
-
- params = %{
- "contract_source_code" => contract,
- "compiler_version" => compiler_version,
- "evm_version" => evm_version,
- "name" => name,
- "optimization" => optimize
- }
-
- assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
- assert abi != nil
- end
-
- test "verifies smart contract with constructor arguments", %{
- contract_code_info: contract_code_info
- } do
- contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode)
-
- constructor_arguments = "0102030405"
-
- params = %{
- "contract_source_code" => contract_code_info.source_code,
- "compiler_version" => contract_code_info.version,
- "name" => contract_code_info.name,
- "optimization" => contract_code_info.optimized,
- "constructor_arguments" => constructor_arguments
- }
-
- :transaction
- |> insert(
- created_contract_address_hash: contract_address.hash,
- input: contract_code_info.tx_input <> constructor_arguments
- )
- |> with_block()
-
- assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
- assert abi != nil
- end
-
- test "tries to compile with the latest evm version if wrong evm version was provided" do
- bytecode =
- "0x6060604052341561000f57600080fd5b604051602080610223833981016040528080519060200190919050505b8060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b505b61019d806100866000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063256fec88146100545780633fa4f245146100a9578063812600df146100d2575b600080fd5b341561005f57600080fd5b6100676100f5565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100b457600080fd5b6100bc61011b565b6040518082815260200191505060405180910390f35b34156100dd57600080fd5b6100f36004808035906020019091905050610121565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b505600a165627a7a723058201de7017582ff17d45730bc9aedeac5b399399b71b188f42a164609c1b6f7f4760029"
-
- constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a"
-
- contract_address = insert(:contract_address, contract_code: bytecode)
-
- :transaction
- |> insert(
- created_contract_address_hash: contract_address.hash,
- input: bytecode <> constructor_arguments
- )
- |> with_block()
-
- code = """
- pragma solidity ^0.4.15;
- contract Incrementer {
- event Incremented(address indexed sender, uint256 newValue);
- uint256 public value;
- address public lastSender;
- function Incrementer(uint256 initialValue) {
- value = initialValue;
- lastSender = msg.sender;
- }
- function inc(uint256 delta) {
- value = value + delta;
- lastSender = msg.sender;
- }
- }
- """
-
- params = %{
- "contract_source_code" => code,
- "compiler_version" => "v0.4.15+commit.bbb8e64f",
- "evm_version" => "homestead",
- "name" => "Incrementer",
- "optimization" => false,
- "constructor_arguments" => constructor_arguments
- }
-
- assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
- assert abi != nil
- end
-
- test "verifies a library" do
- bytecode =
- "0x610102610026600b82828239805160001a60731461001957fe5b30600052607381538281f3fe730000000000000000000000000000000000000000301460806040526004361060335760003560e01c8063c2985578146038575b600080fd5b603e60b0565b6040805160208082528351818301528351919283929083019185019080838360005b8381101560765781810151838201526020016060565b50505050905090810190601f16801560a25780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b604080518082019091526003815262666f6f60e81b60208201529056fea265627a7a7231582079c18e1f9cf2812147d15e5d44f16ff748f8b7349d32dc9db50300a3ffbd3a9664736f6c634300050b0032"
-
- contract_address = insert(:contract_address, contract_code: bytecode)
- insert(:transaction, created_contract_address_hash: contract_address.hash, input: bytecode)
-
- code = """
- pragma solidity 0.5.11;
-
- library Foo {
- function foo() external pure returns (string memory) {
- return "foo";
- }
- }
- """
-
- params = %{
- "contract_source_code" => code,
- "compiler_version" => "v0.5.11+commit.c082d0b4",
- "evm_version" => "default",
- "name" => "Foo",
- "optimization" => true
- }
-
- assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
- assert abi != nil
- end
-
- test "verifies smart contract compiled with Solidity 0.5.9 (includes new metadata in bytecode) with constructor args" do
- path = File.cwd!() <> "/test/support/fixture/smart_contract/solidity_0.5.9_smart_contract.sol"
- contract = File.read!(path)
-
- constructor_arguments =
- "00000000000000000000000000000000000000000000003635c9adc5dea000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000a54657374546f6b656e32000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006546f6b656e320000000000000000000000000000000000000000000000000000"
-
- bytecode =
- "0x608060405234801561001057600080fd5b50600436106100a95760003560e01c80633177029f116100715780633177029f1461025f57806354fd4d50146102c557806370a082311461034857806395d89b41146103a0578063a9059cbb14610423578063dd62ed3e14610489576100a9565b806306fdde03146100ae578063095ea7b31461013157806318160ddd1461019757806323b872dd146101b5578063313ce5671461023b575b600080fd5b6100b6610501565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f65780820151818401526020810190506100db565b50505050905090810190601f1680156101235780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61017d6004803603604081101561014757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061059f565b604051808215151515815260200191505060405180910390f35b61019f610691565b6040518082815260200191505060405180910390f35b610221600480360360608110156101cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610696565b604051808215151515815260200191505060405180910390f35b61024361090f565b604051808260ff1660ff16815260200191505060405180910390f35b6102ab6004803603604081101561027557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610922565b604051808215151515815260200191505060405180910390f35b6102cd610a14565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561030d5780820151818401526020810190506102f2565b50505050905090810190601f16801561033a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61038a6004803603602081101561035e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ab2565b6040518082815260200191505060405180910390f35b6103a8610afa565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103e85780820151818401526020810190506103cd565b50505050905090810190601f1680156104155780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61046f6004803603604081101561043957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610b98565b604051808215151515815260200191505060405180910390f35b6104eb6004803603604081101561049f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cfe565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105975780601f1061056c57610100808354040283529160200191610597565b820191906000526020600020905b81548152906001019060200180831161057a57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b600090565b6000816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610762575081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410155b801561076e5750600082115b1561090357816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610908565b600090505b9392505050565b600460009054906101000a900460ff1681565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60068054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610aaa5780601f10610a7f57610100808354040283529160200191610aaa565b820191906000526020600020905b815481529060010190602001808311610a8d57829003601f168201915b505050505081565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b905780601f10610b6557610100808354040283529160200191610b90565b820191906000526020600020905b815481529060010190602001808311610b7357829003601f168201915b505050505081565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610be85750600082115b15610cf357816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610cf8565b600090505b92915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490509291505056fea265627a7a72305820fe0ba5210ac95870683c2cb054304b04565703bd16c7d7e956df694c9643c6d264736f6c63430005090032"
-
- input =
- "0x60806040526040518060400160405280600381526020017f302e3100000000000000000000000000000000000000000000000000000000008152506006908051906020019062000051929190620001e2565b503480156200005f57600080fd5b506040516200105b3803806200105b833981810160405260808110156200008557600080fd5b81019080805190602001909291908051640100000000811115620000a857600080fd5b82810190506020810184811115620000bf57600080fd5b8151856001820283011164010000000082111715620000dd57600080fd5b50509291906020018051906020019092919080516401000000008111156200010457600080fd5b828101905060208101848111156200011b57600080fd5b81518560018202830111640100000000821117156200013957600080fd5b5050929190505050836000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550836002819055508260039080519060200190620001a3929190620001e2565b5081600460006101000a81548160ff021916908360ff1602179055508060059080519060200190620001d7929190620001e2565b505050505062000291565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200022557805160ff191683800117855562000256565b8280016001018555821562000256579182015b828111156200025557825182559160200191906001019062000238565b5b50905062000265919062000269565b5090565b6200028e91905b808211156200028a57600081600090555060010162000270565b5090565b90565b610dba80620002a16000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c80633177029f116100715780633177029f1461025f57806354fd4d50146102c557806370a082311461034857806395d89b41146103a0578063a9059cbb14610423578063dd62ed3e14610489576100a9565b806306fdde03146100ae578063095ea7b31461013157806318160ddd1461019757806323b872dd146101b5578063313ce5671461023b575b600080fd5b6100b6610501565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f65780820151818401526020810190506100db565b50505050905090810190601f1680156101235780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61017d6004803603604081101561014757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061059f565b604051808215151515815260200191505060405180910390f35b61019f610691565b6040518082815260200191505060405180910390f35b610221600480360360608110156101cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610696565b604051808215151515815260200191505060405180910390f35b61024361090f565b604051808260ff1660ff16815260200191505060405180910390f35b6102ab6004803603604081101561027557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610922565b604051808215151515815260200191505060405180910390f35b6102cd610a14565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561030d5780820151818401526020810190506102f2565b50505050905090810190601f16801561033a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61038a6004803603602081101561035e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ab2565b6040518082815260200191505060405180910390f35b6103a8610afa565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103e85780820151818401526020810190506103cd565b50505050905090810190601f1680156104155780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61046f6004803603604081101561043957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610b98565b604051808215151515815260200191505060405180910390f35b6104eb6004803603604081101561049f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cfe565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105975780601f1061056c57610100808354040283529160200191610597565b820191906000526020600020905b81548152906001019060200180831161057a57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b600090565b6000816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610762575081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410155b801561076e5750600082115b1561090357816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610908565b600090505b9392505050565b600460009054906101000a900460ff1681565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60068054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610aaa5780601f10610a7f57610100808354040283529160200191610aaa565b820191906000526020600020905b815481529060010190602001808311610a8d57829003601f168201915b505050505081565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b905780601f10610b6557610100808354040283529160200191610b90565b820191906000526020600020905b815481529060010190602001808311610b7357829003601f168201915b505050505081565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610be85750600082115b15610cf357816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610cf8565b600090505b92915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490509291505056fea265627a7a72305820c63b805fe76ce64d7ba5aedfa99bbfa5b13cc8c2fabad31b2364f8ae421cd40c64736f6c63430005090032"
-
- contract_address = insert(:contract_address, contract_code: bytecode)
-
- :transaction
- |> insert(
- created_contract_address_hash: contract_address.hash,
- input: input <> constructor_arguments
- )
- |> with_block()
-
- params = %{
- "contract_source_code" => contract,
- "compiler_version" => "v0.5.9+commit.e560f70d",
- "evm_version" => "petersburg",
- "name" => "TestToken",
- "optimization" => false,
- "constructor_arguments" => constructor_arguments
- }
-
- assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
- assert abi != nil
- end
-
- # flaky test
- # test "returns error when bytecode doesn't match", %{contract_code_info: contract_code_info} do
- # contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode)
- # insert(:transaction, created_contract_address_hash: contract_address.hash)
-
- # different_code = "pragma solidity ^0.4.24; contract SimpleStorage {}"
-
- # params = %{
- # "contract_source_code" => different_code,
- # "compiler_version" => contract_code_info.version,
- # "evm_version" => "default",
- # "name" => contract_code_info.name,
- # "optimization" => contract_code_info.optimized
- # }
-
- # response = Verifier.evaluate_authenticity(contract_address.hash, params)
-
- # assert {:error, :generated_bytecode} = response
- # end
-
- # flaky test
- # test "returns error when contract has constructor arguments and they were not provided" do
- # path = File.cwd!() <> "/test/support/fixture/smart_contract/solidity_0.5.9_smart_contract.sol"
- # contract = File.read!(path)
-
- # constructor_arguments = ""
-
- # bytecode =
- # "0x608060405234801561001057600080fd5b50600436106100a95760003560e01c80633177029f116100715780633177029f1461025f57806354fd4d50146102c557806370a082311461034857806395d89b41146103a0578063a9059cbb14610423578063dd62ed3e14610489576100a9565b806306fdde03146100ae578063095ea7b31461013157806318160ddd1461019757806323b872dd146101b5578063313ce5671461023b575b600080fd5b6100b6610501565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f65780820151818401526020810190506100db565b50505050905090810190601f1680156101235780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61017d6004803603604081101561014757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061059f565b604051808215151515815260200191505060405180910390f35b61019f610691565b6040518082815260200191505060405180910390f35b610221600480360360608110156101cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610696565b604051808215151515815260200191505060405180910390f35b61024361090f565b604051808260ff1660ff16815260200191505060405180910390f35b6102ab6004803603604081101561027557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610922565b604051808215151515815260200191505060405180910390f35b6102cd610a14565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561030d5780820151818401526020810190506102f2565b50505050905090810190601f16801561033a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61038a6004803603602081101561035e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ab2565b6040518082815260200191505060405180910390f35b6103a8610afa565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103e85780820151818401526020810190506103cd565b50505050905090810190601f1680156104155780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61046f6004803603604081101561043957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610b98565b604051808215151515815260200191505060405180910390f35b6104eb6004803603604081101561049f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cfe565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105975780601f1061056c57610100808354040283529160200191610597565b820191906000526020600020905b81548152906001019060200180831161057a57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b600090565b6000816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610762575081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410155b801561076e5750600082115b1561090357816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610908565b600090505b9392505050565b600460009054906101000a900460ff1681565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60068054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610aaa5780601f10610a7f57610100808354040283529160200191610aaa565b820191906000526020600020905b815481529060010190602001808311610a8d57829003601f168201915b505050505081565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b905780601f10610b6557610100808354040283529160200191610b90565b820191906000526020600020905b815481529060010190602001808311610b7357829003601f168201915b505050505081565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610be85750600082115b15610cf357816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610cf8565b600090505b92915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490509291505056fea265627a7a72305820fe0ba5210ac95870683c2cb054304b04565703bd16c7d7e956df694c9643c6d264736f6c63430005090032"
-
- # contract_address = insert(:contract_address, contract_code: bytecode)
-
- # :transaction
- # |> insert(
- # created_contract_address_hash: contract_address.hash,
- # input: bytecode <> constructor_arguments
- # )
- # |> with_block()
-
- # params = %{
- # "contract_source_code" => contract,
- # "compiler_version" => "v0.5.9+commit.e560f70d",
- # "evm_version" => "petersburg",
- # "name" => "TestToken",
- # "optimization" => false,
- # "constructor_arguments" => constructor_arguments
- # }
-
- # assert {:error, :generated_bytecode} = Verifier.evaluate_authenticity(contract_address.hash, params)
- # end
-
- test "returns error when there is a compilation problem", %{contract_code_info: contract_code_info} do
- contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode)
-
- params = %{
- "contract_source_code" => "pragma solidity ^0.4.24; contract SimpleStorage { ",
- "compiler_version" => contract_code_info.version,
- "name" => contract_code_info.name,
- "optimization" => contract_code_info.optimized
- }
-
- assert {:error, :compilation, "Function, variable, struct or modifier declaration expected."} =
- Verifier.evaluate_authenticity(contract_address.hash, params)
- end
- end
-
- describe "extract_bytecode_and_metadata_hash/1" do
- test "extracts the bytecode from the hash" do
- code =
- "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a723058203c381c1b48b38d050c54d7ef296ecd411040e19420dfec94772b9c49ae106a0b0029"
-
- swarm_source = "3c381c1b48b38d050c54d7ef296ecd411040e19420dfec94772b9c49ae106a0b"
-
- bytecode =
- "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600"
-
- %{"metadata_hash" => _metadata_hash, "bytecode" => bytecode_from_code, "compiler_version" => _compiler_version} =
- Verifier.extract_bytecode_and_metadata_hash(code)
-
- assert bytecode == bytecode_from_code
- assert bytecode != code
- assert String.contains?(code, bytecode) == true
- assert String.contains?(bytecode, "0029") == false
- assert String.contains?(bytecode, swarm_source) == false
- end
-
- test "extracts everything to the left of the swarm hash" do
- code =
- "0x608060405234801561001057600080fd5b5060df80610010029f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a723058203c381c1b48b38d050c54d7ef296ecd411040e19420dfec94772b9c49ae106a0b0029"
-
- swarm_source = "3c381c1b48b38d050c54d7ef296ecd411040e19420dfec94772b9c49ae106a0b"
-
- bytecode =
- "0x608060405234801561001057600080fd5b5060df80610010029f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600"
-
- %{"metadata_hash" => _metadata_hash, "bytecode" => bytecode_from_code, "compiler_version" => _compiler_version} =
- Verifier.extract_bytecode_and_metadata_hash(code)
-
- assert bytecode == bytecode_from_code
- assert bytecode != code
- assert String.contains?(code, bytecode) == true
- assert String.contains?(bytecode, "0029") == true
- assert String.contains?(bytecode, swarm_source) == false
- end
-
- # https://solidity.readthedocs.io/en/v0.6.6/contracts.html?highlight=immutable#constant-and-immutable-state-variables
- test "verifies smart-contract with `immutable` assignment" do
- bytecode =
- "0x6080604052348015600f57600080fd5b506004361060285760003560e01c8063fb49908514602d575b600080fd5b605060048036036020811015604157600080fd5b50356001600160a01b03166064565b604080519115158252519081900360200190f35b7f0000000000000000000000000000000000000000000000056b3977a93ae7c2006001600160a01b038216311191905056fea2646970667358221220b4fbf35809f2d1b85699a897ebb75d00c8c26b29b72decc53db18ddbd853352164736f6c63430006070033"
-
- tx_input =
- "0x60e06040523360601b60c05234801561001757600080fd5b5060405161013f38038061013f8339818101604052604081101561003a57600080fd5b50805160209091015160808290526001600160a01b03163160a081905260c05160601c60cc6100736000395080606652505060cc6000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063fb49908514602d575b600080fd5b605060048036036020811015604157600080fd5b50356001600160a01b03166064565b604080519115158252519081900360200190f35b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b038216311191905056fea2646970667358221220b4fbf35809f2d1b85699a897ebb75d00c8c26b29b72decc53db18ddbd853352164736f6c63430006070033000000000000000000000000000000000000000000000000000000000000000100000000000000000000000023602745048d3b8d0a7f953ad444da4cd237ac83"
-
- contract_address = insert(:contract_address, contract_code: bytecode)
- insert(:transaction, created_contract_address_hash: contract_address.hash, input: tx_input)
-
- code = """
- pragma solidity >0.6.4 <0.7.0;
-
- contract C {
- uint constant X = 32**22 + 8;
- string constant TEXT = "abc";
- bytes32 constant MY_HASH = keccak256("abc");
- uint immutable decimals;
- uint immutable maxBalance;
- address immutable owner = msg.sender;
-
- constructor(uint _decimals, address _reference) public {
- decimals = _decimals;
- // Assignments to immutables can even access the environment.
- maxBalance = _reference.balance;
- }
-
- function isBalanceTooHigh(address _other) public view returns (bool) {
- return _other.balance > maxBalance;
- }
- }
- """
-
- constructor_arguments =
- "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000023602745048d3b8d0a7f953ad444da4cd237ac83"
-
- params = %{
- "contract_source_code" => code,
- "compiler_version" => "v0.6.7+commit.b8d736ae",
- "evm_version" => "default",
- "name" => "C",
- "optimization" => true,
- "constructor_arguments" => constructor_arguments
- }
-
- assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
- assert abi != nil
- end
-
- test "verifies smart-contract created from another contract" do
- path = File.cwd!() <> "/test/support/fixture/smart_contract/contract_from_factory.sol"
- contract = File.read!(path)
-
- constructor_arguments = "4e1477bdc40fc2458bf646f96f269502658277779fdf0f4080fe798a2d45bc37"
-
- bytecode =
- "0x608060405260043610603e5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416638052474d81146043575b600080fd5b348015604e57600080fd5b5060556067565b60408051918252519081900360200190f35b600054815600a165627a7a72305820a1a0ec90e133c3064fab0ae82aa02a020224ea39d2b5421b6788f800bdde02f60029"
-
- init =
- "0x608060405234801561001057600080fd5b506040516020806100cc83398101604052516000556099806100336000396000f300608060405260043610603e5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416638052474d81146043575b600080fd5b348015604e57600080fd5b5060556067565b60408051918252519081900360200190f35b600054815600a165627a7a72305820a1a0ec90e133c3064fab0ae82aa02a020224ea39d2b5421b6788f800bdde02f600294e1477bdc40fc2458bf646f96f269502658277779fdf0f4080fe798a2d45bc37"
-
- contract_address = insert(:contract_address, contract_code: bytecode)
-
- transaction_success_details = [
- status: :ok
- ]
-
- transaction =
- :transaction
- |> insert()
- |> with_block(transaction_success_details)
-
- :internal_transaction
- |> insert(
- created_contract_address_hash: contract_address.hash,
- init: init,
- type: "create",
- created_contract_code: bytecode,
- input: nil,
- transaction_hash: transaction.hash,
- index: 0,
- block_hash: transaction.block_hash,
- block_index: 0
- )
-
- params = %{
- "contract_source_code" => contract,
- "compiler_version" => "v0.4.26+commit.4563c3fc",
- "evm_version" => "default",
- "name" => "ContractFromFactory",
- "optimization" => true,
- "constructor_arguments" => constructor_arguments
- }
-
- assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
- assert abi != nil
- end
-
- test "verifies smart-contract created from another contract using successful tx" do
- path = File.cwd!() <> "/test/support/fixture/smart_contract/contract_from_factory.sol"
- contract = File.read!(path)
-
- constructor_arguments = "4e1477bdc40fc2458bf646f96f269502658277779fdf0f4080fe798a2d45bc37"
-
- bytecode =
- "0x608060405260043610603e5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416638052474d81146043575b600080fd5b348015604e57600080fd5b5060556067565b60408051918252519081900360200190f35b600054815600a165627a7a72305820a1a0ec90e133c3064fab0ae82aa02a020224ea39d2b5421b6788f800bdde02f60029"
-
- init =
- "0x608060405234801561001057600080fd5b506040516020806100cc83398101604052516000556099806100336000396000f300608060405260043610603e5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416638052474d81146043575b600080fd5b348015604e57600080fd5b5060556067565b60408051918252519081900360200190f35b600054815600a165627a7a72305820a1a0ec90e133c3064fab0ae82aa02a020224ea39d2b5421b6788f800bdde02f600294e1477bdc40fc2458bf646f96f269502658277779fdf0f4080fe798a2d45bc37"
-
- contract_address = insert(:contract_address, contract_code: bytecode)
-
- transaction_success_details = [
- status: :ok
- ]
-
- transaction_success =
- :transaction
- |> insert()
- |> with_block(transaction_success_details)
-
- transaction_failure_details = [
- status: :error
- ]
-
- transaction_failure =
- :transaction
- |> insert()
- |> with_block(transaction_failure_details)
-
- :internal_transaction
- |> insert(
- created_contract_address_hash: contract_address.hash,
- init: init,
- type: "create",
- created_contract_code: bytecode,
- input: nil,
- transaction_hash: transaction_success.hash,
- index: 0,
- block_hash: transaction_success.block_hash,
- block_index: 0
- )
-
- :internal_transaction
- |> insert(
- created_contract_address_hash: contract_address.hash,
- init: init,
- type: "create",
- created_contract_code: bytecode,
- input: nil,
- transaction_hash: transaction_failure.hash,
- index: 0,
- block_hash: transaction_failure.block_hash,
- block_index: 0
- )
-
- params = %{
- "contract_source_code" => contract,
- "compiler_version" => "v0.4.26+commit.4563c3fc",
- "evm_version" => "default",
- "name" => "ContractFromFactory",
- "optimization" => true,
- "constructor_arguments" => constructor_arguments
- }
-
- assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
- assert abi != nil
- end
- end
-
- describe "compiler version tests" do
- # flaky test
- # test "verification is failed if wrong version of compiler" do
- # bytecode_0_5_10 =
- # "0x608060405234801561001057600080fd5b506040516102453803806102458339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101a98061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72305820fb47165501c50aae8ccb0394b15f4302606e0ba55eb6d59fe12eca19ba494d5e64736f6c634300050a0032"
-
- # constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a"
- # contract_address = insert(:contract_address, contract_code: bytecode_0_5_10)
- # bytecode_construtor_arguments = "#{bytecode_0_5_10}#{constructor_arguments}"
-
- # :transaction
- # |> insert(
- # created_contract_address_hash: contract_address.hash,
- # input: bytecode_construtor_arguments
- # )
- # |> with_block()
-
- # params = %{
- # "contract_source_code" => @code_0_5,
- # "compiler_version" => "v0.5.11+commit.c082d0b4",
- # "evm_version" => "homestead",
- # "name" => "Incrementer",
- # "optimization" => false,
- # "constructor_arguments" => constructor_arguments
- # }
-
- # response = Verifier.evaluate_authenticity(contract_address.hash, params)
- # assert {:error, :compiler_version} = response
- # end
-
- test "verification is successful if proper version of compiler" do
- bytecode_0_5_10 =
- "0x608060405234801561001057600080fd5b506040516102453803806102458339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101a98061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72305820fb47165501c50aae8ccb0394b15f4302606e0ba55eb6d59fe12eca19ba494d5e64736f6c634300050a0032"
-
- constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a"
- contract_address = insert(:contract_address, contract_code: bytecode_0_5_10)
- bytecode_construtor_arguments = "#{bytecode_0_5_10}#{constructor_arguments}"
-
- :transaction
- |> insert(
- created_contract_address_hash: contract_address.hash,
- input: bytecode_construtor_arguments
- )
- |> with_block()
-
- params = %{
- "contract_source_code" => @code_0_5,
- "compiler_version" => "v0.5.10+commit.5a6ea5b1",
- "evm_version" => "homestead",
- "name" => "Incrementer",
- "optimization" => false,
- "constructor_arguments" => constructor_arguments
- }
-
- assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
- assert abi != nil
- end
- end
-
- describe "verification with nightly builds" do
- test "verification is successful if proper nightly version of compiler ~0.4" do
- bytecode_v0_4_24_nightly_2018_4_26_commit_ef2111a2 =
- "0x608060405234801561001057600080fd5b5060405160208061023d833981018060405281019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101b28061008b6000396000f300608060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063256fec881461005c5780633fa4f245146100b3578063812600df146100de575b600080fd5b34801561006857600080fd5b5061007161010b565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156100bf57600080fd5b506100c8610131565b6040518082815260200191505060405180910390f35b3480156100ea57600080fd5b5061010960048036038101908080359060200190929190505050610137565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505600a165627a7a723058202d622d653be0a507f7ac0bc89d8934ccdbaf5e127abd603c3864a462149885070029"
-
- constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a"
- contract_address = insert(:contract_address, contract_code: bytecode_v0_4_24_nightly_2018_4_26_commit_ef2111a2)
- bytecode_construtor_arguments = "#{bytecode_v0_4_24_nightly_2018_4_26_commit_ef2111a2}#{constructor_arguments}"
-
- :transaction
- |> insert(
- created_contract_address_hash: contract_address.hash,
- input: bytecode_construtor_arguments
- )
- |> with_block()
-
- params = %{
- "contract_source_code" => @code_0_4,
- "compiler_version" => "v0.4.24-nightly.2018.4.26+commit.ef2111a2",
- "evm_version" => "homestead",
- "name" => "Incrementer",
- "optimization" => false,
- "constructor_arguments" => constructor_arguments
- }
-
- assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
- assert abi != nil
- end
-
- test "verification is successful if proper nightly version of compiler ~0.5.10" do
- bytecode_0_5_10_nightly_2019_6_4_commit_95e6b2e4 =
- "0x608060405234801561001057600080fd5b5060405161026a38038061026a8339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101ce8061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a723058208d4e3fa9b2179a8384e617e388dde334be1b44e7b11b42ab964ab1050e7cedca64736f6c637827302e352e31302d6e696768746c792e323031392e362e342b636f6d6d69742e39356536623265340057"
-
- constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a"
- contract_address = insert(:contract_address, contract_code: bytecode_0_5_10_nightly_2019_6_4_commit_95e6b2e4)
- bytecode_construtor_arguments = "#{bytecode_0_5_10_nightly_2019_6_4_commit_95e6b2e4}#{constructor_arguments}"
-
- :transaction
- |> insert(
- created_contract_address_hash: contract_address.hash,
- input: bytecode_construtor_arguments
- )
- |> with_block()
-
- params = %{
- "contract_source_code" => @code_0_5,
- "compiler_version" => "v0.5.10-nightly.2019.6.4+commit.95e6b2e4",
- "evm_version" => "homestead",
- "name" => "Incrementer",
- "optimization" => false,
- "constructor_arguments" => constructor_arguments
- }
-
- assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
- assert abi != nil
- end
-
- test "verification is successful if proper nightly version of compiler ~0.5.11" do
- bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753 =
- "0x608060405234801561001057600080fd5b5060405161026b38038061026b8339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101cf8061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72305820f7420b8c3b16d83ce728d8c279f0f887c4dcd7bfcd38c484acc9cdb82fde785764736f6c637828302e352e31312d6e696768746c792e323031392e362e32352b636f6d6d69742e31636338343735330058"
-
- constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a"
- contract_address = insert(:contract_address, contract_code: bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753)
- bytecode_construtor_arguments = "#{bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753}#{constructor_arguments}"
-
- :transaction
- |> insert(
- created_contract_address_hash: contract_address.hash,
- input: bytecode_construtor_arguments
- )
- |> with_block()
-
- params = %{
- "contract_source_code" => @code_0_5,
- "compiler_version" => "v0.5.11-nightly.2019.6.25+commit.1cc84753",
- "evm_version" => "homestead",
- "name" => "Incrementer",
- "optimization" => false,
- "constructor_arguments" => constructor_arguments
- }
-
- assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
- assert abi != nil
- end
-
- test "verification is successful if proper nightly version of compiler ~0.5.14" do
- bytecode_0_5_14_nightly_2019_12_10_commit_45aa7a88 =
- "0x608060405234801561001057600080fd5b5060405161026c38038061026c8339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101d08061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72315820ec5a7ce04b1c2f97a3d3e61ae1b5cb06585e81c504542fd9668a8ead654da72764736f6c637829302e352e31342d6e696768746c792e323031392e31322e31302b636f6d6d69742e34356161376138380059"
-
- constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a"
- contract_address = insert(:contract_address, contract_code: bytecode_0_5_14_nightly_2019_12_10_commit_45aa7a88)
- bytecode_construtor_arguments = "#{bytecode_0_5_14_nightly_2019_12_10_commit_45aa7a88}#{constructor_arguments}"
-
- :transaction
- |> insert(
- created_contract_address_hash: contract_address.hash,
- input: bytecode_construtor_arguments
- )
- |> with_block()
-
- params = %{
- "contract_source_code" => @code_0_5,
- "compiler_version" => "v0.5.14-nightly.2019.12.10+commit.45aa7a88",
- "evm_version" => "homestead",
- "name" => "Incrementer",
- "optimization" => false,
- "constructor_arguments" => constructor_arguments
- }
-
- assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
- assert abi != nil
- end
-
- test "verification is successful if proper nightly version of compiler ~0.6.0" do
- bytecode_0_6_1_nightly_2020_1_2_commit_d082b9b8 =
- "0x608060405234801561001057600080fd5b5060405161026a38038061026a8339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101ce8061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea264697066735822122029b5dde5889a195ed02cebb1a638ae3754be34464b9a2bc8b48b6286636031fb64736f6c637826302e362e312d6e696768746c792e323032302e312e322b636f6d6d69742e64303832623962380057"
-
- constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a"
- contract_address = insert(:contract_address, contract_code: bytecode_0_6_1_nightly_2020_1_2_commit_d082b9b8)
- bytecode_construtor_arguments = "#{bytecode_0_6_1_nightly_2020_1_2_commit_d082b9b8}#{constructor_arguments}"
-
- :transaction
- |> insert(
- created_contract_address_hash: contract_address.hash,
- input: bytecode_construtor_arguments
- )
- |> with_block()
-
- params = %{
- "contract_source_code" => @code_0_6,
- "compiler_version" => "v0.6.1-nightly.2020.1.2+commit.d082b9b8",
- "evm_version" => "homestead",
- "name" => "Incrementer",
- "optimization" => false,
- "constructor_arguments" => constructor_arguments
- }
-
- assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params)
- assert abi != nil
- end
-
- test "verification is failed if wrong nightly version of compiler ~0.5.11" do
- bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753 =
- "0x608060405234801561001057600080fd5b5060405161026b38038061026b8339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101cf8061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72305820f7420b8c3b16d83ce728d8c279f0f887c4dcd7bfcd38c484acc9cdb82fde785764736f6c637828302e352e31312d6e696768746c792e323031392e362e32352b636f6d6d69742e31636338343735330058"
-
- constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a"
- contract_address = insert(:contract_address, contract_code: bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753)
- bytecode_construtor_arguments = "#{bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753}#{constructor_arguments}"
-
- :transaction
- |> insert(
- created_contract_address_hash: contract_address.hash,
- input: bytecode_construtor_arguments
- )
- |> with_block()
-
- params = %{
- "contract_source_code" => @code_0_5,
- "compiler_version" => "v0.5.11-nightly.2019.8.10+commit.f5f2bbb2",
- "evm_version" => "homestead",
- "name" => "Incrementer",
- "optimization" => false,
- "constructor_arguments" => constructor_arguments
- }
-
- response = Verifier.evaluate_authenticity(contract_address.hash, params)
- assert {:error, :compiler_version} = response
- end
- 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 b76b2438e4e8..95e8a2b04afd 100644
--- a/apps/explorer/test/support/factory.ex
+++ b/apps/explorer/test/support/factory.ex
@@ -138,6 +138,62 @@ defmodule Explorer.Factory do
}
end
+ def contract_code_info_modern_compilator do
+ %{
+ bytecode:
+ "0x608060405234801561001057600080fd5b50610150806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806360fe47b11461003b5780636d4ce63c14610057575b600080fd5b610055600480360381019061005091906100c3565b610075565b005b61005f61007f565b60405161006c91906100ff565b60405180910390f35b8060008190555050565b60008054905090565b600080fd5b6000819050919050565b6100a08161008d565b81146100ab57600080fd5b50565b6000813590506100bd81610097565b92915050565b6000602082840312156100d9576100d8610088565b5b60006100e7848285016100ae565b91505092915050565b6100f98161008d565b82525050565b600060208201905061011460008301846100f0565b9291505056fea2646970667358221220d5d429d16f620053da9907372b66303e007b04bfd112159cff82cb67ff40da4264736f6c634300080a0033",
+ tx_input:
+ "0x608060405234801561001057600080fd5b50610150806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806360fe47b11461003b5780636d4ce63c14610057575b600080fd5b610055600480360381019061005091906100c3565b610075565b005b61005f61007f565b60405161006c91906100ff565b60405180910390f35b8060008190555050565b60008054905090565b600080fd5b6000819050919050565b6100a08161008d565b81146100ab57600080fd5b50565b6000813590506100bd81610097565b92915050565b6000602082840312156100d9576100d8610088565b5b60006100e7848285016100ae565b91505092915050565b6100f98161008d565b82525050565b600060208201905061011460008301846100f0565b9291505056fea2646970667358221220d5d429d16f620053da9907372b66303e007b04bfd112159cff82cb67ff40da4264736f6c634300080a0033",
+ name: "SimpleStorage",
+ source_code: """
+ pragma solidity ^0.8.10;
+ // SPDX-License-Identifier: MIT
+
+ contract SimpleStorage {
+ uint storedData;
+
+ function set(uint x) public {
+ storedData = x;
+ }
+
+ function get() public view returns (uint) {
+ return storedData;
+ }
+ }
+ """,
+ abi: [
+ %{
+ "inputs" => [],
+ "name" => "get",
+ "outputs" => [
+ %{
+ "internalType" => "uint256",
+ "name" => "",
+ "type" => "uint256"
+ }
+ ],
+ "stateMutability" => "view",
+ "type" => "function"
+ },
+ %{
+ "inputs" => [
+ %{
+ "internalType" => "uint256",
+ "name" => "x",
+ "type" => "uint256"
+ }
+ ],
+ "name" => "set",
+ "outputs" => [],
+ "stateMutability" => "nonpayable",
+ "type" => "function"
+ }
+ ],
+ version: "v0.8.10+commit.fc410830",
+ optimized: false
+ }
+ end
+
def address_hash do
{:ok, address_hash} =
"address_hash"
@@ -586,7 +642,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/explorer/test/support/fixture/smart_contract/issue_3082.sol b/apps/explorer/test/support/fixture/smart_contract/issue_3082.sol
new file mode 100644
index 000000000000..7a06a2bfb48b
--- /dev/null
+++ b/apps/explorer/test/support/fixture/smart_contract/issue_3082.sol
@@ -0,0 +1,594 @@
+pragma solidity 0.5.10;
+
+interface IMultipleDistribution {
+ function initialize(address _tokenAddress) external;
+ function poolStake() external view returns (uint256);
+}
+
+
+interface IDistribution {
+ function supply() external view returns(uint256);
+ function poolAddress(uint8) external view returns(address);
+}
+
+
+interface IERC677MultiBridgeToken {
+ function transfer(address _to, uint256 _value) external returns (bool);
+ function transferDistribution(address _to, uint256 _value) external returns (bool);
+ function transferFrom(address _from, address _to, uint256 _value) external returns (bool);
+ function balanceOf(address _account) external view returns (uint256);
+}
+
+
+
+
+/**
+ * @dev Contract module which provides a basic access control mechanism, where
+ * there is an account (an owner) that can be granted exclusive access to
+ * specific functions.
+ *
+ * This module is used through inheritance. It will make available the modifier
+ * `onlyOwner`, which can be aplied to your functions to restrict their use to
+ * the owner.
+ */
+contract Ownable {
+ address private _owner;
+
+ event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
+
+ /**
+ * @dev Initializes the contract setting the deployer as the initial owner.
+ */
+ constructor () internal {
+ _owner = msg.sender;
+ emit OwnershipTransferred(address(0), _owner);
+ }
+
+ /**
+ * @dev Returns the address of the current owner.
+ */
+ function owner() public view returns (address) {
+ return _owner;
+ }
+
+ /**
+ * @dev Throws if called by any account other than the owner.
+ */
+ modifier onlyOwner() {
+ require(isOwner(), "Ownable: caller is not the owner");
+ _;
+ }
+
+ /**
+ * @dev Returns true if the caller is the current owner.
+ */
+ function isOwner() public view returns (bool) {
+ return msg.sender == _owner;
+ }
+
+ /**
+ * @dev Leaves the contract without owner. It will not be possible to call
+ * `onlyOwner` functions anymore. Can only be called by the current owner.
+ *
+ * > Note: Renouncing ownership will leave the contract without an owner,
+ * thereby removing any functionality that is only available to the owner.
+ */
+ function renounceOwnership() public onlyOwner {
+ emit OwnershipTransferred(_owner, address(0));
+ _owner = address(0);
+ }
+
+ /**
+ * @dev Transfers ownership of the contract to a new account (`newOwner`).
+ * Can only be called by the current owner.
+ */
+ function transferOwnership(address newOwner) public onlyOwner {
+ _transferOwnership(newOwner);
+ }
+
+ /**
+ * @dev Transfers ownership of the contract to a new account (`newOwner`).
+ */
+ function _transferOwnership(address newOwner) internal {
+ require(newOwner != address(0), "Ownable: new owner is the zero address");
+ emit OwnershipTransferred(_owner, newOwner);
+ _owner = newOwner;
+ }
+}
+
+
+
+/**
+ * @dev Wrappers over Solidity's arithmetic operations with added overflow
+ * checks.
+ *
+ * Arithmetic operations in Solidity wrap on overflow. This can easily result
+ * in bugs, because programmers usually assume that an overflow raises an
+ * error, which is the standard behavior in high level programming languages.
+ * `SafeMath` restores this intuition by reverting the transaction when an
+ * operation overflows.
+ *
+ * Using this library instead of the unchecked operations eliminates an entire
+ * class of bugs, so it's recommended to use it always.
+ */
+library SafeMath {
+ /**
+ * @dev Returns the addition of two unsigned integers, reverting on
+ * overflow.
+ *
+ * Counterpart to Solidity's `+` operator.
+ *
+ * Requirements:
+ * - Addition cannot overflow.
+ */
+ function add(uint256 a, uint256 b) internal pure returns (uint256) {
+ uint256 c = a + b;
+ require(c >= a, "SafeMath: addition overflow");
+
+ return c;
+ }
+
+ /**
+ * @dev Returns the subtraction of two unsigned integers, reverting on
+ * overflow (when the result is negative).
+ *
+ * Counterpart to Solidity's `-` operator.
+ *
+ * Requirements:
+ * - Subtraction cannot overflow.
+ */
+ function sub(uint256 a, uint256 b) internal pure returns (uint256) {
+ require(b <= a, "SafeMath: subtraction overflow");
+ uint256 c = a - b;
+
+ return c;
+ }
+
+ /**
+ * @dev Returns the multiplication of two unsigned integers, reverting on
+ * overflow.
+ *
+ * Counterpart to Solidity's `*` operator.
+ *
+ * Requirements:
+ * - Multiplication cannot overflow.
+ */
+ function mul(uint256 a, uint256 b) internal pure returns (uint256) {
+ // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
+ // benefit is lost if 'b' is also tested.
+ // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
+ if (a == 0) {
+ return 0;
+ }
+
+ uint256 c = a * b;
+ require(c / a == b, "SafeMath: multiplication overflow");
+
+ return c;
+ }
+
+ /**
+ * @dev Returns the integer division of two unsigned integers. Reverts on
+ * division by zero. The result is rounded towards zero.
+ *
+ * Counterpart to Solidity's `/` operator. Note: this function uses a
+ * `revert` opcode (which leaves remaining gas untouched) while Solidity
+ * uses an invalid opcode to revert (consuming all remaining gas).
+ *
+ * Requirements:
+ * - The divisor cannot be zero.
+ */
+ function div(uint256 a, uint256 b) internal pure returns (uint256) {
+ // Solidity only automatically asserts when dividing by 0
+ require(b > 0, "SafeMath: division by zero");
+ uint256 c = a / b;
+ // assert(a == b * c + a % b); // There is no case in which this doesn't hold
+
+ return c;
+ }
+
+ /**
+ * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
+ * Reverts when dividing by zero.
+ *
+ * Counterpart to Solidity's `%` operator. This function uses a `revert`
+ * opcode (which leaves remaining gas untouched) while Solidity uses an
+ * invalid opcode to revert (consuming all remaining gas).
+ *
+ * Requirements:
+ * - The divisor cannot be zero.
+ */
+ function mod(uint256 a, uint256 b) internal pure returns (uint256) {
+ require(b != 0, "SafeMath: modulo by zero");
+ return a % b;
+ }
+}
+
+
+
+/**
+ * @dev Collection of functions related to the address type,
+ */
+library Address {
+ /**
+ * @dev Returns true if `account` is a contract.
+ *
+ * This test is non-exhaustive, and there may be false-negatives: during the
+ * execution of a contract's constructor, its address will be reported as
+ * not containing a contract.
+ *
+ * > It is unsafe to assume that an address for which this function returns
+ * false is an externally-owned account (EOA) and not a contract.
+ */
+ function isContract(address account) internal view returns (bool) {
+ // This method relies in extcodesize, which returns 0 for contracts in
+ // construction, since the code is only stored at the end of the
+ // constructor execution.
+
+ uint256 size;
+ // solhint-disable-next-line no-inline-assembly
+ assembly { size := extcodesize(account) }
+ return size > 0;
+ }
+}
+
+
+
+
+
+/// @dev Distributes STAKE tokens.
+contract Distribution is Ownable, IDistribution {
+ using SafeMath for uint256;
+ using Address for address;
+
+ /// @dev Emits when `preInitialize` method has been called.
+ /// @param token The address of ERC677MultiBridgeToken.
+ /// @param caller The address of the caller.
+ event PreInitialized(address token, address caller);
+
+ /// @dev Emits when `initialize` method has been called.
+ /// @param caller The address of the caller.
+ event Initialized(address caller);
+
+ /// @dev Emits when an installment for the specified pool has been made.
+ /// @param pool The index of the pool.
+ /// @param value The installment value.
+ /// @param caller The address of the caller.
+ event InstallmentMade(uint8 indexed pool, uint256 value, address caller);
+
+ /// @dev Emits when the pool address was changed.
+ /// @param pool The index of the pool.
+ /// @param oldAddress Old address.
+ /// @param newAddress New address.
+ event PoolAddressChanged(uint8 indexed pool, address oldAddress, address newAddress);
+
+ /// @dev The instance of ERC677MultiBridgeToken.
+ IERC677MultiBridgeToken public token;
+
+ uint8 public constant ECOSYSTEM_FUND = 1;
+ uint8 public constant PUBLIC_OFFERING = 2;
+ uint8 public constant PRIVATE_OFFERING = 3;
+ uint8 public constant ADVISORS_REWARD = 4;
+ uint8 public constant FOUNDATION_REWARD = 5;
+ uint8 public constant LIQUIDITY_FUND = 6;
+
+ /// @dev Pool address.
+ mapping (uint8 => address) public poolAddress;
+ /// @dev Pool total amount of tokens.
+ mapping (uint8 => uint256) public stake;
+ /// @dev Amount of remaining tokens to distribute for the pool.
+ mapping (uint8 => uint256) public tokensLeft;
+ /// @dev Pool cliff period (in seconds).
+ mapping (uint8 => uint256) public cliff;
+ /// @dev Total number of installments for the pool.
+ mapping (uint8 => uint256) public numberOfInstallments;
+ /// @dev Number of installments that were made.
+ mapping (uint8 => uint256) public numberOfInstallmentsMade;
+ /// @dev The value of one-time installment for the pool.
+ mapping (uint8 => uint256) public installmentValue;
+ /// @dev The value to transfer to the pool at cliff.
+ mapping (uint8 => uint256) public valueAtCliff;
+ /// @dev Boolean variable that contains whether the value for the pool at cliff was paid or not.
+ mapping (uint8 => bool) public wasValueAtCliffPaid;
+ /// @dev Boolean variable that contains whether all installments for the pool were made or not.
+ mapping (uint8 => bool) public installmentsEnded;
+
+ /// @dev The total token supply.
+ uint256 constant public supply = 8537500 ether;
+
+ /// @dev The timestamp of the distribution start.
+ uint256 public distributionStartTimestamp;
+
+ /// @dev The timestamp of pre-initialization.
+ uint256 public preInitializationTimestamp;
+ /// @dev Boolean variable that indicates whether the contract was pre-initialized.
+ bool public isPreInitialized = false;
+ /// @dev Boolean variable that indicates whether the contract was initialized.
+ bool public isInitialized = false;
+
+ /// @dev Checks that the contract is initialized.
+ modifier initialized() {
+ require(isInitialized, "not initialized");
+ _;
+ }
+
+ /// @dev Checks that the installments for the given pool are started and are not finished already.
+ /// @param _pool The index of the pool.
+ modifier active(uint8 _pool) {
+ require(
+ // solium-disable-next-line security/no-block-members
+ _now() >= distributionStartTimestamp.add(cliff[_pool]) && !installmentsEnded[_pool],
+ "installments are not active for this pool"
+ );
+ _;
+ }
+
+ /// @dev Sets up constants and pools addresses that are used in distribution.
+ /// @param _ecosystemFundAddress The address of the Ecosystem Fund.
+ /// @param _publicOfferingAddress The address of the Public Offering.
+ /// @param _privateOfferingAddress The address of the Private Offering contract.
+ /// @param _advisorsRewardAddress The address of the Advisors Reward contract.
+ /// @param _foundationAddress The address of the Foundation Reward.
+ /// @param _liquidityFundAddress The address of the Liquidity Fund.
+ constructor(
+ address _ecosystemFundAddress,
+ address _publicOfferingAddress,
+ address _privateOfferingAddress,
+ address _advisorsRewardAddress,
+ address _foundationAddress,
+ address _liquidityFundAddress
+ ) public {
+ // validate provided addresses
+ require(
+ _privateOfferingAddress.isContract() &&
+ _advisorsRewardAddress.isContract(),
+ "not a contract address"
+ );
+ _validateAddress(_ecosystemFundAddress);
+ _validateAddress(_publicOfferingAddress);
+ _validateAddress(_foundationAddress);
+ _validateAddress(_liquidityFundAddress);
+ poolAddress[ECOSYSTEM_FUND] = _ecosystemFundAddress;
+ poolAddress[PUBLIC_OFFERING] = _publicOfferingAddress;
+ poolAddress[PRIVATE_OFFERING] = _privateOfferingAddress;
+ poolAddress[ADVISORS_REWARD] = _advisorsRewardAddress;
+ poolAddress[FOUNDATION_REWARD] = _foundationAddress;
+ poolAddress[LIQUIDITY_FUND] = _liquidityFundAddress;
+
+ // initialize token amounts
+ stake[ECOSYSTEM_FUND] = 4000000 ether;
+ stake[PUBLIC_OFFERING] = 400000 ether;
+ stake[PRIVATE_OFFERING] = IMultipleDistribution(poolAddress[PRIVATE_OFFERING]).poolStake();
+ stake[ADVISORS_REWARD] = IMultipleDistribution(poolAddress[ADVISORS_REWARD]).poolStake();
+ stake[FOUNDATION_REWARD] = 699049 ether;
+ stake[LIQUIDITY_FUND] = 816500 ether;
+
+ require(
+ stake[ECOSYSTEM_FUND] // solium-disable-line operator-whitespace
+ .add(stake[PUBLIC_OFFERING])
+ .add(stake[PRIVATE_OFFERING])
+ .add(stake[ADVISORS_REWARD])
+ .add(stake[FOUNDATION_REWARD])
+ .add(stake[LIQUIDITY_FUND])
+ == supply,
+ "wrong sum of pools stakes"
+ );
+
+ tokensLeft[ECOSYSTEM_FUND] = stake[ECOSYSTEM_FUND];
+ tokensLeft[PUBLIC_OFFERING] = stake[PUBLIC_OFFERING];
+ tokensLeft[PRIVATE_OFFERING] = stake[PRIVATE_OFFERING];
+ tokensLeft[ADVISORS_REWARD] = stake[ADVISORS_REWARD];
+ tokensLeft[FOUNDATION_REWARD] = stake[FOUNDATION_REWARD];
+ tokensLeft[LIQUIDITY_FUND] = stake[LIQUIDITY_FUND];
+
+ valueAtCliff[ECOSYSTEM_FUND] = stake[ECOSYSTEM_FUND].mul(20).div(100); // 20%
+ valueAtCliff[PRIVATE_OFFERING] = stake[PRIVATE_OFFERING].mul(10).div(100); // 10%
+ valueAtCliff[ADVISORS_REWARD] = stake[ADVISORS_REWARD].mul(20).div(100); // 20%
+ valueAtCliff[FOUNDATION_REWARD] = stake[FOUNDATION_REWARD].mul(20).div(100); // 20%
+
+ cliff[ECOSYSTEM_FUND] = 336 days;
+ cliff[PRIVATE_OFFERING] = 28 days;
+ cliff[ADVISORS_REWARD] = 84 days;
+ cliff[FOUNDATION_REWARD] = 84 days;
+
+ numberOfInstallments[ECOSYSTEM_FUND] = 336; // days
+ numberOfInstallments[PRIVATE_OFFERING] = 224; // days
+ numberOfInstallments[ADVISORS_REWARD] = 252; // days
+ numberOfInstallments[FOUNDATION_REWARD] = 252; // days
+
+ installmentValue[ECOSYSTEM_FUND] = _calculateInstallmentValue(ECOSYSTEM_FUND);
+ installmentValue[PRIVATE_OFFERING] = _calculateInstallmentValue(
+ PRIVATE_OFFERING,
+ stake[PRIVATE_OFFERING].mul(35).div(100) // 25% will be distributed at pre-initializing and 10% at cliff
+ );
+ installmentValue[ADVISORS_REWARD] = _calculateInstallmentValue(ADVISORS_REWARD);
+ installmentValue[FOUNDATION_REWARD] = _calculateInstallmentValue(FOUNDATION_REWARD);
+ }
+
+ /// @dev Pre-initializes the contract after the token is created.
+ /// Distributes tokens for Public Offering, Liquidity Fund, and part of Private Offering.
+ /// @param _tokenAddress The address of the STAKE token contract.
+ /// @param _initialStakeAmount The sum of initial stakes made on xDai chain before transitioning to POSDAO.
+ /// This amount must be sent to address(0) because it is excess on Mainnet side.
+ function preInitialize(address _tokenAddress, uint256 _initialStakeAmount) external onlyOwner {
+ require(!isPreInitialized, "already pre-initialized");
+
+ token = IERC677MultiBridgeToken(_tokenAddress);
+ uint256 balance = token.balanceOf(address(this));
+ require(balance == supply, "wrong contract balance");
+
+ preInitializationTimestamp = _now(); // solium-disable-line security/no-block-members
+ isPreInitialized = true;
+
+ IMultipleDistribution(poolAddress[PRIVATE_OFFERING]).initialize(_tokenAddress);
+ IMultipleDistribution(poolAddress[ADVISORS_REWARD]).initialize(_tokenAddress);
+ uint256 privateOfferingPrerelease = stake[PRIVATE_OFFERING].mul(25).div(100); // 25%
+
+ token.transferDistribution(poolAddress[PUBLIC_OFFERING], stake[PUBLIC_OFFERING]); // 100%
+ token.transfer(poolAddress[PRIVATE_OFFERING], privateOfferingPrerelease);
+
+ uint256 liquidityFundDistribution = stake[LIQUIDITY_FUND].sub(_initialStakeAmount);
+ token.transferDistribution(poolAddress[LIQUIDITY_FUND], liquidityFundDistribution);
+ if (_initialStakeAmount > 0) {
+ token.transferDistribution(address(0), _initialStakeAmount);
+ }
+
+ tokensLeft[PUBLIC_OFFERING] = tokensLeft[PUBLIC_OFFERING].sub(stake[PUBLIC_OFFERING]);
+ tokensLeft[PRIVATE_OFFERING] = tokensLeft[PRIVATE_OFFERING].sub(privateOfferingPrerelease);
+ tokensLeft[LIQUIDITY_FUND] = tokensLeft[LIQUIDITY_FUND].sub(stake[LIQUIDITY_FUND]);
+
+ emit PreInitialized(_tokenAddress, msg.sender);
+ emit InstallmentMade(PUBLIC_OFFERING, stake[PUBLIC_OFFERING], msg.sender);
+ emit InstallmentMade(PRIVATE_OFFERING, privateOfferingPrerelease, msg.sender);
+ emit InstallmentMade(LIQUIDITY_FUND, liquidityFundDistribution, msg.sender);
+
+ if (_initialStakeAmount > 0) {
+ emit InstallmentMade(0, _initialStakeAmount, msg.sender);
+ }
+ }
+
+ /// @dev Initializes token distribution.
+ function initialize() external {
+ require(isPreInitialized, "not pre-initialized");
+ require(!isInitialized, "already initialized");
+
+ if (_now().sub(preInitializationTimestamp) < 90 days) { // solium-disable-line security/no-block-members
+ require(isOwner(), "for now only owner can call this method");
+ }
+
+ distributionStartTimestamp = _now(); // solium-disable-line security/no-block-members
+ isInitialized = true;
+
+ emit Initialized(msg.sender);
+ }
+
+ /// @dev Changes the address of the specified pool.
+ /// @param _pool The index of the pool (only ECOSYSTEM_FUND or FOUNDATION_REWARD are allowed).
+ /// @param _newAddress The new address for the change.
+ function changePoolAddress(uint8 _pool, address _newAddress) external {
+ require(_pool == ECOSYSTEM_FUND || _pool == FOUNDATION_REWARD, "wrong pool");
+ require(msg.sender == poolAddress[_pool], "not authorized");
+ _validateAddress(_newAddress);
+ emit PoolAddressChanged(_pool, poolAddress[_pool], _newAddress);
+ poolAddress[_pool] = _newAddress;
+ }
+
+ /// @dev Makes an installment for one of the following pools:
+ /// Private Offering, Advisors Reward, Ecosystem Fund, Foundation Reward.
+ /// @param _pool The index of the pool.
+ function makeInstallment(uint8 _pool) public initialized active(_pool) {
+ require(
+ _pool == PRIVATE_OFFERING ||
+ _pool == ADVISORS_REWARD ||
+ _pool == ECOSYSTEM_FUND ||
+ _pool == FOUNDATION_REWARD,
+ "wrong pool"
+ );
+ uint256 value = 0;
+ if (!wasValueAtCliffPaid[_pool]) {
+ value = valueAtCliff[_pool];
+ wasValueAtCliffPaid[_pool] = true;
+ }
+ uint256 availableNumberOfInstallments = _calculateNumberOfAvailableInstallments(_pool);
+ value = value.add(installmentValue[_pool].mul(availableNumberOfInstallments));
+
+ require(value > 0, "no installments available");
+
+ uint256 remainder = _updatePoolData(_pool, value, availableNumberOfInstallments);
+ value = value.add(remainder);
+
+ if (_pool == PRIVATE_OFFERING || _pool == ADVISORS_REWARD) {
+ token.transfer(poolAddress[_pool], value);
+ } else {
+ token.transferDistribution(poolAddress[_pool], value);
+ }
+
+ emit InstallmentMade(_pool, value, msg.sender);
+ }
+
+ /// @dev This method is called after the STAKE tokens are transferred to this contract.
+ function onTokenTransfer(address, uint256, bytes memory) public pure returns (bool) {
+ revert("sending tokens to this contract is not allowed");
+ }
+
+ /// @dev The removed implementation of the ownership renouncing.
+ function renounceOwnership() public onlyOwner {
+ revert("not implemented");
+ }
+
+ function _now() internal view returns (uint256) {
+ return now; // solium-disable-line security/no-block-members
+ }
+
+ /// @dev Updates the given pool data after each installment:
+ /// the remaining number of tokens,
+ /// the number of made installments.
+ /// If the last installment are done and the tokens remained
+ /// then transfers them to the pool and marks that all installments for the given pool are made.
+ /// @param _pool The index of the pool.
+ /// @param _value Current installment value.
+ /// @param _currentNumberOfInstallments Number of installment that are made.
+ function _updatePoolData(
+ uint8 _pool,
+ uint256 _value,
+ uint256 _currentNumberOfInstallments
+ ) internal returns (uint256) {
+ uint256 remainder = 0;
+ tokensLeft[_pool] = tokensLeft[_pool].sub(_value);
+ numberOfInstallmentsMade[_pool] = numberOfInstallmentsMade[_pool].add(_currentNumberOfInstallments);
+ if (numberOfInstallmentsMade[_pool] >= numberOfInstallments[_pool]) {
+ if (tokensLeft[_pool] > 0) {
+ remainder = tokensLeft[_pool];
+ tokensLeft[_pool] = 0;
+ }
+ _endInstallment(_pool);
+ }
+ return remainder;
+ }
+
+ /// @dev Marks that all installments for the given pool are made.
+ /// @param _pool The index of the pool.
+ function _endInstallment(uint8 _pool) internal {
+ installmentsEnded[_pool] = true;
+ }
+
+ /// @dev Calculates the value of the installment for 1 day for the given pool.
+ /// @param _pool The index of the pool.
+ /// @param _valueAtCliff Custom value to distribute at cliff.
+ function _calculateInstallmentValue(
+ uint8 _pool,
+ uint256 _valueAtCliff
+ ) internal view returns (uint256) {
+ return stake[_pool].sub(_valueAtCliff).div(numberOfInstallments[_pool]);
+ }
+
+ /// @dev Calculates the value of the installment for 1 day for the given pool.
+ /// @param _pool The index of the pool.
+ function _calculateInstallmentValue(uint8 _pool) internal view returns (uint256) {
+ return _calculateInstallmentValue(_pool, valueAtCliff[_pool]);
+ }
+
+ /// @dev Calculates the number of available installments for the given pool.
+ /// @param _pool The index of the pool.
+ /// @return The number of available installments.
+ function _calculateNumberOfAvailableInstallments(
+ uint8 _pool
+ ) internal view returns (
+ uint256 availableNumberOfInstallments
+ ) {
+ uint256 paidDays = numberOfInstallmentsMade[_pool].mul(1 days);
+ uint256 lastTimestamp = distributionStartTimestamp.add(cliff[_pool]).add(paidDays);
+ // solium-disable-next-line security/no-block-members
+ availableNumberOfInstallments = _now().sub(lastTimestamp).div(1 days);
+ if (numberOfInstallmentsMade[_pool].add(availableNumberOfInstallments) > numberOfInstallments[_pool]) {
+ availableNumberOfInstallments = numberOfInstallments[_pool].sub(numberOfInstallmentsMade[_pool]);
+ }
+ }
+
+ /// @dev Checks for an empty address.
+ function _validateAddress(address _address) internal pure {
+ if (_address == address(0)) {
+ revert("invalid address");
+ }
+ }
+}
\ No newline at end of file
diff --git a/apps/explorer/test/support/fixture/smart_contract/issue_5114.sol b/apps/explorer/test/support/fixture/smart_contract/issue_5114.sol
new file mode 100644
index 000000000000..942bf82fbd21
--- /dev/null
+++ b/apps/explorer/test/support/fixture/smart_contract/issue_5114.sol
@@ -0,0 +1,686 @@
+// SPDX-License-Identifier: MIT
+
+pragma solidity ^0.8.0;
+
+/**
+ * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
+ * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
+ * be specified by overriding the virtual {_implementation} function.
+ *
+ * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
+ * different contract through the {_delegate} function.
+ *
+ * The success and return data of the delegated call will be returned back to the caller of the proxy.
+ */
+abstract contract Proxy {
+ /**
+ * @dev Delegates the current call to `implementation`.
+ *
+ * This function does not return to its internall call site, it will return directly to the external caller.
+ */
+ function _delegate(address implementation) internal virtual {
+ // solhint-disable-next-line no-inline-assembly
+ assembly {
+ // Copy msg.data. We take full control of memory in this inline assembly
+ // block because it will not return to Solidity code. We overwrite the
+ // Solidity scratch pad at memory position 0.
+ calldatacopy(0, 0, calldatasize())
+
+ // Call the implementation.
+ // out and outsize are 0 because we don't know the size yet.
+ let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
+
+ // Copy the returned data.
+ returndatacopy(0, 0, returndatasize())
+
+ switch result
+ // delegatecall returns 0 on error.
+ case 0 { revert(0, returndatasize()) }
+ default { return(0, returndatasize()) }
+ }
+ }
+
+ /**
+ * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
+ * and {_fallback} should delegate.
+ */
+ function _implementation() internal view virtual returns (address);
+
+ /**
+ * @dev Delegates the current call to the address returned by `_implementation()`.
+ *
+ * This function does not return to its internall call site, it will return directly to the external caller.
+ */
+ function _fallback() internal virtual {
+ _beforeFallback();
+ _delegate(_implementation());
+ }
+
+ /**
+ * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
+ * function in the contract matches the call data.
+ */
+ fallback () external payable virtual {
+ _fallback();
+ }
+
+ /**
+ * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
+ * is empty.
+ */
+ receive () external payable virtual {
+ _fallback();
+ }
+
+ /**
+ * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
+ * call, or as part of the Solidity `fallback` or `receive` functions.
+ *
+ * If overriden should call `super._beforeFallback()`.
+ */
+ function _beforeFallback() internal virtual {
+ }
+}
+
+
+/**
+ * @dev This is the interface that {BeaconProxy} expects of its beacon.
+ */
+interface IBeacon {
+ /**
+ * @dev Must return an address that can be used as a delegate call target.
+ *
+ * {BeaconProxy} will check that this address is a contract.
+ */
+ function implementation() external view returns (address);
+}
+
+
+/**
+ * @dev Collection of functions related to the address type
+ */
+library Address {
+ /**
+ * @dev Returns true if `account` is a contract.
+ *
+ * [IMPORTANT]
+ * ====
+ * It is unsafe to assume that an address for which this function returns
+ * false is an externally-owned account (EOA) and not a contract.
+ *
+ * Among others, `isContract` will return false for the following
+ * types of addresses:
+ *
+ * - an externally-owned account
+ * - a contract in construction
+ * - an address where a contract will be created
+ * - an address where a contract lived, but was destroyed
+ * ====
+ */
+ function isContract(address account) internal view returns (bool) {
+ // This method relies on extcodesize, which returns 0 for contracts in
+ // construction, since the code is only stored at the end of the
+ // constructor execution.
+
+ uint256 size;
+ // solhint-disable-next-line no-inline-assembly
+ assembly { size := extcodesize(account) }
+ return size > 0;
+ }
+
+ /**
+ * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
+ * `recipient`, forwarding all available gas and reverting on errors.
+ *
+ * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
+ * of certain opcodes, possibly making contracts go over the 2300 gas limit
+ * imposed by `transfer`, making them unable to receive funds via
+ * `transfer`. {sendValue} removes this limitation.
+ *
+ * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
+ *
+ * IMPORTANT: because control is transferred to `recipient`, care must be
+ * taken to not create reentrancy vulnerabilities. Consider using
+ * {ReentrancyGuard} or the
+ * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
+ */
+ function sendValue(address payable recipient, uint256 amount) internal {
+ require(address(this).balance >= amount, "Address: insufficient balance");
+
+ // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
+ (bool success, ) = recipient.call{ value: amount }("");
+ require(success, "Address: unable to send value, recipient may have reverted");
+ }
+
+ /**
+ * @dev Performs a Solidity function call using a low level `call`. A
+ * plain`call` is an unsafe replacement for a function call: use this
+ * function instead.
+ *
+ * If `target` reverts with a revert reason, it is bubbled up by this
+ * function (like regular Solidity function calls).
+ *
+ * Returns the raw returned data. To convert to the expected return value,
+ * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
+ *
+ * Requirements:
+ *
+ * - `target` must be a contract.
+ * - calling `target` with `data` must not revert.
+ *
+ * _Available since v3.1._
+ */
+ function functionCall(address target, bytes memory data) internal returns (bytes memory) {
+ return functionCall(target, data, "Address: low-level call failed");
+ }
+
+ /**
+ * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
+ * `errorMessage` as a fallback revert reason when `target` reverts.
+ *
+ * _Available since v3.1._
+ */
+ function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
+ return functionCallWithValue(target, data, 0, errorMessage);
+ }
+
+ /**
+ * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
+ * but also transferring `value` wei to `target`.
+ *
+ * Requirements:
+ *
+ * - the calling contract must have an ETH balance of at least `value`.
+ * - the called Solidity function must be `payable`.
+ *
+ * _Available since v3.1._
+ */
+ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
+ return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
+ }
+
+ /**
+ * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
+ * with `errorMessage` as a fallback revert reason when `target` reverts.
+ *
+ * _Available since v3.1._
+ */
+ function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
+ require(address(this).balance >= value, "Address: insufficient balance for call");
+ require(isContract(target), "Address: call to non-contract");
+
+ // solhint-disable-next-line avoid-low-level-calls
+ (bool success, bytes memory returndata) = target.call{ value: value }(data);
+ return _verifyCallResult(success, returndata, errorMessage);
+ }
+
+ /**
+ * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
+ * but performing a static call.
+ *
+ * _Available since v3.3._
+ */
+ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
+ return functionStaticCall(target, data, "Address: low-level static call failed");
+ }
+
+ /**
+ * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
+ * but performing a static call.
+ *
+ * _Available since v3.3._
+ */
+ function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
+ require(isContract(target), "Address: static call to non-contract");
+
+ // solhint-disable-next-line avoid-low-level-calls
+ (bool success, bytes memory returndata) = target.staticcall(data);
+ return _verifyCallResult(success, returndata, errorMessage);
+ }
+
+ /**
+ * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
+ * but performing a delegate call.
+ *
+ * _Available since v3.4._
+ */
+ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
+ return functionDelegateCall(target, data, "Address: low-level delegate call failed");
+ }
+
+ /**
+ * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
+ * but performing a delegate call.
+ *
+ * _Available since v3.4._
+ */
+ function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
+ require(isContract(target), "Address: delegate call to non-contract");
+
+ // solhint-disable-next-line avoid-low-level-calls
+ (bool success, bytes memory returndata) = target.delegatecall(data);
+ return _verifyCallResult(success, returndata, errorMessage);
+ }
+
+ function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
+ if (success) {
+ return returndata;
+ } else {
+ // Look for revert reason and bubble it up if present
+ if (returndata.length > 0) {
+ // The easiest way to bubble the revert reason is using memory via assembly
+
+ // solhint-disable-next-line no-inline-assembly
+ assembly {
+ let returndata_size := mload(returndata)
+ revert(add(32, returndata), returndata_size)
+ }
+ } else {
+ revert(errorMessage);
+ }
+ }
+ }
+}
+
+/**
+ * @dev Library for reading and writing primitive types to specific storage slots.
+ *
+ * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
+ * This library helps with reading and writing to such slots without the need for inline assembly.
+ *
+ * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
+ *
+ * Example usage to set ERC1967 implementation slot:
+ * ```
+ * contract ERC1967 {
+ * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
+ *
+ * function _getImplementation() internal view returns (address) {
+ * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
+ * }
+ *
+ * function _setImplementation(address newImplementation) internal {
+ * require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
+ * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
+ * }
+ * }
+ * ```
+ *
+ * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
+ */
+library StorageSlot {
+ struct AddressSlot {
+ address value;
+ }
+
+ struct BooleanSlot {
+ bool value;
+ }
+
+ struct Bytes32Slot {
+ bytes32 value;
+ }
+
+ struct Uint256Slot {
+ uint256 value;
+ }
+
+ /**
+ * @dev Returns an `AddressSlot` with member `value` located at `slot`.
+ */
+ function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
+ assembly {
+ r.slot := slot
+ }
+ }
+
+ /**
+ * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
+ */
+ function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
+ assembly {
+ r.slot := slot
+ }
+ }
+
+ /**
+ * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
+ */
+ function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
+ assembly {
+ r.slot := slot
+ }
+ }
+
+ /**
+ * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
+ */
+ function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
+ assembly {
+ r.slot := slot
+ }
+ }
+}
+
+/**
+ * @dev This abstract contract provides getters and event emitting update functions for
+ * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
+ *
+ * _Available since v4.1._
+ *
+ * @custom:oz-upgrades-unsafe-allow delegatecall
+ */
+abstract contract ERC1967Upgrade {
+ // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
+ bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
+
+ /**
+ * @dev Storage slot with the address of the current implementation.
+ * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
+ * validated in the constructor.
+ */
+ bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
+
+ /**
+ * @dev Emitted when the implementation is upgraded.
+ */
+ event Upgraded(address indexed implementation);
+
+ /**
+ * @dev Returns the current implementation address.
+ */
+ function _getImplementation() internal view returns (address) {
+ return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
+ }
+
+ /**
+ * @dev Stores a new address in the EIP1967 implementation slot.
+ */
+ function _setImplementation(address newImplementation) private {
+ require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
+ StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
+ }
+
+ /**
+ * @dev Perform implementation upgrade
+ *
+ * Emits an {Upgraded} event.
+ */
+ function _upgradeTo(address newImplementation) internal {
+ _setImplementation(newImplementation);
+ emit Upgraded(newImplementation);
+ }
+
+ /**
+ * @dev Perform implementation upgrade with additional setup call.
+ *
+ * Emits an {Upgraded} event.
+ */
+ function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {
+ _setImplementation(newImplementation);
+ emit Upgraded(newImplementation);
+ if (data.length > 0 || forceCall) {
+ Address.functionDelegateCall(newImplementation, data);
+ }
+ }
+
+ /**
+ * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
+ *
+ * Emits an {Upgraded} event.
+ */
+ function _upgradeToAndCallSecure(address newImplementation, bytes memory data, bool forceCall) internal {
+ address oldImplementation = _getImplementation();
+
+ // Initial upgrade and setup call
+ _setImplementation(newImplementation);
+ if (data.length > 0 || forceCall) {
+ Address.functionDelegateCall(newImplementation, data);
+ }
+
+ // Perform rollback test if not already in progress
+ StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT);
+ if (!rollbackTesting.value) {
+ // Trigger rollback using upgradeTo from the new implementation
+ rollbackTesting.value = true;
+ Address.functionDelegateCall(
+ newImplementation,
+ abi.encodeWithSignature(
+ "upgradeTo(address)",
+ oldImplementation
+ )
+ );
+ rollbackTesting.value = false;
+ // Check rollback was effective
+ require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");
+ // Finally reset to the new implementation and log the upgrade
+ _setImplementation(newImplementation);
+ emit Upgraded(newImplementation);
+ }
+ }
+
+ /**
+ * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
+ * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
+ *
+ * Emits a {BeaconUpgraded} event.
+ */
+ function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {
+ _setBeacon(newBeacon);
+ emit BeaconUpgraded(newBeacon);
+ if (data.length > 0 || forceCall) {
+ Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
+ }
+ }
+
+ /**
+ * @dev Storage slot with the admin of the contract.
+ * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
+ * validated in the constructor.
+ */
+ bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
+
+ /**
+ * @dev Emitted when the admin account has changed.
+ */
+ event AdminChanged(address previousAdmin, address newAdmin);
+
+ /**
+ * @dev Returns the current admin.
+ */
+ function _getAdmin() internal view returns (address) {
+ return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
+ }
+
+ /**
+ * @dev Stores a new address in the EIP1967 admin slot.
+ */
+ function _setAdmin(address newAdmin) private {
+ require(newAdmin != address(0), "ERC1967: new admin is the zero address");
+ StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
+ }
+
+ /**
+ * @dev Changes the admin of the proxy.
+ *
+ * Emits an {AdminChanged} event.
+ */
+ function _changeAdmin(address newAdmin) internal {
+ emit AdminChanged(_getAdmin(), newAdmin);
+ _setAdmin(newAdmin);
+ }
+
+ /**
+ * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
+ * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
+ */
+ bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
+
+ /**
+ * @dev Emitted when the beacon is upgraded.
+ */
+ event BeaconUpgraded(address indexed beacon);
+
+ /**
+ * @dev Returns the current beacon.
+ */
+ function _getBeacon() internal view returns (address) {
+ return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
+ }
+
+ /**
+ * @dev Stores a new beacon in the EIP1967 beacon slot.
+ */
+ function _setBeacon(address newBeacon) private {
+ require(
+ Address.isContract(newBeacon),
+ "ERC1967: new beacon is not a contract"
+ );
+ require(
+ Address.isContract(IBeacon(newBeacon).implementation()),
+ "ERC1967: beacon implementation is not a contract"
+ );
+ StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
+ }
+}
+
+/**
+ * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
+ * implementation address that can be changed. This address is stored in storage in the location specified by
+ * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
+ * implementation behind the proxy.
+ */
+contract ERC1967Proxy is Proxy, ERC1967Upgrade {
+ /**
+ * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
+ *
+ * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
+ * function call, and allows initializating the storage of the proxy like a Solidity constructor.
+ */
+ constructor(address _logic, bytes memory _data) payable {
+ assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
+ _upgradeToAndCall(_logic, _data, false);
+ }
+
+ /**
+ * @dev Returns the current implementation address.
+ */
+ function _implementation() internal view virtual override returns (address impl) {
+ return ERC1967Upgrade._getImplementation();
+ }
+}
+
+/**
+ * @dev This contract implements a proxy that is upgradeable by an admin.
+ *
+ * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
+ * clashing], which can potentially be used in an attack, this contract uses the
+ * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
+ * things that go hand in hand:
+ *
+ * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
+ * that call matches one of the admin functions exposed by the proxy itself.
+ * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
+ * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
+ * "admin cannot fallback to proxy target".
+ *
+ * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
+ * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
+ * to sudden errors when trying to call a function from the proxy implementation.
+ *
+ * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
+ * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
+ */
+contract TransparentUpgradeableProxy is ERC1967Proxy {
+ /**
+ * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
+ * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
+ */
+ constructor(address _logic, address admin_, bytes memory _data) payable ERC1967Proxy(_logic, _data) {
+ assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
+ _changeAdmin(admin_);
+ }
+
+ /**
+ * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
+ */
+ modifier ifAdmin() {
+ if (msg.sender == _getAdmin()) {
+ _;
+ } else {
+ _fallback();
+ }
+ }
+
+ /**
+ * @dev Returns the current admin.
+ *
+ * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
+ *
+ * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
+ * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
+ * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
+ */
+ function admin() external ifAdmin returns (address admin_) {
+ admin_ = _getAdmin();
+ }
+
+ /**
+ * @dev Returns the current implementation.
+ *
+ * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
+ *
+ * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
+ * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
+ * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
+ */
+ function implementation() external ifAdmin returns (address implementation_) {
+ implementation_ = _implementation();
+ }
+
+ /**
+ * @dev Changes the admin of the proxy.
+ *
+ * Emits an {AdminChanged} event.
+ *
+ * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
+ */
+ function changeAdmin(address newAdmin) external virtual ifAdmin {
+ _changeAdmin(newAdmin);
+ }
+
+ /**
+ * @dev Upgrade the implementation of the proxy.
+ *
+ * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
+ */
+ function upgradeTo(address newImplementation) external ifAdmin {
+ _upgradeToAndCall(newImplementation, bytes(""), false);
+ }
+
+ /**
+ * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
+ * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
+ * proxied contract.
+ *
+ * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
+ */
+ function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
+ _upgradeToAndCall(newImplementation, data, true);
+ }
+
+ /**
+ * @dev Returns the current admin.
+ */
+ function _admin() internal view virtual returns (address) {
+ return _getAdmin();
+ }
+
+ /**
+ * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
+ */
+ function _beforeFallback() internal virtual override {
+ require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
+ super._beforeFallback();
+ }
+}
\ No newline at end of file
diff --git a/apps/explorer/test/support/fixture/smart_contract/issue_5127.sol b/apps/explorer/test/support/fixture/smart_contract/issue_5127.sol
new file mode 100644
index 000000000000..f36f90f797d0
--- /dev/null
+++ b/apps/explorer/test/support/fixture/smart_contract/issue_5127.sol
@@ -0,0 +1,554 @@
+// Sources flattened with hardhat v2.7.0 https://hardhat.org
+
+// File contracts/modules/pause/interfaces/IPauseable.sol
+
+// SPDX-License-Identifier: MIT
+
+pragma solidity >=0.5.0;
+
+interface IPauseable {
+ event Paused(address account);
+ event Unpaused(address account);
+
+ function paused() external view returns (bool);
+
+ function pause() external;
+ function unpause() external;
+}
+
+
+// File contracts/modules/blacklist/interfaces/IBlacklist.sol
+
+pragma solidity >=0.5.0;
+
+interface IBlacklist {
+
+ event AddBlacklist(address indexed account, address indexed caller);
+ event RevokeBlacklist(address indexed account, address indexed caller);
+
+ function blacklist(address account) external view returns (bool);
+
+ function addBlacklist(address account) external;
+
+ function revokeBlacklist(address account) external;
+}
+
+
+// File contracts/modules/committee/interfaces/IKAP20Committee.sol
+
+pragma solidity >=0.5.0;
+
+interface IKAP20Committee {
+ event SetCommittee(address oldCommittee, address newComittee);
+
+ function committee() external view returns (address);
+
+ function setCommittee(address _committee) external;
+}
+
+
+// File contracts/modules/kyc/interfaces/IKYCBitkubChain.sol
+
+pragma solidity >=0.6.0;
+
+interface IKYCBitkubChain {
+ function kycsLevel(address _addr) external view returns (uint256);
+}
+
+
+// File contracts/modules/kyc/interfaces/IKAP20KYC.sol
+
+pragma solidity >=0.5.0;
+
+interface IKAP20KYC {
+ event ActivateOnlyKYCAddress();
+ event SetKYC(address oldKyc, address newKyc);
+ event SetAccecptedKycLevel(uint256 oldKycLevel, uint256 newKycLevel);
+
+ function activateOnlyKycAddress() external;
+ function setKYC(address _kyc) external;
+ function setAcceptedKycLevel(uint256 _kycLevel) external;
+
+ function kyc() external returns(IKYCBitkubChain);
+ function acceptedKycLevel() external returns(uint256);
+ function isActivatedOnlyKycAddress() external returns(bool);
+}
+
+
+// File contracts/modules/kap20/interfaces/IKAP20.sol
+
+pragma solidity >=0.5.0;
+
+
+
+
+interface IKAP20 is IPauseable, IBlacklist, IKAP20Committee, IKAP20KYC {
+ event Transfer(address indexed from, address indexed to, uint256 tokens);
+ event Approval(address indexed tokenOwner, address indexed spender, uint256 tokens);
+
+ function name() external view returns (string memory);
+
+ function symbol() external view returns (string memory);
+
+ function decimals() external view returns (uint8);
+
+ function totalSupply() external view returns (uint256);
+
+ function balanceOf(address tokenOwner) external view returns (uint256 balance);
+
+ function allowance(address tokenOwner, address spender) external view returns (uint256 remaining);
+
+ function transfer(address to, uint256 tokens) external returns (bool success);
+
+ function approve(address spender, uint256 tokens) external returns (bool success);
+
+ function transferFrom(address from, address to, uint256 tokens) external returns (bool success);
+
+ function adminTransfer(address _from, address _to, uint256 _value) external returns (bool success);
+
+}
+
+
+// File contracts/modules/kap20/interfaces/IKToken.sol
+
+pragma solidity >=0.5.0;
+
+interface IKToken {
+ function internalTransfer(
+ address sender,
+ address recipient,
+ uint256 amount
+ ) external returns (bool);
+
+ function externalTransfer(
+ address sender,
+ address recipient,
+ uint256 amount
+ ) external returns (bool);
+}
+
+
+// File contracts/modules/misc/Context.sol
+
+pragma solidity ^0.8.0;
+
+abstract contract Context {
+ function _msgSender() internal view virtual returns (address) {
+ return msg.sender;
+ }
+
+ function _msgData() internal view virtual returns (bytes calldata) {
+ return msg.data;
+ }
+}
+
+
+// File contracts/modules/pause/Pausable.sol
+
+pragma solidity ^0.8.0;
+
+
+abstract contract Pausable is IPauseable, Context {
+
+ bool private _paused;
+
+ constructor() {
+ _paused = false;
+ }
+
+ function paused() public view override virtual returns (bool) {
+ return _paused;
+ }
+
+ modifier whenNotPaused() {
+ require(!paused(), "P");
+ _;
+ }
+
+ modifier whenPaused() {
+ require(paused(), "NP");
+ _;
+ }
+
+ function _pause() internal virtual whenNotPaused {
+ _paused = true;
+ emit Paused(_msgSender());
+ }
+
+ function _unpause() internal virtual whenPaused {
+ _paused = false;
+ emit Unpaused(_msgSender());
+ }
+}
+
+
+// File contracts/modules/committee/KAP20Committee.sol
+
+pragma solidity ^0.8.0;
+
+abstract contract KAP20Committee is IKAP20Committee {
+
+ address public override committee;
+
+ modifier onlyCommittee() {
+ require(msg.sender == committee, "Restricted only committee");
+ _;
+ }
+
+ constructor(address committee_) {
+ committee = committee_;
+ }
+
+ function _setCommittee(address _committee) internal {
+ address oldCommittee = _committee;
+ committee = _committee;
+ emit SetCommittee(oldCommittee, committee);
+ }
+
+}
+
+
+// File contracts/modules/admin/interfaces/IAdminProjectRouter.sol
+
+pragma solidity >=0.5.0;
+
+interface IAdminProjectRouter {
+ function isSuperAdmin(address _addr, string calldata _project) external view returns (bool);
+
+ function isAdmin(address _addr, string calldata _project) external view returns (bool);
+}
+
+
+// File contracts/modules/admin/Authorization.sol
+
+pragma solidity >=0.5.0;
+
+abstract contract Authorization {
+ IAdminProjectRouter public adminRouter;
+ string public constant PROJECT = "yuemmai";
+
+ modifier onlySuperAdmin() {
+ require(
+ adminRouter.isSuperAdmin(msg.sender, PROJECT),
+ "Restricted only super admin"
+ );
+ _;
+ }
+
+ modifier onlyAdmin() {
+ require(
+ adminRouter.isAdmin(msg.sender, PROJECT),
+ "Restricted only admin"
+ );
+ _;
+ }
+
+ modifier onlySuperAdminOrAdmin() {
+ require(
+ adminRouter.isSuperAdmin(msg.sender, PROJECT) ||
+ adminRouter.isAdmin(msg.sender, PROJECT),
+ "Restricted only super admin or admin"
+ );
+ _;
+ }
+
+ constructor(address adminRouter_) {
+ adminRouter = IAdminProjectRouter(adminRouter_);
+ }
+
+ function setAdmin(address _adminRouter) external onlySuperAdmin {
+ adminRouter = IAdminProjectRouter(_adminRouter);
+ }
+}
+
+
+// File contracts/modules/kyc/KYCHandler.sol
+
+pragma solidity ^0.8.0;
+
+
+abstract contract KYCHandler is IKAP20KYC {
+ IKYCBitkubChain public override kyc;
+
+ uint256 public override acceptedKycLevel;
+ bool public override isActivatedOnlyKycAddress;
+
+ constructor(address kyc_, uint256 acceptedKycLevel_) {
+ kyc = IKYCBitkubChain(kyc_);
+ acceptedKycLevel = acceptedKycLevel_;
+ }
+
+ function _activateOnlyKycAddress() internal virtual {
+ isActivatedOnlyKycAddress = true;
+ emit ActivateOnlyKYCAddress();
+ }
+
+ function _setKYC(IKYCBitkubChain _kyc) internal virtual {
+ IKYCBitkubChain oldKyc = kyc;
+ kyc = _kyc;
+ emit SetKYC(address(oldKyc), address(kyc));
+ }
+
+ function _setAcceptedKycLevel(uint256 _kycLevel) internal virtual {
+ uint256 oldKycLevel = acceptedKycLevel;
+ acceptedKycLevel = _kycLevel;
+ emit SetAccecptedKycLevel(oldKycLevel, acceptedKycLevel);
+ }
+}
+
+
+// File contracts/modules/blacklist/Blacklist.sol
+
+pragma solidity ^0.8.0;
+
+abstract contract Blacklist is IBlacklist {
+ mapping(address => bool) public override blacklist;
+
+ modifier notInBlacklist(address account) {
+ require(!blacklist[account], "Address is in blacklist");
+ _;
+ }
+
+ modifier inBlacklist(address account) {
+ require(blacklist[account], "Address is not in blacklist");
+ _;
+ }
+
+ function _addBlacklist(address account) internal virtual notInBlacklist(account) {
+ blacklist[account] = true;
+ emit AddBlacklist(account, msg.sender);
+ }
+
+ function _revokeBlacklist(address account) internal virtual inBlacklist(account) {
+ blacklist[account] = false;
+ emit RevokeBlacklist(account, msg.sender);
+ }
+}
+
+
+// File contracts/modules/kap20/KAP20.sol
+
+pragma solidity ^0.8.0;
+
+
+
+
+
+
+contract KAP20 is IKAP20, IKToken, Pausable, KAP20Committee, Authorization, KYCHandler, Blacklist {
+
+ mapping(address => uint256) _balances;
+
+ mapping(address => mapping(address => uint256)) internal _allowance;
+
+ uint256 public override totalSupply;
+
+ string public override name;
+ string public override symbol;
+ uint8 public override decimals;
+
+ constructor(
+ string memory _name,
+ string memory _symbol,
+ uint8 _decimals,
+ address committee_,
+ address adminRouter_,
+ address kyc_,
+ uint256 acceptedKycLevel_
+ ) KAP20Committee(committee_) Authorization(adminRouter_) KYCHandler(kyc_, acceptedKycLevel_) {
+ name = _name;
+ symbol = _symbol;
+ decimals = _decimals;
+ }
+
+ function balanceOf(address account) public view virtual override returns (uint256) {
+ return _balances[account];
+ }
+
+ function transfer(address recipient, uint256 amount) public virtual override whenNotPaused notInBlacklist(msg.sender) returns (bool) {
+ _transfer(msg.sender, recipient, amount);
+ return true;
+ }
+
+ function allowance(address owner, address spender) public view virtual override returns (uint256) {
+ return _allowance[owner][spender];
+ }
+
+ function approve(address spender, uint256 amount) public virtual override returns (bool) {
+ _approve(msg.sender, spender, amount);
+ return true;
+ }
+
+ function transferFrom(
+ address sender,
+ address recipient,
+ uint256 amount
+ ) public virtual override whenNotPaused notInBlacklist(sender) returns (bool) {
+ _transfer(sender, recipient, amount);
+
+ uint256 currentAllowance = _allowance[sender][msg.sender];
+ require(currentAllowance >= amount, "KAP20: transfer amount exceeds allowance");
+ unchecked { _approve(sender, msg.sender, currentAllowance - amount); }
+
+ return true;
+ }
+
+ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
+ _approve(msg.sender, spender, _allowance[msg.sender][spender] + addedValue);
+ return true;
+ }
+
+ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
+ uint256 currentAllowance = _allowance[msg.sender][spender];
+ require(currentAllowance >= subtractedValue, "KAP20: decreased allowance below zero");
+ unchecked { _approve(msg.sender, spender, currentAllowance - subtractedValue); }
+
+ return true;
+ }
+
+ function _transfer(
+ address sender,
+ address recipient,
+ uint256 amount
+ ) internal virtual {
+ require(sender != address(0), "KAP20: transfer from the zero address");
+ require(recipient != address(0), "KAP20: transfer to the zero address");
+
+ uint256 senderBalance = _balances[sender];
+ require(senderBalance >= amount, "KAP20: transfer amount exceeds balance");
+ unchecked { _balances[sender] = senderBalance - amount; }
+ _balances[recipient] += amount;
+
+ emit Transfer(sender, recipient, amount);
+ }
+
+ function _mint(address account, uint256 amount) internal virtual {
+ require(account != address(0), "KAP20: mint to the zero address");
+
+ totalSupply += amount;
+ _balances[account] += amount;
+ emit Transfer(address(0), account, amount);
+ }
+
+ function _burn(address account, uint256 amount) internal virtual {
+ require(account != address(0), "KAP20: burn from the zero address");
+
+ uint256 accountBalance = _balances[account];
+ require(accountBalance >= amount, "KAP20: burn amount exceeds balance");
+ unchecked { _balances[account] = accountBalance - amount; }
+ totalSupply -= amount;
+
+ emit Transfer(account, address(0), amount);
+ }
+
+ function _approve(
+ address owner,
+ address spender,
+ uint256 amount
+ ) internal virtual {
+ require(owner != address(0), "KAP20: approve from the zero address");
+ require(spender != address(0), "KAP20: approve to the zero address");
+
+ _allowance[owner][spender] = amount;
+ emit Approval(owner, spender, amount);
+ }
+
+ function adminTransfer(
+ address sender,
+ address recipient,
+ uint256 amount
+ ) public virtual override onlyCommittee returns (bool) {
+ require(_balances[sender] >= amount, "KAP20: transfer amount exceed balance");
+ require(recipient != address(0), "KAP20: transfer to zero address");
+ _balances[sender] -= amount;
+ _balances[recipient] += amount;
+ emit Transfer(sender, recipient, amount);
+
+ return true;
+ }
+
+ function internalTransfer(
+ address sender,
+ address recipient,
+ uint256 amount
+ ) external override whenNotPaused onlySuperAdmin returns (bool) {
+ require(
+ kyc.kycsLevel(sender) >= acceptedKycLevel && kyc.kycsLevel(recipient) >= acceptedKycLevel,
+ "Only internal purpose"
+ );
+
+ _transfer(sender, recipient, amount);
+ return true;
+ }
+
+ function externalTransfer(
+ address sender,
+ address recipient,
+ uint256 amount
+ ) external override whenNotPaused onlySuperAdmin returns (bool) {
+ require(kyc.kycsLevel(sender) >= acceptedKycLevel, "Only internal purpose");
+
+ _transfer(sender, recipient, amount);
+ return true;
+ }
+
+ function activateOnlyKycAddress() public override onlyCommittee {
+ _activateOnlyKycAddress();
+ }
+
+ function setKYC(address _kyc) public override onlyCommittee {
+ _setKYC(IKYCBitkubChain(_kyc));
+ }
+
+ function setAcceptedKycLevel(uint256 _kycLevel) public override onlyCommittee {
+ _setAcceptedKycLevel(_kycLevel);
+ }
+
+ function setCommittee(address _committee) external override onlyCommittee {
+ _setCommittee(_committee);
+ }
+
+ function pause() external override onlyCommittee {
+ _pause();
+ }
+
+ function unpause() external override onlyCommittee {
+ _unpause();
+ }
+
+ function addBlacklist(address account) external override onlyCommittee {
+ _addBlacklist(account);
+ }
+
+ function revokeBlacklist(address account) external override onlyCommittee {
+ _revokeBlacklist(account);
+ }
+
+}
+
+
+// File contracts/YESToken.sol
+
+pragma solidity ^0.8.0;
+
+contract YESToken is KAP20 {
+ constructor(
+ uint256 totalSupply_,
+ address committee_,
+ address adminRouter_,
+ address kyc_,
+ uint256 acceptedKycLevel_
+ )
+ KAP20(
+ "YES Token",
+ "YES",
+ 18,
+ committee_,
+ adminRouter_,
+ kyc_,
+ acceptedKycLevel_
+ )
+ {
+ _mint(msg.sender, totalSupply_);
+ }
+}
\ No newline at end of file
diff --git a/apps/explorer/test/support/fixture/smart_contract/issue_with_constructor_args.sol b/apps/explorer/test/support/fixture/smart_contract/issue_with_constructor_args.sol
new file mode 100644
index 000000000000..58ab19969526
--- /dev/null
+++ b/apps/explorer/test/support/fixture/smart_contract/issue_with_constructor_args.sol
@@ -0,0 +1,573 @@
+// SPDX-License-Identifier: MIT
+
+pragma solidity ^0.8.2;
+
+
+
+/**
+ * @dev Library for reading and writing primitive types to specific storage slots.
+ *
+ * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
+ * This library helps with reading and writing to such slots without the need for inline assembly.
+ *
+ * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
+ *
+ * Example usage to set ERC1967 implementation slot:
+ * ```
+ * contract ERC1967 {
+ * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
+ *
+ * function _getImplementation() internal view returns (address) {
+ * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
+ * }
+ *
+ * function _setImplementation(address newImplementation) internal {
+ * require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
+ * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
+ * }
+ * }
+ * ```
+ *
+ * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
+ */
+library StorageSlot {
+ struct AddressSlot {
+ address value;
+ }
+
+ struct BooleanSlot {
+ bool value;
+ }
+
+ struct Bytes32Slot {
+ bytes32 value;
+ }
+
+ struct Uint256Slot {
+ uint256 value;
+ }
+
+ /**
+ * @dev Returns an `AddressSlot` with member `value` located at `slot`.
+ */
+ function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
+ assembly {
+ r.slot := slot
+ }
+ }
+
+ /**
+ * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
+ */
+ function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
+ assembly {
+ r.slot := slot
+ }
+ }
+
+ /**
+ * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
+ */
+ function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
+ assembly {
+ r.slot := slot
+ }
+ }
+
+ /**
+ * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
+ */
+ function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
+ assembly {
+ r.slot := slot
+ }
+ }
+}
+
+library Address {
+ /**
+ * @dev Returns true if `account` is a contract.
+ *
+ * [IMPORTANT]
+ * ====
+ * It is unsafe to assume that an address for which this function returns
+ * false is an externally-owned account (EOA) and not a contract.
+ *
+ * Among others, `isContract` will return false for the following
+ * types of addresses:
+ *
+ * - an externally-owned account
+ * - a contract in construction
+ * - an address where a contract will be created
+ * - an address where a contract lived, but was destroyed
+ * ====
+ */
+ function isContract(address account) internal view returns (bool) {
+ // This method relies on extcodesize, which returns 0 for contracts in
+ // construction, since the code is only stored at the end of the
+ // constructor execution.
+
+ uint256 size;
+ // solhint-disable-next-line no-inline-assembly
+ assembly { size := extcodesize(account) }
+ return size > 0;
+ }
+
+ /**
+ * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
+ * `recipient`, forwarding all available gas and reverting on errors.
+ *
+ * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
+ * of certain opcodes, possibly making contracts go over the 2300 gas limit
+ * imposed by `transfer`, making them unable to receive funds via
+ * `transfer`. {sendValue} removes this limitation.
+ *
+ * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
+ *
+ * IMPORTANT: because control is transferred to `recipient`, care must be
+ * taken to not create reentrancy vulnerabilities. Consider using
+ * {ReentrancyGuard} or the
+ * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
+ */
+ function sendValue(address payable recipient, uint256 amount) internal {
+ require(address(this).balance >= amount, "Address: insufficient balance");
+
+ // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
+ (bool success, ) = recipient.call{ value: amount }("");
+ require(success, "Address: unable to send value, recipient may have reverted");
+ }
+
+ /**
+ * @dev Performs a Solidity function call using a low level `call`. A
+ * plain`call` is an unsafe replacement for a function call: use this
+ * function instead.
+ *
+ * If `target` reverts with a revert reason, it is bubbled up by this
+ * function (like regular Solidity function calls).
+ *
+ * Returns the raw returned data. To convert to the expected return value,
+ * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
+ *
+ * Requirements:
+ *
+ * - `target` must be a contract.
+ * - calling `target` with `data` must not revert.
+ *
+ * _Available since v3.1._
+ */
+ function functionCall(address target, bytes memory data) internal returns (bytes memory) {
+ return functionCall(target, data, "Address: low-level call failed");
+ }
+
+ /**
+ * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
+ * `errorMessage` as a fallback revert reason when `target` reverts.
+ *
+ * _Available since v3.1._
+ */
+ function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
+ return functionCallWithValue(target, data, 0, errorMessage);
+ }
+
+ /**
+ * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
+ * but also transferring `value` wei to `target`.
+ *
+ * Requirements:
+ *
+ * - the calling contract must have an ETH balance of at least `value`.
+ * - the called Solidity function must be `payable`.
+ *
+ * _Available since v3.1._
+ */
+ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
+ return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
+ }
+
+ /**
+ * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
+ * with `errorMessage` as a fallback revert reason when `target` reverts.
+ *
+ * _Available since v3.1._
+ */
+ function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
+ require(address(this).balance >= value, "Address: insufficient balance for call");
+ require(isContract(target), "Address: call to non-contract");
+
+ // solhint-disable-next-line avoid-low-level-calls
+ (bool success, bytes memory returndata) = target.call{ value: value }(data);
+ return _verifyCallResult(success, returndata, errorMessage);
+ }
+
+ /**
+ * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
+ * but performing a static call.
+ *
+ * _Available since v3.3._
+ */
+ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
+ return functionStaticCall(target, data, "Address: low-level static call failed");
+ }
+
+ /**
+ * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
+ * but performing a static call.
+ *
+ * _Available since v3.3._
+ */
+ function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
+ require(isContract(target), "Address: static call to non-contract");
+
+ // solhint-disable-next-line avoid-low-level-calls
+ (bool success, bytes memory returndata) = target.staticcall(data);
+ return _verifyCallResult(success, returndata, errorMessage);
+ }
+
+ /**
+ * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
+ * but performing a delegate call.
+ *
+ * _Available since v3.4._
+ */
+ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
+ return functionDelegateCall(target, data, "Address: low-level delegate call failed");
+ }
+
+ /**
+ * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
+ * but performing a delegate call.
+ *
+ * _Available since v3.4._
+ */
+ function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
+ require(isContract(target), "Address: delegate call to non-contract");
+
+ // solhint-disable-next-line avoid-low-level-calls
+ (bool success, bytes memory returndata) = target.delegatecall(data);
+ return _verifyCallResult(success, returndata, errorMessage);
+ }
+
+ function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
+ if (success) {
+ return returndata;
+ } else {
+ // Look for revert reason and bubble it up if present
+ if (returndata.length > 0) {
+ // The easiest way to bubble the revert reason is using memory via assembly
+
+ // solhint-disable-next-line no-inline-assembly
+ assembly {
+ let returndata_size := mload(returndata)
+ revert(add(32, returndata), returndata_size)
+ }
+ } else {
+ revert(errorMessage);
+ }
+ }
+ }
+}
+
+/**
+ * @dev This is the interface that {BeaconProxy} expects of its beacon.
+ */
+interface IBeacon {
+ /**
+ * @dev Must return an address that can be used as a delegate call target.
+ *
+ * {BeaconProxy} will check that this address is a contract.
+ */
+ function implementation() external view returns (address);
+}
+/**
+ * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
+ * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
+ * be specified by overriding the virtual {_implementation} function.
+ *
+ * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
+ * different contract through the {_delegate} function.
+ *
+ * The success and return data of the delegated call will be returned back to the caller of the proxy.
+ */
+abstract contract Proxy {
+ /**
+ * @dev Delegates the current call to `implementation`.
+ *
+ * This function does not return to its internall call site, it will return directly to the external caller.
+ */
+ function _delegate(address implementation) internal virtual {
+ // solhint-disable-next-line no-inline-assembly
+ assembly {
+ // Copy msg.data. We take full control of memory in this inline assembly
+ // block because it will not return to Solidity code. We overwrite the
+ // Solidity scratch pad at memory position 0.
+ calldatacopy(0, 0, calldatasize())
+
+ // Call the implementation.
+ // out and outsize are 0 because we don't know the size yet.
+ let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
+
+ // Copy the returned data.
+ returndatacopy(0, 0, returndatasize())
+
+ switch result
+ // delegatecall returns 0 on error.
+ case 0 { revert(0, returndatasize()) }
+ default { return(0, returndatasize()) }
+ }
+ }
+
+ /**
+ * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
+ * and {_fallback} should delegate.
+ */
+ function _implementation() internal view virtual returns (address);
+
+ /**
+ * @dev Delegates the current call to the address returned by `_implementation()`.
+ *
+ * This function does not return to its internall call site, it will return directly to the external caller.
+ */
+ function _fallback() internal virtual {
+ _beforeFallback();
+ _delegate(_implementation());
+ }
+
+ /**
+ * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
+ * function in the contract matches the call data.
+ */
+ fallback () external payable virtual {
+ _fallback();
+ }
+
+ /**
+ * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
+ * is empty.
+ */
+ receive () external payable virtual {
+ _fallback();
+ }
+
+ /**
+ * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
+ * call, or as part of the Solidity `fallback` or `receive` functions.
+ *
+ * If overriden should call `super._beforeFallback()`.
+ */
+ function _beforeFallback() internal virtual {
+ }
+}
+
+
+
+
+/**
+ * @dev This abstract contract provides getters and event emitting update functions for
+ * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
+ *
+ * _Available since v4.1._
+ *
+ * @custom:oz-upgrades-unsafe-allow delegatecall
+ */
+abstract contract ERC1967Upgrade {
+ // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
+ bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
+
+ /**
+ * @dev Storage slot with the address of the current implementation.
+ * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
+ * validated in the constructor.
+ */
+ bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
+
+ /**
+ * @dev Emitted when the implementation is upgraded.
+ */
+ event Upgraded(address indexed implementation);
+
+ /**
+ * @dev Returns the current implementation address.
+ */
+ function _getImplementation() internal view returns (address) {
+ return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
+ }
+
+ /**
+ * @dev Stores a new address in the EIP1967 implementation slot.
+ */
+ function _setImplementation(address newImplementation) private {
+ require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
+ StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
+ }
+
+ /**
+ * @dev Perform implementation upgrade
+ *
+ * Emits an {Upgraded} event.
+ */
+ function _upgradeTo(address newImplementation) internal {
+ _setImplementation(newImplementation);
+ emit Upgraded(newImplementation);
+ }
+
+ /**
+ * @dev Perform implementation upgrade with additional setup call.
+ *
+ * Emits an {Upgraded} event.
+ */
+ function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {
+ _setImplementation(newImplementation);
+ emit Upgraded(newImplementation);
+ if (data.length > 0 || forceCall) {
+ Address.functionDelegateCall(newImplementation, data);
+ }
+ }
+
+ /**
+ * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
+ *
+ * Emits an {Upgraded} event.
+ */
+ function _upgradeToAndCallSecure(address newImplementation, bytes memory data, bool forceCall) internal {
+ address oldImplementation = _getImplementation();
+
+ // Initial upgrade and setup call
+ _setImplementation(newImplementation);
+ if (data.length > 0 || forceCall) {
+ Address.functionDelegateCall(newImplementation, data);
+ }
+
+ // Perform rollback test if not already in progress
+ StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT);
+ if (!rollbackTesting.value) {
+ // Trigger rollback using upgradeTo from the new implementation
+ rollbackTesting.value = true;
+ Address.functionDelegateCall(
+ newImplementation,
+ abi.encodeWithSignature(
+ "upgradeTo(address)",
+ oldImplementation
+ )
+ );
+ rollbackTesting.value = false;
+ // Check rollback was effective
+ require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");
+ // Finally reset to the new implementation and log the upgrade
+ _setImplementation(newImplementation);
+ emit Upgraded(newImplementation);
+ }
+ }
+
+ /**
+ * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
+ * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
+ *
+ * Emits a {BeaconUpgraded} event.
+ */
+ function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {
+ _setBeacon(newBeacon);
+ emit BeaconUpgraded(newBeacon);
+ if (data.length > 0 || forceCall) {
+ Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
+ }
+ }
+
+ /**
+ * @dev Storage slot with the admin of the contract.
+ * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
+ * validated in the constructor.
+ */
+ bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
+
+ /**
+ * @dev Emitted when the admin account has changed.
+ */
+ event AdminChanged(address previousAdmin, address newAdmin);
+
+ /**
+ * @dev Returns the current admin.
+ */
+ function _getAdmin() internal view returns (address) {
+ return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
+ }
+
+ /**
+ * @dev Stores a new address in the EIP1967 admin slot.
+ */
+ function _setAdmin(address newAdmin) private {
+ require(newAdmin != address(0), "ERC1967: new admin is the zero address");
+ StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
+ }
+
+ /**
+ * @dev Changes the admin of the proxy.
+ *
+ * Emits an {AdminChanged} event.
+ */
+ function _changeAdmin(address newAdmin) internal {
+ emit AdminChanged(_getAdmin(), newAdmin);
+ _setAdmin(newAdmin);
+ }
+
+ /**
+ * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
+ * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
+ */
+ bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
+
+ /**
+ * @dev Emitted when the beacon is upgraded.
+ */
+ event BeaconUpgraded(address indexed beacon);
+
+ /**
+ * @dev Returns the current beacon.
+ */
+ function _getBeacon() internal view returns (address) {
+ return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
+ }
+
+ /**
+ * @dev Stores a new beacon in the EIP1967 beacon slot.
+ */
+ function _setBeacon(address newBeacon) private {
+ require(
+ Address.isContract(newBeacon),
+ "ERC1967: new beacon is not a contract"
+ );
+ require(
+ Address.isContract(IBeacon(newBeacon).implementation()),
+ "ERC1967: beacon implementation is not a contract"
+ );
+ StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
+ }
+}
+
+
+
+
+/**
+ * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
+ * implementation address that can be changed. This address is stored in storage in the location specified by
+ * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
+ * implementation behind the proxy.
+ */
+ contract ERC1967Proxy is Proxy, ERC1967Upgrade {
+ /**
+ * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
+ *
+ * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
+ * function call, and allows initializating the storage of the proxy like a Solidity constructor.
+ */
+ constructor(address _logic, bytes memory _data) payable {
+ assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
+ _upgradeToAndCall(_logic, _data, false);
+ }
+
+ /**
+ * @dev Returns the current implementation address.
+ */
+ function _implementation() internal view virtual override returns (address impl) {
+ return ERC1967Upgrade._getImplementation();
+ }
+}
\ No newline at end of file
diff --git a/apps/explorer/test/test_helper.exs b/apps/explorer/test/test_helper.exs
index d45d8d212b5d..50847130f8ba 100644
--- a/apps/explorer/test/test_helper.exs
+++ b/apps/explorer/test/test_helper.exs
@@ -11,7 +11,7 @@ ExUnit.start()
{:ok, _} = Application.ensure_all_started(:ex_machina)
-Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo, :manual)
+Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo, :auto)
Mox.defmock(Explorer.ExchangeRates.Source.TestSource, for: Explorer.ExchangeRates.Source)
Mox.defmock(Explorer.KnownTokens.Source.TestSource, for: Explorer.KnownTokens.Source)
diff --git a/apps/indexer/config/config.exs b/apps/indexer/config/config.exs
index b20a028fdb35..1719b956bf5c 100644
--- a/apps/indexer/config/config.exs
+++ b/apps/indexer/config/config.exs
@@ -36,10 +36,14 @@ config :indexer,
# bytes
memory_limit: 1 <<< 30,
first_block: System.get_env("FIRST_BLOCK") || "",
- last_block: System.get_env("LAST_BLOCK") || ""
+ last_block: System.get_env("LAST_BLOCK") || "",
+ trace_first_block: System.get_env("TRACE_FIRST_BLOCK") || "",
+ trace_last_block: System.get_env("TRACE_LAST_BLOCK") || ""
config :indexer, Indexer.Fetcher.PendingTransaction.Supervisor,
- disabled?: System.get_env("ETHEREUM_JSONRPC_VARIANT") == "besu"
+ disabled?:
+ System.get_env("ETHEREUM_JSONRPC_VARIANT") == "besu" ||
+ System.get_env("INDEXER_DISABLE_PENDING_TRANSACTIONS_FETCHER", "false") == "true"
token_balance_on_demand_fetcher_threshold =
if System.get_env("TOKEN_BALANCE_ON_DEMAND_FETCHER_THRESHOLD_MINUTES") do
@@ -56,8 +60,13 @@ config :indexer, Indexer.Fetcher.TokenBalanceOnDemand, threshold: token_balance_
# config :indexer, Indexer.Fetcher.ReplacedTransaction.Supervisor, disabled?: true
if System.get_env("POS_STAKING_CONTRACT") do
config :indexer, Indexer.Fetcher.BlockReward.Supervisor, disabled?: true
+else
+ config :indexer, Indexer.Fetcher.BlockReward.Supervisor, disabled?: false
end
+config :indexer, Indexer.Fetcher.InternalTransaction.Supervisor,
+ disabled?: System.get_env("INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER", "false") == "true"
+
config :indexer, Indexer.Supervisor, enabled: System.get_env("DISABLE_INDEXER") != "true"
config :indexer, Indexer.Tracer,
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/dev/arbitrum.exs b/apps/indexer/config/dev/arbitrum.exs
index e035ab01f524..f56213ae012d 100644
--- a/apps/indexer/config/dev/arbitrum.exs
+++ b/apps/indexer/config/dev/arbitrum.exs
@@ -10,7 +10,7 @@ config :indexer,
),
transport_options: [
http: EthereumJSONRPC.HTTP.HTTPoison,
- url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "http://localhost:7545",
+ url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "http://localhost:8545",
http_options: [recv_timeout: :timer.minutes(1), timeout: :timer.minutes(1), hackney: [pool: :ethereum_jsonrpc]]
],
variant: EthereumJSONRPC.Arbitrum
diff --git a/apps/indexer/config/dev/geth.exs b/apps/indexer/config/dev/geth.exs
index a3d39c6ea28b..3082fbebf797 100644
--- a/apps/indexer/config/dev/geth.exs
+++ b/apps/indexer/config/dev/geth.exs
@@ -10,7 +10,7 @@ config :indexer,
),
transport_options: [
http: EthereumJSONRPC.HTTP.HTTPoison,
- url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL"),
+ url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "http://localhost:8545",
http_options: [recv_timeout: :timer.minutes(1), timeout: :timer.minutes(1), hackney: [pool: :ethereum_jsonrpc]]
],
variant: EthereumJSONRPC.Geth
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/config/prod/arbitrum.exs b/apps/indexer/config/prod/arbitrum.exs
index b9f23bb45dd3..dffc501d3597 100644
--- a/apps/indexer/config/prod/arbitrum.exs
+++ b/apps/indexer/config/prod/arbitrum.exs
@@ -10,7 +10,7 @@ config :indexer,
),
transport_options: [
http: EthereumJSONRPC.HTTP.HTTPoison,
- url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "http://localhost:7545",
+ url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "http://localhost:8545",
http_options: [recv_timeout: :timer.minutes(5), timeout: :timer.minutes(5), hackney: [pool: :ethereum_jsonrpc]]
],
variant: EthereumJSONRPC.Arbitrum
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 d7f4a3dfd578..e0cf9f196a29 100644
--- a/apps/indexer/lib/indexer/block/catchup/bound_interval_supervisor.ex
+++ b/apps/indexer/lib/indexer/block/catchup/bound_interval_supervisor.ex
@@ -262,6 +262,67 @@ 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(
+ {ref, {:error, :econnrefused}},
+ %__MODULE__{
+ fetcher: %Catchup.Fetcher{
+ block_fetcher: %Block.Fetcher{
+ json_rpc_named_arguments: [
+ transport: _,
+ transport_options: [http: _, url: url, http_options: _],
+ variant: _
+ ]
+ }
+ },
+ task: %Task{ref: ref}
+ } = state
+ ) do
+ Logger.error(fn ->
+ "Catchup index stream exited because the archive node endpoint at #{url} is unavailable. Restarting"
+ end)
+
+ send(self(), :catchup_index)
+
+ {:noreply, %__MODULE__{state | task: nil}}
+ end
+
+ def handle_info(
+ {_ref, {:error, :econnrefused}},
+ %__MODULE__{
+ fetcher: %Catchup.Fetcher{
+ block_fetcher: %Block.Fetcher{
+ json_rpc_named_arguments: [
+ transport: _,
+ transport_options: [http: _, url: url, http_options: _],
+ variant: _
+ ]
+ }
+ },
+ task: nil
+ } = state
+ ) do
+ Logger.error(fn ->
+ "Catchup index stream exited because the archive node endpoint at #{url} is unavailable. Restarting"
+ end)
+
+ 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
@@ -272,4 +333,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 1d3975e07e3a..012f80325d25 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/empty_blocks_sanitizer.ex b/apps/indexer/lib/indexer/empty_blocks_sanitizer.ex
new file mode 100644
index 000000000000..a1e83bb61712
--- /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 lose 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/coin_balance.ex b/apps/indexer/lib/indexer/fetcher/coin_balance.ex
index 3618136f491b..02ddbd29176d 100644
--- a/apps/indexer/lib/indexer/fetcher/coin_balance.ex
+++ b/apps/indexer/lib/indexer/fetcher/coin_balance.ex
@@ -79,7 +79,7 @@ defmodule Indexer.Fetcher.CoinBalance do
unique_filtered_entries =
Enum.filter(unique_entries, fn {_hash, block_number} ->
- block_number >= first_block_to_index()
+ block_number >= EthereumJSONRPC.first_block_to_fetch(:trace_first_block)
end)
unique_entry_count = Enum.count(unique_filtered_entries)
@@ -106,15 +106,6 @@ defmodule Indexer.Fetcher.CoinBalance do
end
end
- defp first_block_to_index do
- string_value = Application.get_env(:indexer, :first_block)
-
- case Integer.parse(string_value) do
- {integer, ""} -> integer
- _ -> 0
- end
- end
-
defp entry_to_params({address_hash_bytes, block_number}) when is_integer(block_number) do
{:ok, address_hash} = Hash.Address.cast(address_hash_bytes)
%{block_quantity: integer_to_quantity(block_number), hash_data: to_string(address_hash)}
diff --git a/apps/indexer/lib/indexer/fetcher/internal_transaction.ex b/apps/indexer/lib/indexer/fetcher/internal_transaction.ex
index a2be833459da..6a112dfeb68d 100644
--- a/apps/indexer/lib/indexer/fetcher/internal_transaction.ex
+++ b/apps/indexer/lib/indexer/fetcher/internal_transaction.ex
@@ -16,6 +16,7 @@ defmodule Indexer.Fetcher.InternalTransaction do
alias Explorer.Chain.Block
alias Explorer.Chain.Cache.{Accounts, Blocks}
alias Indexer.{BufferedTask, Tracer}
+ alias Indexer.Fetcher.InternalTransaction.Supervisor, as: InternalTransactionSupervisor
alias Indexer.Transform.Addresses
@behaviour BufferedTask
@@ -49,7 +50,11 @@ defmodule Indexer.Fetcher.InternalTransaction do
"""
@spec async_fetch([Block.block_number()]) :: :ok
def async_fetch(block_numbers, timeout \\ 5000) when is_list(block_numbers) do
- BufferedTask.buffer(__MODULE__, block_numbers, timeout)
+ if InternalTransactionSupervisor.disabled?() do
+ :ok
+ else
+ BufferedTask.buffer(__MODULE__, block_numbers, timeout)
+ end
end
@doc false
diff --git a/apps/indexer/lib/indexer/fetcher/pending_transaction.ex b/apps/indexer/lib/indexer/fetcher/pending_transaction.ex
index 59d5e945a289..a5da4b96fb6a 100644
--- a/apps/indexer/lib/indexer/fetcher/pending_transaction.ex
+++ b/apps/indexer/lib/indexer/fetcher/pending_transaction.ex
@@ -136,6 +136,16 @@ defmodule Indexer.Fetcher.PendingTransaction do
:ok
+ {:error, :etimedout} ->
+ Logger.error("timeout")
+
+ :ok
+
+ {:error, :econnrefused} ->
+ Logger.error("connection_refused")
+
+ :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/fetcher/token_balance_on_demand.ex b/apps/indexer/lib/indexer/fetcher/token_balance_on_demand.ex
index 5e0367f578b3..620586c0ad0e 100644
--- a/apps/indexer/lib/indexer/fetcher/token_balance_on_demand.ex
+++ b/apps/indexer/lib/indexer/fetcher/token_balance_on_demand.ex
@@ -35,7 +35,7 @@ defmodule Indexer.Fetcher.TokenBalanceOnDemand do
when not is_nil(address_hash) do
stale_current_token_balances =
current_token_balances
- |> Enum.filter(fn {current_token_balance, _} -> current_token_balance.block_number < stale_balance_window end)
+ |> Enum.filter(fn {current_token_balance, _, _} -> current_token_balance.block_number < stale_balance_window end)
if Enum.count(stale_current_token_balances) > 0 do
fetch_and_update(latest_block_number, address_hash, stale_current_token_balances)
@@ -49,11 +49,10 @@ defmodule Indexer.Fetcher.TokenBalanceOnDemand do
defp fetch_and_update(block_number, address_hash, stale_current_token_balances) do
current_token_balances_update_params =
stale_current_token_balances
- |> Enum.map(fn {stale_current_token_balance, _} ->
+ |> Enum.map(fn {stale_current_token_balance, _, token} ->
stale_current_token_balances_to_fetch = [
%{
- token_contract_address_hash:
- "0x" <> Base.encode16(stale_current_token_balance.token_contract_address_hash.bytes),
+ token_contract_address_hash: "0x" <> Base.encode16(token.contract_address_hash.bytes),
address_hash: "0x" <> Base.encode16(address_hash.bytes),
block_number: block_number
}
@@ -65,8 +64,8 @@ defmodule Indexer.Fetcher.TokenBalanceOnDemand do
if updated_balance do
%{}
|> Map.put(:address_hash, stale_current_token_balance.address_hash)
- |> Map.put(:token_contract_address_hash, stale_current_token_balance.token_contract_address_hash)
- |> Map.put(:token_type, stale_current_token_balance.token.type)
+ |> Map.put(:token_contract_address_hash, token.contract_address_hash)
+ |> Map.put(:token_type, token.type)
|> Map.put(:block_number, block_number)
|> Map.put(:value, Decimal.new(updated_balance))
|> Map.put(:value_fetched_at, DateTime.utc_now())
diff --git a/apps/indexer/lib/indexer/pending_transactions_sanitizer.ex b/apps/indexer/lib/indexer/pending_transactions_sanitizer.ex
index 45aa3d201d13..9a8148dff62f 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 61174143afed..4b1d127b6cd8 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,
@@ -126,6 +127,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 ff87ba7d57db..ab6bb02fee8d 100644
--- a/apps/indexer/lib/indexer/transform/address_token_balances.ex
+++ b/apps/indexer/lib/indexer/transform/address_token_balances.ex
@@ -16,14 +16,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
@@ -31,13 +42,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 ade12ef8932a..22638dbc130a 100644
--- a/apps/indexer/lib/indexer/transform/token_transfers.ex
+++ b/apps/indexer/lib/indexer/transform/token_transfers.ex
@@ -18,12 +18,21 @@ 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())))
|> 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 ->
@@ -32,21 +41,26 @@ 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
- 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],
@@ -72,6 +86,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"
}
@@ -109,7 +124,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}])
@@ -157,6 +179,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 6c2eccf373ab..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: token_contract_address_hash,
- 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 b38c1d408843..dbea962c321e 100644
--- a/apps/indexer/test/indexer/transform/token_transfers_test.exs
+++ b/apps/indexer/test/indexer/transform/token_transfers_test.exs
@@ -74,6 +74,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,
@@ -131,6 +132,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/config/config.exs b/config/config.exs
index 6631863bcefa..939702dadeb0 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -30,7 +30,9 @@ 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},
+ {LoggerFileBackend, :api}
]
config :logger, :console,
diff --git a/docker-compose/README.md b/docker-compose/README.md
new file mode 100644
index 000000000000..a94c588c4c35
--- /dev/null
+++ b/docker-compose/README.md
@@ -0,0 +1,31 @@
+# Docker-compose configuration
+
+Runs BlockScout locally in Docker container with usage [docker-compose](https://github.com/docker/compose).
+
+## Prerequisites
+- Docker v20.10+
+- Docker-compose 2.x.x+
+- Running Ethereum JSON RPC client
+
+## Building Docker containers from source
+```
+docker-compose up --build
+```
+
+This command uses by-default `docker-compose.yml`, which build the explorer into Docker image and runs 2 Docker containers:
+- one for the database. Postgres 13.x, which will be available at port 7432 on localhost
+- and the BlockScout explorer at http://localhost:4000
+
+## Configs for different Ethereum clients
+Also, the repo contains built-in configs for different clients without need to build the image
+- Ganache: `docker-compose -d -f docker-compose-no-build-ganache.yml up -d`
+- HardHat network: `docker-compose -d -f docker-compose-no-build-hardhat-network.yml up -d`
+- Geth: `docker-compose -d -f docker-compose-no-build-geth.yml up -d`
+- OpenEthereum, Nethermind: `docker-compose -d -f docker-compose-no-build-open-ethereum-nethermind up -d`
+- Running only explorer without DB: `docker-compose -d -f docker-compose-no-build-no-db-container.yml up -d`. In this case, one container is created - for the explorer itself. And it assumes that the DB credentials are provided through `DATABASE_URL` environment variable.
+
+All of the configs assume, that the Ethereum JSON RPC is running at http://localhost:8545.
+
+In order to stop launched containers, run `docker-compose -d -f config_file.yml down`, where replace `config_file.yml` with the file name of the config, which has been launched before.
+
+You can play with the BlockScout environment variables, which are present at `./envs/common-blockscout.env`. The description of the environment variables are available in [the docs](https://docs.blockscout.com/for-developers/information-and-settings/env-variables).
\ No newline at end of file
diff --git a/docker-compose/docker-compose-no-build-ganache.yml b/docker-compose/docker-compose-no-build-ganache.yml
new file mode 100644
index 000000000000..0f080ef2a116
--- /dev/null
+++ b/docker-compose/docker-compose-no-build-ganache.yml
@@ -0,0 +1,37 @@
+version: '3.8'
+
+services:
+ db:
+ image: postgres:13.6
+ restart: always
+ container_name: 'postgres'
+ environment:
+ POSTGRES_PASSWORD: ''
+ POSTGRES_USER: 'postgres'
+ POSTGRES_HOST_AUTH_METHOD: 'trust'
+ ports:
+ - 7432:5432
+
+ blockscout:
+ depends_on:
+ - postgres
+ image: blockscout/blockscout:${DOCKER_TAG:-latest}
+ restart: always
+ container_name: 'blockscout'
+ links:
+ - db:database
+ command: 'mix do ecto.create, ecto.migrate, phx.server'
+ extra_hosts:
+ - 'host.docker.internal:host-gateway'
+ env_file:
+ - ./envs/common-blockscout.env
+ environment:
+ ETHEREUM_JSONRPC_VARIANT: 'ganache'
+ ETHEREUM_JSONRPC_HTTP_URL: http://host.docker.internal:8545/
+ ETHEREUM_JSONRPC_WS_URL: ws://host.docker.internal:8545/
+ INDEXER_DISABLE_PENDING_TRANSACTIONS_FETCHER: true
+ DATABASE_URL: postgresql://postgres:@host.docker.internal:7432/blockscout?ssl=false
+ ECTO_USE_SSL: false
+ ports:
+ - 4000:4000
+
diff --git a/docker-compose/docker-compose-no-build-geth.yml b/docker-compose/docker-compose-no-build-geth.yml
new file mode 100644
index 000000000000..5f4af8e4b6f8
--- /dev/null
+++ b/docker-compose/docker-compose-no-build-geth.yml
@@ -0,0 +1,38 @@
+version: '3.8'
+
+services:
+ db:
+ image: postgres:13.6
+ restart: always
+ container_name: 'postgres'
+ environment:
+ POSTGRES_PASSWORD: ''
+ POSTGRES_USER: 'postgres'
+ POSTGRES_HOST_AUTH_METHOD: 'trust'
+ volumes:
+ - ./postgres-data:/var/lib/postgresql/data
+ ports:
+ - 7432:5432
+
+ blockscout:
+ depends_on:
+ - postgres
+ image: blockscout/blockscout:${DOCKER_TAG:-latest}
+ restart: always
+ container_name: 'blockscout'
+ links:
+ - db:database
+ command: 'mix do ecto.create, ecto.migrate, phx.server'
+ extra_hosts:
+ - 'host.docker.internal:host-gateway'
+ env_file:
+ - ./envs/common-blockscout.env
+ environment:
+ ETHEREUM_JSONRPC_VARIANT: 'geth'
+ BLOCK_TRANSFORMER: 'clique'
+ ETHEREUM_JSONRPC_HTTP_URL: http://host.docker.internal:8545/
+ DATABASE_URL: postgresql://postgres:@host.docker.internal:7432/blockscout?ssl=false
+ ECTO_USE_SSL: false
+ ports:
+ - 4000:4000
+
diff --git a/docker-compose/docker-compose-no-build-hardhat-network.yml b/docker-compose/docker-compose-no-build-hardhat-network.yml
new file mode 100644
index 000000000000..6fefe35657ca
--- /dev/null
+++ b/docker-compose/docker-compose-no-build-hardhat-network.yml
@@ -0,0 +1,37 @@
+version: '3.8'
+
+services:
+ db:
+ image: postgres:13.6
+ restart: always
+ container_name: 'postgres'
+ environment:
+ POSTGRES_PASSWORD: ''
+ POSTGRES_USER: 'postgres'
+ POSTGRES_HOST_AUTH_METHOD: 'trust'
+ ports:
+ - 7432:5432
+
+ blockscout:
+ depends_on:
+ - postgres
+ image: blockscout/blockscout:${DOCKER_TAG:-latest}
+ restart: always
+ container_name: 'blockscout'
+ links:
+ - db:database
+ command: 'mix do ecto.create, ecto.migrate, phx.server'
+ extra_hosts:
+ - 'host.docker.internal:host-gateway'
+ env_file:
+ - ./envs/common-blockscout.env
+ environment:
+ ETHEREUM_JSONRPC_VARIANT: 'geth'
+ ETHEREUM_JSONRPC_HTTP_URL: http://host.docker.internal:8545/
+ ETHEREUM_JSONRPC_WS_URL: ws://host.docker.internal:8545/
+ INDEXER_DISABLE_PENDING_TRANSACTIONS_FETCHER: true
+ DATABASE_URL: postgresql://postgres:@host.docker.internal:7432/blockscout?ssl=false
+ ECTO_USE_SSL: false
+ ports:
+ - 4000:4000
+
diff --git a/docker-compose/docker-compose-no-build-no-db-container.yml b/docker-compose/docker-compose-no-build-no-db-container.yml
new file mode 100644
index 000000000000..df11860b847a
--- /dev/null
+++ b/docker-compose/docker-compose-no-build-no-db-container.yml
@@ -0,0 +1,20 @@
+version: '3.8'
+
+services:
+ blockscout:
+ image: blockscout/blockscout:${DOCKER_TAG:-latest}
+ restart: always
+ container_name: 'blockscout'
+ command: 'mix do ecto.create, ecto.migrate, phx.server'
+ extra_hosts:
+ - 'host.docker.internal:host-gateway'
+ env_file:
+ - ./envs/common-blockscout.env
+ environment:
+ ETHEREUM_JSONRPC_VARIANT: 'geth'
+ ETHEREUM_JSONRPC_HTTP_URL: http://host.docker.internal:8545/
+ DATABASE_URL: postgresql://postgres:@host.docker.internal:5432/blockscout?ssl=false
+ ECTO_USE_SSL: false
+ ports:
+ - 4000:4000
+
diff --git a/docker-compose/docker-compose-no-build-open-ethereum-nethermind.yml b/docker-compose/docker-compose-no-build-open-ethereum-nethermind.yml
new file mode 100644
index 000000000000..c6f3b551a6ee
--- /dev/null
+++ b/docker-compose/docker-compose-no-build-open-ethereum-nethermind.yml
@@ -0,0 +1,38 @@
+version: '3.8'
+
+services:
+ db:
+ image: postgres:13.6
+ restart: always
+ container_name: 'postgres'
+ environment:
+ POSTGRES_PASSWORD: ''
+ POSTGRES_USER: 'postgres'
+ POSTGRES_HOST_AUTH_METHOD: 'trust'
+ volumes:
+ - ./postgres-data:/var/lib/postgresql/data
+ ports:
+ - 7432:5432
+
+ blockscout:
+ depends_on:
+ - postgres
+ image: blockscout/blockscout:${DOCKER_TAG:-latest}
+ restart: always
+ container_name: 'blockscout'
+ links:
+ - db:database
+ command: 'mix do ecto.create, ecto.migrate, phx.server'
+ extra_hosts:
+ - 'host.docker.internal:host-gateway'
+ env_file:
+ - ./envs/common-blockscout.env
+ environment:
+ ETHEREUM_JSONRPC_VARIANT: 'parity'
+ ETHEREUM_JSONRPC_HTTP_URL: http://host.docker.internal:8545/
+ ETHEREUM_JSONRPC_TRACE_URL: http://host.docker.internal:8545/
+ DATABASE_URL: postgresql://postgres:@host.docker.internal:7432/blockscout?ssl=false
+ ECTO_USE_SSL: false
+ ports:
+ - 4000:4000
+
diff --git a/docker-compose/docker-compose.yml b/docker-compose/docker-compose.yml
new file mode 100644
index 000000000000..5f926ab7b6de
--- /dev/null
+++ b/docker-compose/docker-compose.yml
@@ -0,0 +1,33 @@
+version: '3.8'
+
+services:
+ db:
+ image: postgres:13.6
+ restart: always
+ container_name: 'postgres'
+ environment:
+ POSTGRES_PASSWORD: ''
+ POSTGRES_USER: 'postgres'
+ POSTGRES_HOST_AUTH_METHOD: 'trust'
+ ports:
+ - 7432:5432
+
+ blockscout:
+ depends_on:
+ - postgres
+ image: blockscout/blockscout:${DOCKER_TAG:-latest}
+ build:
+ context: ..
+ dockerfile: ./docker/Dockerfile
+ restart: always
+ container_name: 'blockscout'
+ links:
+ - db:database
+ command: 'mix do ecto.create, ecto.migrate, phx.server'
+ extra_hosts:
+ - 'host.docker.internal:host-gateway'
+ env_file:
+ - ./envs/common-blockscout.env
+ ports:
+ - 4000:4000
+
diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env
new file mode 100644
index 000000000000..b1db041e59ea
--- /dev/null
+++ b/docker-compose/envs/common-blockscout.env
@@ -0,0 +1,127 @@
+# DOCKER_TAG=
+ETHEREUM_JSONRPC_VARIANT=geth
+ETHEREUM_JSONRPC_HTTP_URL=http://host.docker.internal:8545/
+DATABASE_URL=postgresql://postgres:@host.docker.internal:7432/blockscout?ssl=false
+ETHEREUM_JSONRPC_TRACE_URL=http://host.docker.internal:8545/
+NETWORK=
+SUBNETWORK=Awesome chain
+LOGO=/images/blockscout_logo.svg
+LOGO_FOOTER=/images/blockscout_logo.svg
+# ETHEREUM_JSONRPC_WS_URL=
+ETHEREUM_JSONRPC_TRANSPORT=http
+IPC_PATH=
+NETWORK_PATH=/
+API_PATH=/
+SOCKET_ROOT=/
+BLOCKSCOUT_HOST=
+BLOCKSCOUT_PROTOCOL=
+# SECRET_KEY_BASE=
+# CHECK_ORIGIN=
+PORT=4000
+# COIN=
+# COIN_NAME=
+# COINGECKO_COIN_ID=
+# METADATA_CONTRACT=
+# VALIDATORS_CONTRACT=
+# KEYS_MANAGER_CONTRACT=
+# REWARDS_CONTRACT=
+# TOKEN_BRIDGE_CONTRACT=
+EMISSION_FORMAT=DEFAULT
+# CHAIN_SPEC_PATH=
+# SUPPLY_MODULE=
+# SOURCE_MODULE=
+POOL_SIZE=40
+POOL_SIZE_API=10
+ECTO_USE_SSL=false
+# DATADOG_HOST=
+# DATADOG_PORT=
+# SPANDEX_BATCH_SIZE=
+# SPANDEX_SYNC_THRESHOLD=
+HEART_BEAT_TIMEOUT=30
+# HEART_COMMAND=
+BLOCKSCOUT_VERSION=
+RELEASE_LINK=
+BLOCK_TRANSFORMER=base
+# GRAPHIQL_TRANSACTION=
+# FIRST_BLOCK=
+# LAST_BLOCK=
+# TRACE_FIRST_BLOCK=
+# TRACE_LAST_BLOCK=
+LINK_TO_OTHER_EXPLORERS=false
+OTHER_EXPLORERS={}
+SUPPORTED_CHAINS={}
+BLOCK_COUNT_CACHE_PERIOD=7200
+TXS_COUNT_CACHE_PERIOD=7200
+ADDRESS_COUNT_CACHE_PERIOD=7200
+ADDRESS_SUM_CACHE_PERIOD=3600
+TOTAL_GAS_USAGE_CACHE_PERIOD=3600
+ADDRESS_TRANSACTIONS_GAS_USAGE_COUNTER_CACHE_PERIOD=1800
+TOKEN_HOLDERS_COUNTER_CACHE_PERIOD=3600
+TOKEN_TRANSFERS_COUNTER_CACHE_PERIOD=3600
+ADDRESS_WITH_BALANCES_UPDATE_INTERVAL=1800
+TOKEN_METADATA_UPDATE_INTERVAL=172800
+AVERAGE_BLOCK_CACHE_PERIOD=1800
+MARKET_HISTORY_CACHE_PERIOD=21600
+ADDRESS_TRANSACTIONS_CACHE_PERIOD=1800
+ADDRESS_TOKENS_USD_SUM_CACHE_PERIOD=1800
+ADDRESS_TOKEN_TRANSFERS_COUNTER_CACHE_PERIOD=1800
+BRIDGE_MARKET_CAP_UPDATE_INTERVAL=1800
+TOKEN_EXCHANGE_RATE_CACHE_PERIOD=1800
+ALLOWED_EVM_VERSIONS=homestead,tangerineWhistle,spuriousDragon,byzantium,constantinople,petersburg,istanbul,berlin,london,default
+UNCLES_IN_AVERAGE_BLOCK_TIME=false
+DISABLE_WEBAPP=false
+DISABLE_READ_API=false
+DISABLE_WRITE_API=false
+DISABLE_INDEXER=false
+INDEXER_DISABLE_PENDING_TRANSACTIONS_FETCHER=false
+INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER=false
+# WEBAPP_URL=
+# API_URL=
+WOBSERVER_ENABLED=false
+SHOW_ADDRESS_MARKETCAP_PERCENTAGE=true
+CHECKSUM_ADDRESS_HASHES=true
+CHECKSUM_FUNCTION=eth
+DISABLE_EXCHANGE_RATES=true
+DISABLE_KNOWN_TOKENS=false
+ENABLE_TXS_STATS=true
+SHOW_PRICE_CHART=false
+SHOW_TXS_CHART=true
+HISTORY_FETCH_INTERVAL=30
+TXS_HISTORIAN_INIT_LAG=0
+TXS_STATS_DAYS_TO_COMPILE_AT_INIT=10
+COIN_BALANCE_HISTORY_DAYS=90
+APPS_MENU=true
+EXTERNAL_APPS=[]
+# ETH_OMNI_BRIDGE_MEDIATOR=
+# BSC_OMNI_BRIDGE_MEDIATOR=
+# AMB_BRIDGE_MEDIATORS=
+# GAS_PRICE=
+# FOREIGN_JSON_RPC=
+# RESTRICTED_LIST=
+# RESTRICTED_LIST_KEY=
+DISABLE_BRIDGE_MARKET_CAP_UPDATER=true
+# POS_STAKING_CONTRACT=
+ENABLE_POS_STAKING_IN_MENU=false
+SHOW_MAINTENANCE_ALERT=false
+MAINTENANCE_ALERT_MESSAGE=
+SHOW_STAKING_WARNING=false
+STAKING_WARNING_MESSAGE=
+CUSTOM_CONTRACT_ADDRESSES_TEST_TOKEN=
+ENABLE_SOURCIFY_INTEGRATION=false
+SOURCIFY_SERVER_URL=
+SOURCIFY_REPO_URL=
+# CHAIN_ID=
+MAX_SIZE_UNLESS_HIDE_ARRAY=50
+HIDE_BLOCK_MINER=false
+DISPLAY_TOKEN_ICONS=false
+SHOW_TENDERLY_LINK=false
+TENDERLY_CHAIN_PATH=
+MAX_STRING_LENGTH_WITHOUT_TRIMMING=2040
+RE_CAPTCHA_SECRET_KEY=
+RE_CAPTCHA_CLIENT_KEY=
+# JSON_RPC=
+API_RATE_LIMIT=50
+API_RATE_LIMIT_BY_KEY=50
+API_RATE_LIMIT_BY_IP=50
+API_RATE_LIMIT_WHITELISTED_IPS=
+API_RATE_LIMIT_STATIC_API_KEY=
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 2ff694c0112f..961197c196f0 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -2,6 +2,17 @@ FROM bitwalker/alpine-elixir-phoenix:1.12
RUN apk --no-cache --update add alpine-sdk gmp-dev automake libtool inotify-tools autoconf python3 file
+ENV GLIBC_REPO=https://github.com/sgerrand/alpine-pkg-glibc
+ENV GLIBC_VERSION=2.30-r0
+
+RUN set -ex && \
+ apk --update add libstdc++ curl ca-certificates && \
+ for pkg in glibc-${GLIBC_VERSION} glibc-bin-${GLIBC_VERSION}; \
+ do curl -sSL ${GLIBC_REPO}/releases/download/${GLIBC_VERSION}/${pkg}.apk -o /tmp/${pkg}.apk; done && \
+ apk add --allow-untrusted /tmp/*.apk && \
+ rm -v /tmp/*.apk && \
+ /usr/glibc-compat/sbin/ldconfig /lib /usr/glibc-compat/lib
+
# Get Rust
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
diff --git a/docker/Makefile b/docker/Makefile
index ef25a865ad7e..3f51e7f98f70 100644
--- a/docker/Makefile
+++ b/docker/Makefile
@@ -2,15 +2,23 @@ HOST_SYSTEM ?= '$(shell uname -s)'
HOST = host.docker.internal
DOCKER_IMAGE = blockscout_prod
BS_CONTAINER_NAME = blockscout
+BS_CONTAINER_IMAGE := blockscout
PG_CONTAINER_NAME = postgres
-PG_CONTAINER_IMAGE = postgres:12.5
+PG_CONTAINER_IMAGE := postgres:13.6
+PG_CONTAINER_NAME := db
THIS_FILE = $(lastword $(MAKEFILE_LIST))
+BS_NEXT_RELEASE = 4.1.2
+TAG := $(BS_NEXT_RELEASE)-prerelease-$(shell git log -1 --pretty=format:"%h")
ifeq ($(HOST_SYSTEM), Linux)
HOST=localhost
endif
-DB_URL = postgresql://postgres:@$(HOST):5432/explorer?ssl=false
+ifdef DATABASE_URL
+ DB_URL = $(DATABASE_URL)
+else
+ DB_URL = postgresql://postgres:@$(HOST):5432/blockscout?ssl=false
+endif
BLOCKSCOUT_CONTAINER_PARAMS = -e 'MIX_ENV=prod' \
-e 'DATABASE_URL=$(DB_URL)'
ifeq ($(HOST_SYSTEM), Linux)
@@ -118,6 +126,12 @@ endif
ifdef LAST_BLOCK
BLOCKSCOUT_CONTAINER_PARAMS += -e 'LAST_BLOCK=$(LAST_BLOCK)'
endif
+ifdef TRACE_FIRST_BLOCK
+ BLOCKSCOUT_CONTAINER_PARAMS += -e 'TRACE_FIRST_BLOCK=$(TRACE_FIRST_BLOCK)'
+endif
+ifdef TRACE_LAST_BLOCK
+ BLOCKSCOUT_CONTAINER_PARAMS += -e 'TRACE_LAST_BLOCK=$(TRACE_LAST_BLOCK)'
+endif
ifdef TXS_COUNT_CACHE_PERIOD
BLOCKSCOUT_CONTAINER_PARAMS += -e 'TXS_COUNT_CACHE_PERIOD=$(TXS_COUNT_CACHE_PERIOD)'
endif
@@ -320,31 +334,73 @@ 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
-ifdef ENABLE_1559_SUPPORT
- BLOCKSCOUT_CONTAINER_PARAMS += -e 'ENABLE_1559_SUPPORT=$(ENABLE_1559_SUPPORT)'
-endif
ifdef DISPLAY_TOKEN_ICONS
BLOCKSCOUT_CONTAINER_PARAMS += -e 'DISPLAY_TOKEN_ICONS=$(DISPLAY_TOKEN_ICONS)'
endif
+ifdef SHOW_TENDERLY_LINK
+ BLOCKSCOUT_CONTAINER_PARAMS += -e 'SHOW_TENDERLY_LINK=$(SHOW_TENDERLY_LINK)'
+endif
+ifdef TENDERLY_CHAIN_PATH
+ BLOCKSCOUT_CONTAINER_PARAMS += -e 'TENDERLY_CHAIN_PATH=$(TENDERLY_CHAIN_PATH)'
+endif
+ifdef MAX_STRING_LENGTH_WITHOUT_TRIMMING
+ BLOCKSCOUT_CONTAINER_PARAMS += -e 'MAX_STRING_LENGTH_WITHOUT_TRIMMING=$(MAX_STRING_LENGTH_WITHOUT_TRIMMING)'
+endif
+ifdef RE_CAPTCHA_SECRET_KEY
+ BLOCKSCOUT_CONTAINER_PARAMS += -e 'RE_CAPTCHA_SECRET_KEY=$(RE_CAPTCHA_SECRET_KEY)'
+endif
+ifdef RE_CAPTCHA_CLIENT_KEY
+ BLOCKSCOUT_CONTAINER_PARAMS += -e 'RE_CAPTCHA_CLIENT_KEY=$(RE_CAPTCHA_CLIENT_KEY)'
+endif
+ifdef ADDRESS_TOKEN_TRANSFERS_COUNTER_CACHE_PERIOD
+ BLOCKSCOUT_CONTAINER_PARAMS += -e 'ADDRESS_TOKEN_TRANSFERS_COUNTER_CACHE_PERIOD=$(ADDRESS_TOKEN_TRANSFERS_COUNTER_CACHE_PERIOD)'
+endif
+ifdef API_RATE_LIMIT
+ BLOCKSCOUT_CONTAINER_PARAMS += -e 'API_RATE_LIMIT=$(API_RATE_LIMIT)'
+endif
+ifdef API_RATE_LIMIT_BY_KEY
+ BLOCKSCOUT_CONTAINER_PARAMS += -e 'API_RATE_LIMIT_BY_KEY=$(API_RATE_LIMIT_BY_KEY)'
+endif
+ifdef API_RATE_LIMIT_BY_IP
+ BLOCKSCOUT_CONTAINER_PARAMS += -e 'API_RATE_LIMIT_BY_IP=$(API_RATE_LIMIT_BY_IP)'
+endif
+ifdef API_RATE_LIMIT_STATIC_API_KEY
+ BLOCKSCOUT_CONTAINER_PARAMS += -e 'API_RATE_LIMIT_STATIC_API_KEY=$(API_RATE_LIMIT_STATIC_API_KEY)'
+endif
+ifdef API_RATE_LIMIT_WHITELISTED_IPS
+ BLOCKSCOUT_CONTAINER_PARAMS += -e 'API_RATE_LIMIT_WHITELISTED_IPS=$(API_RATE_LIMIT_WHITELISTED_IPS)'
+endif
+ifdef COIN_NAME
+ BLOCKSCOUT_CONTAINER_PARAMS += -e 'COIN_NAME=$(COIN_NAME)'
+endif
+ifdef INDEXER_DISABLE_PENDING_TRANSACTIONS_FETCHER
+ BLOCKSCOUT_CONTAINER_PARAMS += -e 'INDEXER_DISABLE_PENDING_TRANSACTIONS_FETCHER=$(INDEXER_DISABLE_PENDING_TRANSACTIONS_FETCHER)'
+endif
+ifdef INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER
+ BLOCKSCOUT_CONTAINER_PARAMS += -e 'INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER=$(INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER)'
+endif
-HAS_BLOCKSCOUT_IMAGE := $(shell docker images | grep -sw ${DOCKER_IMAGE})
+HAS_BLOCKSCOUT_IMAGE := $(shell docker images | grep -sw "${BS_CONTAINER_IMAGE} ")
build:
- @echo "==> Checking for blockscout image $(DOCKER_IMAGE)"
+ @echo "==> Checking for blockscout image $(BS_CONTAINER_IMAGE)"
ifdef HAS_BLOCKSCOUT_IMAGE
- @echo "==> Image exist. Using $(DOCKER_IMAGE)"
+ @echo "==> Image exist. Using $(BS_CONTAINER_IMAGE)"
else
- @echo "==> No image found trying to build one..."
- @docker build --build-arg COIN="$(COIN)" -f ./Dockerfile -t $(DOCKER_IMAGE) ../
+ @echo "==> No image found, trying to build one..."
+ @docker build --build-arg COIN="$(COIN)" -f ./Dockerfile -t $(BS_CONTAINER_IMAGE) ../
endif
migrate_only:
@echo "==> Running migrations"
@docker run --rm \
$(BLOCKSCOUT_CONTAINER_PARAMS) \
- $(DOCKER_IMAGE) /bin/sh -c "echo $$MIX_ENV && mix do ecto.create, ecto.migrate"
+ $(BS_CONTAINER_IMAGE) /bin/sh -c "echo $$MIX_ENV && mix do ecto.create, ecto.migrate"
migrate: build postgres
@$(MAKE) -f $(THIS_FILE) migrate_only
@@ -353,6 +409,10 @@ migrate: build postgres
PG_EXIST := $(shell docker ps -a --filter name=${PG_CONTAINER_NAME} | grep ${PG_CONTAINER_NAME})
PG_STARTED := $(shell docker ps --filter name=${PG_CONTAINER_NAME} | grep ${PG_CONTAINER_NAME})
postgres:
+ifdef DATABASE_URL
+ @echo "==> DATABASE_URL of external DB provided. There is no need to start a container for DB."
+ @$(MAKE) -f $(THIS_FILE) migrate_only
+else
ifdef PG_EXIST
@echo "==> Checking PostrgeSQL container"
ifdef PG_STARTED
@@ -374,18 +434,74 @@ else
@sleep 1
@$(MAKE) -f $(THIS_FILE) migrate_only
endif
+endif
start: build postgres
@echo "==> Starting blockscout"
@docker run --rm --name $(BS_CONTAINER_NAME) \
$(BLOCKSCOUT_CONTAINER_PARAMS) \
-p 4000:4000 \
- $(DOCKER_IMAGE) /bin/sh -c "mix do ecto.create, ecto.migrate; mix phx.server"
+ $(BS_CONTAINER_IMAGE) /bin/sh -c "mix phx.server"
+
+BS_STARTED := $(shell docker ps --filter name=${BS_CONTAINER_NAME} | grep ${BS_CONTAINER_NAME})
+stop:
+ifdef BS_STARTED
+ @echo "==> Stopping BlockScout container."
+ @docker stop $(BS_CONTAINER_NAME)
+ @echo "==> BlockScout container stopped."
+else
+ @echo "==> BlockScout container already stopped before."
+endif
+ifdef PG_STARTED
+ @echo "==> Stopping Postgres container."
+ @docker stop $(PG_CONTAINER_NAME)
+ @echo "==> Postgres container stopped."
+else
+ @echo "==> Postgres container already stopped before."
+endif
run: start
+docker-login: ## login to DockerHub with credentials found in env
+ docker login -u ${DOCKER_USERNAME} -p ${DOCKER_PASSWORD}
+
+# Docker release - build, tag and push the container
+release: build publish ## Make a release by building and publishing the `{version}` ans `latest` tagged containers to hub
+
+# Docker publish
+publish: docker-login publish-latest publish-version ## publish the `{version}` ans `latest` tagged containers to hub
+
+publish-latest: tag-latest ## publish the `latest` tagged container to hub
+ @echo 'publish latest to $(DOCKER_REPO)'
+ docker push $(DOCKER_REPO)/$(APP_NAME):latest
+
+publish-version: tag-version ## publish the `{version}` tagged container to hub
+ @echo 'publish $(TAG) to $(DOCKER_REPO)'
+ docker push $(DOCKER_REPO)/$(APP_NAME):$(TAG)
+
+# Docker tagging
+tag: tag-latest tag-version ## Generate container tags for the `{version}` ans `latest` tags
+
+tag-latest: ## Generate container `latest` tag
+ @echo 'create latest tag'
+ docker tag $(APP_NAME) $(DOCKER_REPO)/$(APP_NAME):latest
+
+tag-version: ## Generate container `{version}` tag
+ @echo 'create tag $(TAG)'
+ docker tag $(APP_NAME) $(DOCKER_REPO)/$(APP_NAME):$(TAG)
+
.PHONY: build \
migrate \
start \
+ stop \
postgres \
- run
+ run \
+ docker-login \
+ release \
+ publish \
+ publish-latest \
+ publish-version \
+ tag \
+ tag-latest \
+ tag-version
+
diff --git a/mix.exs b/mix.exs
index c5629a2feda8..174eaffeff4e 100644
--- a/mix.exs
+++ b/mix.exs
@@ -11,7 +11,7 @@ defmodule BlockScout.Mixfile do
apps_path: "apps",
deps: deps(),
dialyzer: dialyzer(),
- elixir: "~> 1.10",
+ elixir: "~> 1.12",
preferred_cli_env: [
credo: :test,
dialyzer: :test
diff --git a/mix.lock b/mix.lock
index 0f9a6e19552f..4598ad9168f3 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.9", "afdef4279eb24a36bef1d4a83df8a34997c2dddf3ffac48866bb1113f28ae55b", [:mix], [{:ex_keccak, "~> 0.3.0", [hex: :ex_keccak, repo: "hexpm", optional: false]}, {:jason, "~> 1.3", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "230f6dada9995336f1d1b739194d99307a1d079e045548bbca0b622c8c4dbf5b"},
"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.3.0", "8c286f8c44c63c5f95e84053603f09cbef52d96314f2b751e56cf8836ceaea63", [:mix], [{:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: false]}], "hexpm", "18352c5873ee4938363ae9802038b6a157177a080db40b6e4ab842255ae44b9b"},
"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"},
@@ -57,10 +57,11 @@
"gen_stage": {:hex, :gen_stage, "0.14.3", "d0c66f1c87faa301c1a85a809a3ee9097a4264b2edf7644bf5c123237ef732bf", [:mix], [], "hexpm", "8453e2289d94c3199396eb517d65d6715ef26bcae0ee83eb5ff7a84445458d76"},
"gettext": {:hex, :gettext, "0.18.2", "7df3ea191bb56c0309c00a783334b288d08a879f53a7014341284635850a6e55", [:mix], [], "hexpm", "f9f537b13d4fdd30f3039d33cb80144c3aa1f8d9698e47d7bcbcc8df93b1f5c5"},
"hackney": {:hex, :hackney, "1.17.4", "99da4674592504d3fb0cfef0db84c3ba02b4508bae2dff8c0108baa0d6e0977c", [:rebar3], [{:certifi, "~>2.6.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "de16ff4996556c8548d512f4dbe22dd58a587bf3332e7fd362430a7ef3986b16"},
+ "hammer": {:hex, :hammer, "6.0.0", "72ec6fff10e9d63856968988a22ee04c4d6d5248071ddccfbda50aa6c455c1d7", [:mix], [{:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}], "hexpm", "d8e1ec2e534c4aae508b906759e077c3c1eb3e2b9425235d4b7bbab0b016210a"},
"html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"},
"httpoison": {:hex, :httpoison, "1.8.0", "6b85dea15820b7804ef607ff78406ab449dd78bed923a49c7160e1886e987a3d", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "28089eaa98cf90c66265b6b5ad87c59a3729bea2e74e9d08f9b51eb9729b3c3a"},
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
- "jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"},
+ "jason": {:hex, :jason, "1.3.0", "fa6b82a934feb176263ad2df0dbd91bf633d4a46ebfdffea0c8ae82953714946", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "53fc1f51255390e0ec7e50f9cb41e751c260d065dcba2bf0d08dc51a4002c2ac"},
"jsx": {:hex, :jsx, "2.8.3", "a05252d381885240744d955fbe3cf810504eb2567164824e19303ea59eef62cf", [:mix, :rebar3], [], "hexpm", "fc3499fed7a726995aa659143a248534adc754ebd16ccd437cd93b649a95091f"},
"junit_formatter": {:hex, :junit_formatter, "3.3.0", "bd7914d92885f7cf949dbe1dc6bacf76badfb2c1f5f7b3f9433c20e5b6ec42c8", [:mix], [], "hexpm", "4d040410925324b155ae4c7d41e884a0cdebe53b917bee4f22adf152e987a666"},
"libsecp256k1": {:hex, :libsecp256k1, "0.1.10", "d27495e2b9851c7765129b76c53b60f5e275bd6ff68292c50536bf6b8d091a4d", [:make, :mix], [{:mix_erlang_tasks, "0.1.0", [hex: :mix_erlang_tasks, repo: "hexpm", optional: false]}], "hexpm", "09ea06239938571124f7f5a27bc9ac45dfb1cfc2df40d46ee9b59c3d51366652"},
@@ -107,7 +108,8 @@
"quantile_estimator": {:hex, :quantile_estimator, "0.2.1", "ef50a361f11b5f26b5f16d0696e46a9e4661756492c981f7b2229ef42ff1cd15", [:rebar3], [], "hexpm", "282a8a323ca2a845c9e6f787d166348f776c1d4a41ede63046d72d422e3da946"},
"que": {:hex, :que, "0.10.1", "788ed0ec92ed69bdf9cfb29bf41a94ca6355b8d44959bd0669cf706e557ac891", [:mix], [{:ex_utils, "~> 0.1.6", [hex: :ex_utils, repo: "hexpm", optional: false]}, {:memento, "~> 0.3.0", [hex: :memento, repo: "hexpm", optional: false]}], "hexpm", "a737b365253e75dbd24b2d51acc1d851049e87baae08cd0c94e2bc5cd65088d5"},
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
- "rustler": {:hex, :rustler, "0.22.0", "e2930f9d6933e910f87526bb0a7f904e32b62a7e838a3ca4a884ee7fdfb957ed", [:mix], [{:toml, "~> 0.5.2", [hex: :toml, repo: "hexpm", optional: false]}], "hexpm", "01f5989dd511ebec09be481e07d3c59773d5373c5061e09d3ebc3ef61811b49d"},
+ "remote_ip": {:hex, :remote_ip, "1.0.0", "3d7fb45204a5704443f480cee9515e464997f52c35e0a60b6ece1f81484067ae", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "9e9fcad4e50c43b5234bb6a9629ed6ab223f3ed07147bd35470e4ee5c8caf907"},
+ "rustler": {:hex, :rustler, "0.23.0", "87162ffdf5a46b6aa03d624a77367070ff1263961ae35332c059225e136c4a87", [:mix], [{:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}, {:toml, "~> 0.5.2", [hex: :toml, repo: "hexpm", optional: false]}], "hexpm", "f5ab6f0ec564f5569009c0f5685b0e5b379fd72655e82a8dc5a3c24f9fdda36a"},
"sobelow": {:hex, :sobelow, "0.11.1", "23438964486f8112b41e743bbfd402da3e5b296fdc9eacab29914b79c48916dd", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "9897363a7eff96f4809304a90aad819e2ad5e5d24db547af502885146746a53c"},
"spandex": {:hex, :spandex, "3.0.3", "91aa318f3de696bb4d931adf65f7ebdbe5df25cccce1fe8fd376a44c46bcf69b", [:mix], [{:decorator, "~> 1.2", [hex: :decorator, repo: "hexpm", optional: true]}, {:optimal, "~> 0.3.3", [hex: :optimal, repo: "hexpm", optional: false]}, {:plug, ">= 1.0.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "e3e6c319d0ab478ddc9a39102a727a410c962b4d51c0932c72279b86d3b17044"},
"spandex_datadog": {:hex, :spandex_datadog, "1.1.0", "8c84e2f6c4067edc2e920dd79242f7bb0d6403652a7e9bc42109007f76b9be25", [:mix], [{:msgpax, "~> 2.2.1", [hex: :msgpax, repo: "hexpm", optional: false]}, {:spandex, "~> 3.0", [hex: :spandex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f4c20d3e601cad869705d9789f17a9242f245ce0bf2579fc835e96a6834663e2"},