From 8eb6f08f1e1b05692dc0871b94cf909c04114977 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 15 Aug 2024 17:25:30 +0300 Subject: [PATCH] feat: enhance /api/v2/smart-contracts/:hash API endpoint (#10558) * feat: enhance /api/v2/smart-contracts/:hash API endpoint * Refactor implementation fetch * Refactoring address controller * Process review comment * Fix address overview template * Update apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex Co-authored-by: nikitosing <32202610+nikitosing@users.noreply.github.com> --------- Co-authored-by: nikitosing <32202610+nikitosing@users.noreply.github.com> --- .../controllers/api/v2/address_controller.ex | 18 +- .../api/v2/smart_contract_controller.ex | 11 +- .../templates/address/_tabs.html.eex | 2 +- .../templates/address/overview.html.eex | 8 +- .../address_coin_balance/index.html.eex | 2 +- .../templates/address_contract/index.html.eex | 22 +-- .../index.html.eex | 2 +- .../index.html.eex | 2 +- .../templates/address_logs/index.html.eex | 2 +- .../address_read_contract/index.html.eex | 2 +- .../address_read_proxy/index.html.eex | 2 +- .../templates/address_token/index.html.eex | 2 +- .../address_token_transfer/index.html.eex | 2 +- .../address_transaction/index.html.eex | 2 +- .../address_validation/index.html.eex | 2 +- .../address_withdrawal/index.html.eex | 10 +- .../address_write_contract/index.html.eex | 2 +- .../address_write_proxy/index.html.eex | 2 +- .../block_withdrawal/_withdrawal.html.eex | 2 +- .../tokens/holder/_token_balances.html.eex | 2 +- .../tokens/transfer/_token_transfer.html.eex | 4 +- .../templates/transaction/_actions.html.eex | 8 +- .../transaction/_pending_tile.html.eex | 4 +- .../_total_transfers_from_to.html.eex | 4 +- .../templates/transaction/overview.html.eex | 8 +- .../transaction_state/_state_change.html.eex | 4 +- .../_token_transfer.html.eex | 4 +- .../templates/withdrawal/_withdrawal.html.eex | 2 +- .../views/address_coin_balance_view.ex | 1 + .../views/address_contract_view.ex | 1 + .../views/address_decompiled_contract_view.ex | 1 + .../address_internal_transaction_view.ex | 1 + .../views/address_logs_view.ex | 1 + .../views/address_read_contract_view.ex | 2 + .../views/address_read_proxy_view.ex | 2 + .../views/address_token_transfer_view.ex | 1 + .../views/address_token_view.ex | 1 + .../views/address_transaction_view.ex | 1 + .../views/address_validation_view.ex | 2 +- .../lib/block_scout_web/views/address_view.ex | 35 ++-- .../views/address_withdrawal_view.ex | 2 + .../views/address_write_contract_view.ex | 2 + .../views/address_write_proxy_view.ex | 2 + .../views/api/v2/address_view.ex | 33 +--- .../block_scout_web/views/api/v2/helper.ex | 52 ++---- .../views/api/v2/smart_contract_view.ex | 96 ++++++----- .../views/block_withdrawal_view.ex | 2 + .../views/tokens/holder_view.ex | 2 +- .../views/transaction_state_view.ex | 2 +- .../views/transaction_token_transfer_view.ex | 1 + .../block_scout_web/views/transaction_view.ex | 2 +- .../block_scout_web/views/withdrawal_view.ex | 2 + .../account/api/v2/user_controller_test.exs | 2 + .../address_read_contract_controller_test.exs | 2 - .../address_read_proxy_controller_test.exs | 2 - ...address_write_contract_controller_test.exs | 2 - .../address_write_proxy_controller_test.exs | 2 - .../api/v2/address_controller_test.exs | 3 + .../api/v2/smart_contract_controller_test.exs | 157 ++++++++---------- .../views/address_view_test.exs | 17 -- apps/explorer/lib/explorer/chain.ex | 47 +----- apps/explorer/lib/explorer/chain/address.ex | 23 +++ .../lib/explorer/chain/address/counters.ex | 2 +- .../lib/explorer/chain/smart_contract.ex | 21 +-- .../explorer/chain/smart_contract/proxy.ex | 89 +++++++--- .../proxy/models/implementation.ex | 114 +++++++------ .../lib/explorer/chain/transaction.ex | 2 +- .../lib/explorer/smart_contract/helper.ex | 48 +++++- .../proxy/models/implementation_test.exs | 36 ++-- apps/explorer/test/explorer/chain_test.exs | 5 +- 70 files changed, 487 insertions(+), 471 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex index f5c2ef44f4..59afca890e 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex @@ -29,6 +29,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do alias Explorer.Chain.{Address, Hash, Transaction} alias Explorer.Chain.Address.Counters alias Explorer.Chain.Token.Instance + alias Explorer.SmartContract.Helper, as: SmartContractHelper alias BlockScoutWeb.API.V2.CeloView alias Explorer.Chain.Celo.ElectionReward, as: CeloElectionReward @@ -118,16 +119,23 @@ defmodule BlockScoutWeb.API.V2.AddressController do action_fallback(BlockScoutWeb.API.V2.FallbackController) def address(conn, %{"address_hash_param" => address_hash_string} = params) do - with {:ok, _address_hash, address} <- validate_address(address_hash_string, params, @address_options), - fully_preloaded_address <- - Address.maybe_preload_smart_contract_associations(address, @contract_address_preloads, @api_true) do - CoinBalanceOnDemand.trigger_fetch(fully_preloaded_address) + with {:ok, _address_hash, address} <- validate_address(address_hash_string, params, @address_options) do + fully_preloaded_address = + Address.maybe_preload_smart_contract_associations(address, @contract_address_preloads, @api_true) + {implementations, proxy_type} = + SmartContractHelper.pre_fetch_implementations(fully_preloaded_address) + + CoinBalanceOnDemand.trigger_fetch(address) ContractCodeOnDemand.trigger_fetch(address) conn |> put_status(200) - |> render(:address, %{address: fully_preloaded_address |> maybe_preload_ens_to_address()}) + |> render(:address, %{ + address: fully_preloaded_address |> maybe_preload_ens_to_address(), + implementations: implementations, + proxy_type: proxy_type + }) end end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/smart_contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/smart_contract_controller.ex index 03d33589e1..7d19376428 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/smart_contract_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/smart_contract_controller.ex @@ -14,6 +14,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do alias Explorer.Chain.{Address, SmartContract} alias Explorer.Chain.SmartContract.AuditReport alias Explorer.Chain.SmartContract.Proxy.Models.Implementation + alias Explorer.SmartContract.Helper, as: SmartContractHelper alias Explorer.SmartContract.{Reader, Writer} alias Explorer.SmartContract.Solidity.PublishHelper alias Explorer.ThirdPartyIntegrations.SolidityScan @@ -22,7 +23,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do necessity_by_association: %{ :contracts_creation_internal_transaction => :optional, [smart_contract: :smart_contract_additional_sources] => :optional, - :contracts_creation_transaction => :optional + :contracts_creation_transaction => :optional, + :proxy_implementations => :optional }, api?: true ] @@ -37,9 +39,12 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do _ <- PublishHelper.sourcify_check(address_hash_string), {:not_found, {:ok, address}} <- {:not_found, Chain.find_contract_address(address_hash, @smart_contract_address_options, false)} do + {implementations, proxy_type} = + SmartContractHelper.pre_fetch_implementations(address) + conn |> put_status(200) - |> render(:smart_contract, %{address: address}) + |> render(:smart_contract, %{address: address, implementations: implementations, proxy_type: proxy_type}) end end @@ -206,7 +211,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do end @doc """ - /api/v2/smart-contracts/${address_hash_string}/solidityscan-report logic + /api/v2/smart-contracts/:address_hash_string/solidityscan-report logic """ @spec solidityscan_report(Plug.Conn.t(), map()) :: {:address, {:error, :not_found}} diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/_tabs.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/_tabs.html.eex index a4060c9485..67a69145bf 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address/_tabs.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/_tabs.html.eex @@ -59,7 +59,7 @@ to: AccessHelper.get_path(@conn, :address_validation_path, :index, @address.hash) ) %> <% end %> - <%= if contract?(@address) do %> + <%= if Address.smart_contract?(@address) do %> <%= link( to: AccessHelper.get_path(@conn, :address_contract_path, :index, @address.hash), class: "card-tab #{tab_status("contracts", @conn.request_path)}") do %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/overview.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/overview.html.eex index dfc00f7048..0e4e200001 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address/overview.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/overview.html.eex @@ -40,7 +40,7 @@ <%= render BlockScoutWeb.CommonComponentsView, "_btn_qr_code.html" %> -

<%= @address %>

+

<%= @address %>

<% from_address_hash = from_address_hash(@address) %> - <%= if contract?(@address) do %> + <%= if Address.smart_contract?(@address) do %>
<%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", @@ -124,7 +124,7 @@ <% end %> <%= if @is_proxy do %> - <% {implementation_addresses, implementation_names} = Implementation.get_implementation(@address.smart_contract) %> + <% {implementation_addresses, implementation_names, _proxy_type} = Implementation.get_implementation(@address.smart_contract) %> <% implementation_address_ = Enum.at(implementation_addresses, 0) %> <% name = Enum.at(implementation_names, 0) %> <% implementation_address = implementation_address_ || "0x0000000000000000000000000000000000000000" %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_coin_balance/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_coin_balance/index.html.eex index 268b148cdb..27c10ec6b7 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_coin_balance/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_coin_balance/index.html.eex @@ -1,7 +1,7 @@
- <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + <% is_proxy = SmartContractHelper.address_is_proxy?(@address) %> <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex index d5b804f42b..7b06783f2e 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex @@ -1,12 +1,12 @@ <% contract_creation_code = contract_creation_code(@address) %> <% minimal_proxy_template = EIP1167.get_implementation_smart_contract(@address.hash) %> -<% implementation_or_bytecode_twin_contract = minimal_proxy_template || SmartContract.get_address_verified_bytecode_twin_contract(@address.hash).verified_contract %> +<% bytecode_twin_contract = SmartContract.get_address_verified_bytecode_twin_contract(@address.hash).verified_contract %> <% smart_contract_verified = BlockScoutWeb.AddressView.smart_contract_verified?(@address) %> <% fully_verified = SmartContract.verified_with_full_match?(@address.hash)%> -<% additional_sources = BlockScoutWeb.API.V2.SmartContractView.get_additional_sources(@address.smart_contract, smart_contract_verified, implementation_or_bytecode_twin_contract) %> +<% additional_sources = BlockScoutWeb.API.V2.SmartContractView.get_additional_sources(@address.smart_contract, smart_contract_verified, bytecode_twin_contract) %> <% visualize_sol2uml_enabled = Explorer.Visualize.Sol2uml.enabled?() %>
- <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + <% is_proxy = SmartContractHelper.address_is_proxy?(@address) %> <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> @@ -15,16 +15,16 @@
<%= unless smart_contract_verified do %> <%= if minimal_proxy_template do %> - <%= render BlockScoutWeb.CommonComponentsView, "_minimal_proxy_pattern.html", address_hash: implementation_or_bytecode_twin_contract.address_hash, conn: @conn %> + <%= render BlockScoutWeb.CommonComponentsView, "_minimal_proxy_pattern.html", address_hash: bytecode_twin_contract.address_hash, conn: @conn %> <% else %> - <%= if implementation_or_bytecode_twin_contract do %> + <%= if bytecode_twin_contract do %> <% 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( - implementation_or_bytecode_twin_contract.address_hash, - to: address_contract_path(@conn, :index, implementation_or_bytecode_twin_contract.address_hash)) %>.
<%= gettext("All metadata displayed below is from that contract. In order to verify current contract, click") %> <%= gettext("Verify & Publish") %> <%= gettext("button") %>
+ bytecode_twin_contract.address_hash, + to: address_contract_path(@conn, :index, bytecode_twin_contract.address_hash)) %>.
<%= gettext("All metadata displayed below is from that contract. In order to verify current contract, click") %> <%= gettext("Verify & Publish") %> <%= gettext("button") %>
<%= link(gettext("Verify & Publish"), to: path, class: "button button-primary button-sm float-right ml-3", "data-test": "verify_and_publish") %>
@@ -40,8 +40,8 @@
<% end %> <% end %> - <%= if smart_contract_verified || (!smart_contract_verified && implementation_or_bytecode_twin_contract) do %> - <% target_contract = if smart_contract_verified, do: @address.smart_contract, else: implementation_or_bytecode_twin_contract %> + <%= if smart_contract_verified || (!smart_contract_verified && bytecode_twin_contract) do %> + <% target_contract = if smart_contract_verified, do: @address.smart_contract, else: bytecode_twin_contract %> <%= if @address.smart_contract && @address.smart_contract.verified_via_sourcify && @address.smart_contract.partially_verified && smart_contract_verified do %>
<%= gettext("This contract has been partially verified via Sourcify.") %> @@ -240,8 +240,8 @@ <% end %>
- <%= if smart_contract_verified || (!smart_contract_verified && implementation_or_bytecode_twin_contract) do %> - <% target_contract = if smart_contract_verified, do: @address.smart_contract, else: implementation_or_bytecode_twin_contract %> + <%= if smart_contract_verified || (!smart_contract_verified && bytecode_twin_contract) do %> + <% target_contract = if smart_contract_verified, do: @address.smart_contract, else: bytecode_twin_contract %> <%= if target_contract.external_libraries && target_contract.external_libraries != [] do %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_decompiled_contract/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_decompiled_contract/index.html.eex index 5390e7806e..d9967f71a8 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_decompiled_contract/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_decompiled_contract/index.html.eex @@ -1,5 +1,5 @@
- <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + <% is_proxy = SmartContractHelper.address_is_proxy?(@address) %> <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/index.html.eex index 0a5b2bcf94..fe847a2a14 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/index.html.eex @@ -1,5 +1,5 @@
- <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + <% is_proxy = SmartContractHelper.address_is_proxy?(@address) %> <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_logs/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_logs/index.html.eex index 3dc20d3a7f..5b8058fdee 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_logs/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_logs/index.html.eex @@ -1,5 +1,5 @@
- <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + <% is_proxy = SmartContractHelper.address_is_proxy?(@address) %> <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/index.html.eex index 58ff9c96f5..afd3ab3a7d 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/index.html.eex @@ -1,6 +1,6 @@
- <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + <% is_proxy = SmartContractHelper.address_is_proxy?(@address) %> <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_read_proxy/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_read_proxy/index.html.eex index a3577a7cb9..efe8695dec 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_read_proxy/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_read_proxy/index.html.eex @@ -1,6 +1,6 @@
- <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + <% is_proxy = SmartContractHelper.address_is_proxy?(@address) %> <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_token/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_token/index.html.eex index 1fd811570f..4ecd808911 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_token/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_token/index.html.eex @@ -1,5 +1,5 @@
- <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + <% is_proxy = SmartContractHelper.address_is_proxy?(@address) %> <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_token_transfer/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_token_transfer/index.html.eex index 896ea5f8ab..3d8e5196a8 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_token_transfer/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_token_transfer/index.html.eex @@ -1,5 +1,5 @@
- <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + <% is_proxy = SmartContractHelper.address_is_proxy?(@address) %> <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex index 74f122040a..7d8d54178c 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex @@ -1,6 +1,6 @@
- <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + <% is_proxy = SmartContractHelper.address_is_proxy?(@address) %> <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_validation/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_validation/index.html.eex index e435195fe3..9c2947ad06 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_validation/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_validation/index.html.eex @@ -1,5 +1,5 @@
- <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + <% is_proxy = SmartContractHelper.address_is_proxy?(@address) %> <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_withdrawal/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_withdrawal/index.html.eex index d07c664e6b..c11adfd87c 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_withdrawal/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_withdrawal/index.html.eex @@ -1,6 +1,6 @@
- <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + <% is_proxy = SmartContractHelper.address_is_proxy?(@address) %> <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> @@ -9,17 +9,17 @@ <%= render BlockScoutWeb.AddressView, "_tabs.html", address: @address, is_proxy: is_proxy, conn: @conn %>
- +

<%= gettext "Withdrawals" %>

<%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %>
- + - +
@@ -54,7 +54,7 @@ <%= gettext "There are no withdrawals for this address." %> - +
<%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_write_contract/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_write_contract/index.html.eex index a1d915d2ec..a538e98b5f 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_write_contract/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_write_contract/index.html.eex @@ -1,6 +1,6 @@
- <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + <% is_proxy = SmartContractHelper.address_is_proxy?(@address) %> <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_write_proxy/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_write_proxy/index.html.eex index a3577a7cb9..efe8695dec 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_write_proxy/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_write_proxy/index.html.eex @@ -1,6 +1,6 @@
- <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + <% is_proxy = SmartContractHelper.address_is_proxy?(@address) %> <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/block_withdrawal/_withdrawal.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/block_withdrawal/_withdrawal.html.eex index 9506edc331..5a9ff7c439 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/block_withdrawal/_withdrawal.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/block_withdrawal/_withdrawal.html.eex @@ -12,7 +12,7 @@ <%= render BlockScoutWeb.AddressView, "_link.html", address: @withdrawal.address, - contract: BlockScoutWeb.AddressView.contract?(@withdrawal.address), + contract: Address.smart_contract?(@withdrawal.address), use_custom_tooltip: false %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/_token_balances.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/_token_balances.html.eex index f8fecf59a6..324741db64 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/_token_balances.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/_token_balances.html.eex @@ -2,7 +2,7 @@
- <%= render BlockScoutWeb.AddressView, "_link.html", address: @token_balance.address, contract: BlockScoutWeb.AddressView.contract?(@token_balance.address), use_custom_tooltip: false %> + <%= render BlockScoutWeb.AddressView, "_link.html", address: @token_balance.address, contract: Address.smart_contract?(@token_balance.address), use_custom_tooltip: false %> 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 73b6bbf5af..1e019df78e 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 @@ -15,7 +15,7 @@ BlockScoutWeb.AddressView, "_responsive_hash.html", address: @token_transfer.from_address, - contract: BlockScoutWeb.AddressView.contract?(@token_transfer.from_address), + contract: Address.smart_contract?(@token_transfer.from_address), use_custom_tooltip: false ) %> <% end %> @@ -25,7 +25,7 @@ BlockScoutWeb.AddressView, "_responsive_hash.html", address: @token_transfer.to_address, - contract: BlockScoutWeb.AddressView.contract?(@token_transfer.to_address), + contract: Address.smart_contract?(@token_transfer.to_address), use_custom_tooltip: false ) %> <% end %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_actions.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_actions.html.eex index 9a820f5ad9..982434fcbd 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_actions.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_actions.html.eex @@ -84,7 +84,7 @@ <% address_string = Map.get(@action.data, "address") %> <% {address_status, address} = transaction_action_string_to_address(address_string) %> - <% address = if address_status == :ok, do: render_to_string(BlockScoutWeb.AddressView, "_link.html", address: address, contract: BlockScoutWeb.AddressView.contract?(address), use_custom_tooltip: false, trimmed: false), else: render_to_string(BlockScoutWeb.TransactionView, "_actions_address.html", address_string: address_string, action: @action) %> + <% address = if address_status == :ok, do: render_to_string(BlockScoutWeb.AddressView, "_link.html", address: address, contract: Address.smart_contract?(address), use_custom_tooltip: false, trimmed: false), else: render_to_string(BlockScoutWeb.TransactionView, "_actions_address.html", address_string: address_string, action: @action) %> <% to_address = Map.get(@action.data, "to") %> <% to_content = raw(render_to_string BlockScoutWeb.TransactionView, "_actions_to.html", address: to_address) %> <% to = link to: address_path(BlockScoutWeb.Endpoint, :show, to_address), "data-test": "address_hash_link", do: to_content %> @@ -114,11 +114,11 @@ <% address0 = Map.get(@action.data, "address0") %> <% symbol1 = Map.get(@action.data, "symbol1") %> <% address1 = Map.get(@action.data, "address1") %> - + - + <% symbol0 = if symbol0 != "Ether", do: link(symbol0, to: token_path(BlockScoutWeb.Endpoint, :show, address0), "data-test": "token_link"), else: raw(symbol0) %> - + <% symbol1 = if symbol1 != "Ether", do: link(symbol1, to: token_path(BlockScoutWeb.Endpoint, :show, address1), "data-test": "token_link"), else: raw(symbol1) %> <%= if @action.type == :mint do %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_pending_tile.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_pending_tile.html.eex index fd1a1496f1..e3b68d786d 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_pending_tile.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_pending_tile.html.eex @@ -8,10 +8,10 @@
<%= render BlockScoutWeb.TransactionView, "_link.html", transaction_hash: @transaction.hash %> - <%= render BlockScoutWeb.AddressView, "_link.html", address: @transaction.from_address, contract: BlockScoutWeb.AddressView.contract?(@transaction.from_address), use_custom_tooltip: false %> + <%= render BlockScoutWeb.AddressView, "_link.html", address: @transaction.from_address, contract: Address.smart_contract?(@transaction.from_address), use_custom_tooltip: false %> → <%= if @transaction.to_address_hash do %> - <%= render BlockScoutWeb.AddressView, "_link.html", address: @transaction.to_address, contract: BlockScoutWeb.AddressView.contract?(@transaction.to_address), use_custom_tooltip: false %> + <%= render BlockScoutWeb.AddressView, "_link.html", address: @transaction.to_address, contract: Address.smart_contract?(@transaction.to_address), use_custom_tooltip: false %> <% else %> <%= gettext("Contract Address Pending") %> <% end %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex index 91de91125a..db68b6f095 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex @@ -7,7 +7,7 @@ From
@@ -28,7 +28,7 @@ <% else %> <% end %> <%= if not_negative?(@balance_before) and not_negative?(@balance_after) do %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex index 885990a377..8611cb5b02 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex @@ -7,9 +7,9 @@
<%= render BlockScoutWeb.TransactionView, "_link.html", transaction_hash: @token_transfer.transaction_hash %> - <%= render BlockScoutWeb.AddressView, "_link.html", address: @token_transfer.from_address, contract: BlockScoutWeb.AddressView.contract?(@token_transfer.from_address), use_custom_tooltip: false %> + <%= render BlockScoutWeb.AddressView, "_link.html", address: @token_transfer.from_address, contract: Address.smart_contract?(@token_transfer.from_address), use_custom_tooltip: false %> → - <%= render BlockScoutWeb.AddressView, "_link.html", address: @token_transfer.to_address, contract: BlockScoutWeb.AddressView.contract?(@token_transfer.to_address), use_custom_tooltip: false %> + <%= render BlockScoutWeb.AddressView, "_link.html", address: @token_transfer.to_address, contract: Address.smart_contract?(@token_transfer.to_address), use_custom_tooltip: false %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/withdrawal/_withdrawal.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/withdrawal/_withdrawal.html.eex index e90e6c99d2..cb3928bc02 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/withdrawal/_withdrawal.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/withdrawal/_withdrawal.html.eex @@ -19,7 +19,7 @@ <%= render BlockScoutWeb.AddressView, "_link.html", address: @withdrawal.address, - contract: BlockScoutWeb.AddressView.contract?(@withdrawal.address), + contract: Address.smart_contract?(@withdrawal.address), use_custom_tooltip: false %> diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_coin_balance_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_coin_balance_view.ex index d2bd7904bc..6e0363dab3 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_coin_balance_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_coin_balance_view.ex @@ -3,6 +3,7 @@ defmodule BlockScoutWeb.AddressCoinBalanceView do alias BlockScoutWeb.AccessHelper alias Explorer.Chain.Wei + alias Explorer.SmartContract.Helper, as: SmartContractHelper def format(%Wei{} = value) do format_wei_value(value, :ether) diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_contract_view.ex index 01b13e6a71..efb3dd168f 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_contract_view.ex @@ -10,6 +10,7 @@ defmodule BlockScoutWeb.AddressContractView do alias Explorer.Chain.{Address, Data, InternalTransaction, Transaction} alias Explorer.Chain.SmartContract alias Explorer.Chain.SmartContract.Proxy.EIP1167 + alias Explorer.SmartContract.Helper, as: SmartContractHelper def render("scripts.html", %{conn: conn}) do render_scripts(conn, "address_contract/code_highlighting.js") diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_decompiled_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_decompiled_contract_view.ex index daab6b54b1..8542b7f067 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_decompiled_contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_decompiled_contract_view.ex @@ -1,5 +1,6 @@ defmodule BlockScoutWeb.AddressDecompiledContractView do use BlockScoutWeb, :view + alias Explorer.SmartContract.Helper, as: SmartContractHelper @colors %{ "\e[95m" => "", 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 28a478b12a..81cb6ca558 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 @@ -3,6 +3,7 @@ defmodule BlockScoutWeb.AddressInternalTransactionView do alias BlockScoutWeb.AccessHelper alias Explorer.Chain.Address + alias Explorer.SmartContract.Helper, as: SmartContractHelper 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 0c4daa067d..e1a4862fe2 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 @@ -2,6 +2,7 @@ defmodule BlockScoutWeb.AddressLogsView do use BlockScoutWeb, :view alias Explorer.Chain.Address + alias Explorer.SmartContract.Helper, as: SmartContractHelper import BlockScoutWeb.AddressView, only: [decode: 2] end diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_read_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_read_contract_view.ex index 559af200bc..e13b14768e 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_read_contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_read_contract_view.ex @@ -1,3 +1,5 @@ defmodule BlockScoutWeb.AddressReadContractView do use BlockScoutWeb, :view + + alias Explorer.SmartContract.Helper, as: SmartContractHelper end diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_read_proxy_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_read_proxy_view.ex index e51247fdd8..447f362c3c 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_read_proxy_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_read_proxy_view.ex @@ -1,3 +1,5 @@ defmodule BlockScoutWeb.AddressReadProxyView do use BlockScoutWeb, :view + + alias Explorer.SmartContract.Helper, as: SmartContractHelper end 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 6a4eeffafe..c053e54af5 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 @@ -3,6 +3,7 @@ defmodule BlockScoutWeb.AddressTokenTransferView do alias BlockScoutWeb.AccessHelper alias Explorer.Chain.Address + alias Explorer.SmartContract.Helper, as: SmartContractHelper 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 83cdf79a71..941cfe2924 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 @@ -4,4 +4,5 @@ defmodule BlockScoutWeb.AddressTokenView do alias BlockScoutWeb.{AddressView, ChainView} alias Explorer.Chain alias Explorer.Chain.{Address, Wei} + alias Explorer.SmartContract.Helper, as: SmartContractHelper 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 1b18148a47..b6539a49a2 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 @@ -3,6 +3,7 @@ defmodule BlockScoutWeb.AddressTransactionView do alias BlockScoutWeb.AccessHelper alias Explorer.Chain.Address + alias Explorer.SmartContract.Helper, as: SmartContractHelper def format_current_filter(filter) do case filter do diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_validation_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_validation_view.ex index 65f2bb0e9d..1625fb33cd 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_validation_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_validation_view.ex @@ -1,5 +1,5 @@ defmodule BlockScoutWeb.AddressValidationView do use BlockScoutWeb, :view - # import BlockScoutWeb.AddressView, only: [contract?: 1, smart_contract_verified?: 1] + alias Explorer.SmartContract.Helper, as: SmartContractHelper end diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_view.ex index 6792270e7c..a8016bdcca 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_view.ex @@ -36,7 +36,7 @@ defmodule BlockScoutWeb.AddressView do def address_partial_selector(struct_to_render_from, direction, current_address, truncate \\ false) def address_partial_selector(%Address{} = address, _, current_address, truncate) do - matching_address_check(current_address, address, contract?(address), truncate) + matching_address_check(current_address, address, Address.smart_contract?(address), truncate) end def address_partial_selector( @@ -58,19 +58,19 @@ defmodule BlockScoutWeb.AddressView do end def address_partial_selector(%InternalTransaction{to_address: address}, :to, current_address, truncate) do - matching_address_check(current_address, address, contract?(address), truncate) + matching_address_check(current_address, address, Address.smart_contract?(address), truncate) end def address_partial_selector(%InternalTransaction{from_address: address}, :from, current_address, truncate) do - matching_address_check(current_address, address, contract?(address), truncate) + matching_address_check(current_address, address, Address.smart_contract?(address), truncate) end def address_partial_selector(%TokenTransfer{to_address: address}, :to, current_address, truncate) do - matching_address_check(current_address, address, contract?(address), truncate) + matching_address_check(current_address, address, Address.smart_contract?(address), truncate) end def address_partial_selector(%TokenTransfer{from_address: address}, :from, current_address, truncate) do - matching_address_check(current_address, address, contract?(address), truncate) + matching_address_check(current_address, address, Address.smart_contract?(address), truncate) end def address_partial_selector( @@ -92,11 +92,11 @@ defmodule BlockScoutWeb.AddressView do end def address_partial_selector(%Transaction{to_address: address}, :to, current_address, truncate) do - matching_address_check(current_address, address, contract?(address), truncate) + matching_address_check(current_address, address, Address.smart_contract?(address), truncate) end def address_partial_selector(%Transaction{from_address: address}, :from, current_address, truncate) do - matching_address_check(current_address, address, contract?(address), truncate) + matching_address_check(current_address, address, Address.smart_contract?(address), truncate) end def address_partial_selector(%Reward{address: address}, _, current_address, truncate) do @@ -104,7 +104,7 @@ defmodule BlockScoutWeb.AddressView do end def address_title(%Address{} = address) do - if contract?(address) do + if Address.smart_contract?(address) do gettext("Contract Address") else gettext("Address") @@ -167,12 +167,6 @@ defmodule BlockScoutWeb.AddressView do to_string(fetched_coin_balance_block_number) end - def contract?(%Address{contract_code: nil}), do: false - - def contract?(%Address{contract_code: _}), do: true - - def contract?(nil), do: true - def validator?(val) when val > 0, do: true def validator?(_), do: false @@ -259,14 +253,6 @@ defmodule BlockScoutWeb.AddressView do def read_function?(function), do: Helper.queriable_method?(function) || Helper.read_with_wallet_method?(function) - def smart_contract_is_proxy?(address, options \\ []) - - def smart_contract_is_proxy?(%Address{smart_contract: %SmartContract{} = smart_contract}, options) do - Proxy.proxy_contract?(smart_contract, options) - end - - def smart_contract_is_proxy?(%Address{smart_contract: _}, _), do: false - def smart_contract_with_write_functions?(%Address{smart_contract: %SmartContract{}} = address) do !contract_interaction_disabled?() && Enum.any?( @@ -447,13 +433,14 @@ defmodule BlockScoutWeb.AddressView do def address_page_title(address) do cond do smart_contract_verified?(address) -> "#{address.smart_contract.name} (#{to_string(address)})" - contract?(address) -> "Contract #{to_string(address)}" + Address.smart_contract?(address) -> "Contract #{to_string(address)}" true -> "#{to_string(address)}" end end def smart_contract_is_gnosis_safe_proxy?(%Address{smart_contract: %SmartContract{}} = address) do - address.smart_contract.name == "GnosisSafeProxy" && Proxy.gnosis_safe_contract?(address.smart_contract.abi) + address.smart_contract.name == "GnosisSafeProxy" && + Proxy.gnosis_safe_contract?(address.smart_contract.abi) end def smart_contract_is_gnosis_safe_proxy?(_address), do: false diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_withdrawal_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_withdrawal_view.ex index 9ff659e847..80572ba127 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_withdrawal_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_withdrawal_view.ex @@ -1,3 +1,5 @@ defmodule BlockScoutWeb.AddressWithdrawalView do use BlockScoutWeb, :view + + alias Explorer.SmartContract.Helper, as: SmartContractHelper end diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_write_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_write_contract_view.ex index c21e1f51c9..dc8d88ce5c 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_write_contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_write_contract_view.ex @@ -1,3 +1,5 @@ defmodule BlockScoutWeb.AddressWriteContractView do use BlockScoutWeb, :view + + alias Explorer.SmartContract.Helper, as: SmartContractHelper end diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_write_proxy_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_write_proxy_view.ex index 17634e9fb1..b27e5df503 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_write_proxy_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_write_proxy_view.ex @@ -1,3 +1,5 @@ defmodule BlockScoutWeb.AddressWriteProxyView do use BlockScoutWeb, :view + + alias Explorer.SmartContract.Helper, as: SmartContractHelper end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex index 22841d3059..b39fe2f26f 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex @@ -5,11 +5,9 @@ defmodule BlockScoutWeb.API.V2.AddressView do alias BlockScoutWeb.AddressView alias BlockScoutWeb.API.V2.{ApiView, Helper, TokenView} - alias BlockScoutWeb.API.V2.Helper alias Explorer.{Chain, Market} alias Explorer.Chain.Address alias Explorer.Chain.Address.Counters - alias Explorer.Chain.SmartContract.Proxy.Models.Implementation alias Explorer.Chain.Token.Instance @api_true [api?: true] @@ -18,8 +16,8 @@ defmodule BlockScoutWeb.API.V2.AddressView do ApiView.render("message.json", assigns) end - def render("address.json", %{address: address, conn: conn}) do - prepare_address(address, conn) + def render("address.json", %{address: address, implementations: implementations, proxy_type: proxy_type, conn: conn}) do + prepare_address(address, conn, implementations, proxy_type) end def render("token_balances.json", %{token_balances: token_balances}) do @@ -83,29 +81,13 @@ defmodule BlockScoutWeb.API.V2.AddressView do |> Map.put(:coin_balance, if(address.fetched_coin_balance, do: address.fetched_coin_balance.value)) end - def prepare_address(address, conn \\ nil) do + @doc """ + Prepares address properties for rendering in /addresses and /addresses/:address_hash_param API v2 endpoints + """ + @spec prepare_address(Address.t(), Plug.Conn.t() | nil, list(), String.t() | nil) :: map() + def prepare_address(address, conn \\ nil, implementations \\ [], proxy_type \\ nil) do base_info = Helper.address_with_info(conn, address, address.hash, true) - {:ok, address_with_smart_contract} = - Chain.hash_to_address( - address.hash, - [necessity_by_association: %{:smart_contract => :optional}], - false - ) - - is_proxy = AddressView.smart_contract_is_proxy?(address_with_smart_contract, @api_true) - - implementations = - with true <- is_proxy, - {addresses, names} <- - Implementation.get_implementation(address_with_smart_contract.smart_contract, @api_true), - false <- addresses && Enum.empty?(addresses) do - Helper.proxy_object_info(addresses, names) - else - _ -> - [] - end - balance = address.fetched_coin_balance && address.fetched_coin_balance.value exchange_rate = Market.get_coin_exchange_rate().usd_value @@ -134,6 +116,7 @@ defmodule BlockScoutWeb.API.V2.AddressView do extended_info else Map.merge(extended_info, %{ + "proxy_type" => proxy_type, "implementations" => implementations }) end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex index e9d1f963db..5f483b1911 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex @@ -4,8 +4,8 @@ defmodule BlockScoutWeb.API.V2.Helper do """ alias Ecto.Association.NotLoaded - alias Explorer.Chain - alias Explorer.Chain.{Address, Hash} + alias Explorer.Chain.Address + alias Explorer.Chain.SmartContract.Proxy alias Explorer.Chain.Transaction.History.TransactionStats import BlockScoutWeb.Account.AuthController, only: [current_user: 1] @@ -64,26 +64,28 @@ defmodule BlockScoutWeb.API.V2.Helper do def address_with_info(%Address{} = address, _address_hash) do smart_contract? = Address.smart_contract?(address) - {proxy_implementations, implementation_address_hashes, implementation_names} = + {proxy_implementations, implementation_address_hashes, implementation_names, proxy_type} = case address.proxy_implementations do %NotLoaded{} -> - {nil, [], []} + {nil, [], [], nil} nil -> - {nil, [], []} + {nil, [], [], nil} proxy_implementations -> address_hashes = proxy_implementations.address_hashes names = proxy_implementations.names + proxy_type = proxy_implementations.proxy_type - {proxy_implementations, address_hashes, names} + {proxy_implementations, address_hashes, names, proxy_type} end %{ "hash" => Address.checksum(address), "is_contract" => smart_contract?, "name" => address_name(address), - "implementations" => proxy_object_info(implementation_address_hashes, implementation_names), + "proxy_type" => proxy_type, + "implementations" => Proxy.proxy_object_info(implementation_address_hashes, implementation_names), "is_verified" => verified?(address) || verified_minimal_proxy?(proxy_implementations), "ens_domain_name" => address.ens_domain_name, "metadata" => address.metadata @@ -110,6 +112,7 @@ defmodule BlockScoutWeb.API.V2.Helper do "hash" => Address.checksum(address_hash), "is_contract" => false, "name" => nil, + "proxy_type" => nil, "implementations" => [], "is_verified" => nil, "ens_domain_name" => nil, @@ -117,41 +120,6 @@ defmodule BlockScoutWeb.API.V2.Helper do } end - @doc """ - Retrieves formatted proxy object based on its implementation addresses and names. - - ## Parameters - - * `implementation_addresses` - A list of implementation addresses for the proxy object. - * `implementation_names` - A list of implementation names for the proxy object. - - ## Returns - - A list of maps containing information about the proxy object. - - """ - @spec proxy_object_info([String.t() | Hash.Address.t()], [String.t() | nil]) :: [map()] - def proxy_object_info([], []), do: [] - - def proxy_object_info(implementation_addresses, implementation_names) do - implementation_addresses - |> Enum.zip(implementation_names) - |> Enum.reduce([], fn {address, name}, acc -> - case address do - %Hash{} = address_hash -> - [%{"address" => Address.checksum(address_hash), "name" => name} | acc] - - _ -> - with {:ok, address_hash} <- Chain.string_to_address_hash(address), - checksummed_address <- Address.checksum(address_hash) do - [%{"address" => checksummed_address, "name" => name} | acc] - else - _ -> acc - end - end - end) - end - defp minimal_proxy_pattern?(proxy_implementations) do proxy_implementations.proxy_type == :eip1167 end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex index 4a523f9270..561788f41f 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex @@ -11,18 +11,28 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do alias Ecto.Changeset alias Explorer.Chain alias Explorer.Chain.{Address, SmartContract, SmartContractAdditionalSource} + alias Explorer.SmartContract.Helper, as: SmartContractHelper alias Explorer.Visualize.Sol2uml require Logger @api_true [api?: true] + # Option to skip fetching implementation from the node, + # when checking, if smart-contract is proxy. It is used in smart contract view + # to prevent double request to the JSON RPC node. + @skip_implementation_fetch_true [skip_implementation_fetch?: true] def render("smart_contracts.json", %{smart_contracts: smart_contracts, next_page_params: next_page_params}) do %{"items" => Enum.map(smart_contracts, &prepare_smart_contract_for_list/1), "next_page_params" => next_page_params} end - def render("smart_contract.json", %{address: address, conn: conn}) do - prepare_smart_contract(address, conn) + def render("smart_contract.json", %{ + address: address, + implementations: implementations, + proxy_type: proxy_type, + conn: conn + }) do + prepare_smart_contract(address, implementations, proxy_type, conn) end def render("read_functions.json", %{functions: functions}) do @@ -145,15 +155,20 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do defp prepare_output(output), do: output # credo:disable-for-next-line - def prepare_smart_contract(%Address{smart_contract: %SmartContract{} = smart_contract} = address, conn) do + def prepare_smart_contract( + %Address{smart_contract: %SmartContract{} = smart_contract} = address, + implementations, + proxy_type, + conn + ) do bytecode_twin = SmartContract.get_address_verified_bytecode_twin_contract(address.hash, @api_true) minimal_proxy_address_hash = address.implementation - implementation_or_bytecode_twin_contract = address.implementation || bytecode_twin.verified_contract + bytecode_twin_contract = bytecode_twin.verified_contract smart_contract_verified = AddressView.smart_contract_verified?(address) fully_verified = SmartContract.verified_with_full_match?(address.hash, @api_true) write_methods? = AddressView.smart_contract_with_write_functions?(address) - is_proxy = AddressView.smart_contract_is_proxy?(address, @api_true) + is_proxy = SmartContractHelper.address_is_proxy?(address, Keyword.merge(@api_true, @skip_implementation_fetch_true)) read_custom_abi? = AddressView.has_address_custom_abi_with_read_functions?(conn, address.hash) write_custom_abi? = AddressView.has_address_custom_abi_with_write_functions?(conn, address.hash) @@ -162,57 +177,62 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do get_additional_sources( smart_contract, smart_contract_verified, - implementation_or_bytecode_twin_contract + bytecode_twin_contract ) visualize_sol2uml_enabled = Sol2uml.enabled?() target_contract = - if smart_contract_verified, do: address.smart_contract, else: implementation_or_bytecode_twin_contract + if smart_contract_verified, do: smart_contract, else: bytecode_twin_contract %{ "verified_twin_address_hash" => - implementation_or_bytecode_twin_contract && - Address.checksum(implementation_or_bytecode_twin_contract.address_hash), + bytecode_twin_contract && + Address.checksum(bytecode_twin_contract.address_hash), "is_verified" => smart_contract_verified, - "is_changed_bytecode" => smart_contract_verified && address.smart_contract.is_changed_bytecode, - "is_partially_verified" => address.smart_contract.partially_verified && smart_contract_verified, + "is_changed_bytecode" => smart_contract_verified && smart_contract.is_changed_bytecode, + "is_partially_verified" => smart_contract.partially_verified && smart_contract_verified, "is_fully_verified" => fully_verified, - "is_verified_via_sourcify" => address.smart_contract.verified_via_sourcify && smart_contract_verified, - "is_verified_via_eth_bytecode_db" => address.smart_contract.verified_via_eth_bytecode_db, - "is_verified_via_verifier_alliance" => address.smart_contract.verified_via_verifier_alliance, - "is_vyper_contract" => target_contract.is_vyper_contract, + "is_verified_via_sourcify" => smart_contract.verified_via_sourcify && smart_contract_verified, + "is_verified_via_eth_bytecode_db" => smart_contract.verified_via_eth_bytecode_db, + "is_verified_via_verifier_alliance" => smart_contract.verified_via_verifier_alliance, + "is_vyper_contract" => target_contract && target_contract.is_vyper_contract, "has_custom_methods_read" => read_custom_abi?, "has_custom_methods_write" => write_custom_abi?, "has_methods_read" => AddressView.smart_contract_with_read_only_functions?(address), "has_methods_write" => write_methods?, "has_methods_read_proxy" => is_proxy, "has_methods_write_proxy" => is_proxy && write_methods?, + # todo: remove this property once frontend is bound to "implementations" "minimal_proxy_address_hash" => minimal_proxy_address_hash && Address.checksum(minimal_proxy_address_hash.address_hash), + "proxy_type" => proxy_type, + "implementations" => implementations, "sourcify_repo_url" => - if(address.smart_contract.verified_via_sourcify && smart_contract_verified, - do: AddressContractView.sourcify_repo_url(address.hash, address.smart_contract.partially_verified) + if(smart_contract.verified_via_sourcify && smart_contract_verified, + do: AddressContractView.sourcify_repo_url(address.hash, smart_contract.partially_verified) ), "can_be_visualized_via_sol2uml" => - visualize_sol2uml_enabled && !target_contract.is_vyper_contract && !is_nil(target_contract.abi), - "name" => target_contract.name, - "compiler_version" => target_contract.compiler_version, - "optimization_enabled" => target_contract.optimization, - "optimization_runs" => target_contract.optimization_runs, - "evm_version" => target_contract.evm_version, - "verified_at" => target_contract.inserted_at, - "abi" => target_contract.abi, - "source_code" => target_contract.contract_source_code, - "file_path" => target_contract.file_path, + visualize_sol2uml_enabled && target_contract && !target_contract.is_vyper_contract && + !is_nil(target_contract.abi), + "name" => target_contract && target_contract.name, + "compiler_version" => target_contract && target_contract.compiler_version, + "optimization_enabled" => target_contract && target_contract.optimization, + "optimization_runs" => target_contract && target_contract.optimization_runs, + "evm_version" => target_contract && target_contract.evm_version, + "verified_at" => target_contract && target_contract.inserted_at, + "abi" => target_contract && target_contract.abi, + "source_code" => target_contract && target_contract.contract_source_code, + "file_path" => target_contract && target_contract.file_path, "additional_sources" => (is_list(additional_sources) && Enum.map(additional_sources, &prepare_additional_source/1)) || [], - "compiler_settings" => target_contract.compiler_settings, - "external_libraries" => prepare_external_libraries(target_contract.external_libraries), - "constructor_args" => if(smart_contract_verified, do: target_contract.constructor_arguments), + "compiler_settings" => target_contract && target_contract.compiler_settings, + "external_libraries" => (target_contract && prepare_external_libraries(target_contract.external_libraries)) || [], + "constructor_args" => if(smart_contract_verified, do: target_contract && target_contract.constructor_arguments), "decoded_constructor_args" => if(smart_contract_verified, - do: format_constructor_arguments(target_contract.abi, target_contract.constructor_arguments) + do: + target_contract && format_constructor_arguments(target_contract.abi, target_contract.constructor_arguments) ), "language" => smart_contract_language(smart_contract), "license_type" => smart_contract.license_type, @@ -222,26 +242,28 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do |> Map.merge(bytecode_info(address)) end - def prepare_smart_contract(address, conn) do + def prepare_smart_contract(address, implementations, proxy_type, conn) do read_custom_abi? = AddressView.has_address_custom_abi_with_read_functions?(conn, address.hash) write_custom_abi? = AddressView.has_address_custom_abi_with_write_functions?(conn, address.hash) %{ "has_custom_methods_read" => read_custom_abi?, - "has_custom_methods_write" => write_custom_abi? + "has_custom_methods_write" => write_custom_abi?, + "proxy_type" => proxy_type, + "implementations" => implementations } |> Map.merge(bytecode_info(address)) end @doc """ - Returns additional sources of the smart-contract or from bytecode twin or from implementation, if it fits minimal proxy pattern (EIP-1167, Clone with immutable arguments) + Returns additional sources of the smart-contract from bytecode twin """ @spec get_additional_sources(SmartContract.t(), boolean, SmartContract.t() | nil) :: [SmartContractAdditionalSource.t()] | nil - def get_additional_sources(smart_contract, smart_contract_verified, implementation_or_bytecode_twin_contract) do + def get_additional_sources(smart_contract, smart_contract_verified, bytecode_twin_contract) do cond do - !is_nil(implementation_or_bytecode_twin_contract) -> - implementation_or_bytecode_twin_contract.smart_contract_additional_sources + !is_nil(bytecode_twin_contract) -> + bytecode_twin_contract.smart_contract_additional_sources smart_contract_verified -> smart_contract.smart_contract_additional_sources diff --git a/apps/block_scout_web/lib/block_scout_web/views/block_withdrawal_view.ex b/apps/block_scout_web/lib/block_scout_web/views/block_withdrawal_view.ex index 5fa812f198..f6b22b30f7 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/block_withdrawal_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/block_withdrawal_view.ex @@ -1,3 +1,5 @@ defmodule BlockScoutWeb.BlockWithdrawalView do use BlockScoutWeb, :view + + alias Explorer.Chain.Address 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 745b041ddd..e916a69fe2 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 @@ -2,7 +2,7 @@ defmodule BlockScoutWeb.Tokens.HolderView do use BlockScoutWeb, :view alias BlockScoutWeb.Tokens.OverviewView - alias Explorer.Chain.Token + alias Explorer.Chain.{Address, Token} @doc """ Checks if the total supply percentage must be shown. diff --git a/apps/block_scout_web/lib/block_scout_web/views/transaction_state_view.ex b/apps/block_scout_web/lib/block_scout_web/views/transaction_state_view.ex index 69d6eaad9f..73814009ac 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/transaction_state_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/transaction_state_view.ex @@ -2,7 +2,7 @@ defmodule BlockScoutWeb.TransactionStateView do use BlockScoutWeb, :view alias Explorer.Chain - alias Explorer.Chain.Wei + alias Explorer.Chain.{Address, Wei} import Explorer.Chain.Transaction.StateChange, only: [from_loss: 1, has_diff?: 1, to_profit: 1] 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 66999ad804..3e31c0f2c3 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 @@ -2,4 +2,5 @@ defmodule BlockScoutWeb.TransactionTokenTransferView do use BlockScoutWeb, :view alias Explorer.Chain + alias Explorer.Chain.Address 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 d89ab6f738..64eafbccde 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 @@ -449,7 +449,7 @@ defmodule BlockScoutWeb.TransactionView do end def involves_contract?(%Transaction{from_address: from_address, to_address: to_address}) do - AddressView.contract?(from_address) || AddressView.contract?(to_address) + Address.smart_contract?(from_address) || Address.smart_contract?(to_address) end def involves_token_transfers?(%Transaction{token_transfers: []}), do: false diff --git a/apps/block_scout_web/lib/block_scout_web/views/withdrawal_view.ex b/apps/block_scout_web/lib/block_scout_web/views/withdrawal_view.ex index bbe7be4fcf..296304d622 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/withdrawal_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/withdrawal_view.ex @@ -1,3 +1,5 @@ defmodule BlockScoutWeb.WithdrawalView do use BlockScoutWeb, :view + + alias Explorer.Chain.Address end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/account/api/v2/user_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/account/api/v2/user_controller_test.exs index 0bdb1d1ef8..30f44f611b 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/account/api/v2/user_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/account/api/v2/user_controller_test.exs @@ -151,6 +151,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserControllerTest do "name" => name, "address" => %{ "hash" => Address.checksum(addr), + "proxy_type" => nil, "implementations" => [], "is_contract" => false, "is_verified" => false, @@ -207,6 +208,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserControllerTest do "name" => name, "address" => %{ "hash" => Address.checksum(addr), + "proxy_type" => nil, "implementations" => [], "is_contract" => false, "is_verified" => false, diff --git a/apps/block_scout_web/test/block_scout_web/controllers/address_read_contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/address_read_contract_controller_test.exs index a6be3d9058..f3d6d5f818 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/address_read_contract_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/address_read_contract_controller_test.exs @@ -78,8 +78,6 @@ defmodule BlockScoutWeb.AddressReadContractControllerTest do block_index: 0 ) - TestHelper.get_eip1967_implementation_zero_addresses() - conn = get(conn, address_read_contract_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash))) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/address_read_proxy_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/address_read_proxy_controller_test.exs index a31dccf58c..310eb60851 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/address_read_proxy_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/address_read_proxy_controller_test.exs @@ -77,8 +77,6 @@ defmodule BlockScoutWeb.AddressReadProxyControllerTest do block_index: 0 ) - TestHelper.get_eip1967_implementation_zero_addresses() - conn = get(conn, address_read_proxy_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash))) assert html_response(conn, 404) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/address_write_contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/address_write_contract_controller_test.exs index 63a2a2eaea..20a47ca8ac 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/address_write_contract_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/address_write_contract_controller_test.exs @@ -80,8 +80,6 @@ defmodule BlockScoutWeb.AddressWriteContractControllerTest do block_index: 0 ) - TestHelper.get_eip1967_implementation_zero_addresses() - conn = get(conn, address_write_contract_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash))) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/address_write_proxy_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/address_write_proxy_controller_test.exs index 398317ef22..5155ab618d 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/address_write_proxy_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/address_write_proxy_controller_test.exs @@ -78,8 +78,6 @@ defmodule BlockScoutWeb.AddressWriteProxyControllerTest do block_index: 0 ) - TestHelper.get_eip1967_implementation_zero_addresses() - conn = get(conn, address_write_proxy_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash))) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs index d8feb0c0ec..5f38ff2bbd 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs @@ -85,6 +85,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do "creation_tx_hash" => nil, "token" => nil, "coin_balance" => nil, + "proxy_type" => nil, "implementations" => [], "block_number_balance_updated_at" => nil, "has_decompiled_code" => false, @@ -220,6 +221,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do "watchlist_names" => [], "creator_address_hash" => ^from, "creation_tx_hash" => ^tx_hash, + "proxy_type" => "eip1167", "implementations" => [ %{"address" => ^checksummed_implementation_contract_address_hash, "name" => ^name} ] @@ -265,6 +267,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do "watchlist_names" => [], "creator_address_hash" => ^from, "creation_tx_hash" => ^tx_hash, + "proxy_type" => "eip1967", "implementations" => [%{"address" => ^implementation_address_hash_string, "name" => nil}] } = json_response(request, 200) end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs index c0e19f0b9c..a7d01ba118 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs @@ -32,13 +32,15 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do test "get unverified smart-contract info", %{conn: conn} do address = insert(:contract_address) - TestHelper.get_eip1967_implementation_zero_addresses() + TestHelper.get_eip1967_implementation_error_response() request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") response = json_response(request, 200) assert response == %{ + "proxy_type" => nil, + "implementations" => [], "has_custom_methods_read" => false, "has_custom_methods_write" => false, "is_self_destructed" => false, @@ -53,11 +55,15 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do ) |> with_block() + TestHelper.get_eip1967_implementation_error_response() + request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") response = json_response(request, 200) assert response == %{ + "proxy_type" => nil, + "implementations" => [], "has_custom_methods_read" => false, "has_custom_methods_write" => false, "is_self_destructed" => false, @@ -112,6 +118,10 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do ) |> with_block() + implementation_address = insert(:address) + implementation_address_hash_string = to_string(implementation_address.hash) + formatted_implementation_address_hash_string = to_string(Address.checksum(implementation_address.hash)) + correct_response = %{ "verified_twin_address_hash" => nil, "is_verified" => true, @@ -151,6 +161,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do "creation_bytecode" => "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029", "abi" => target_contract.abi, + "proxy_type" => "eip1967", + "implementations" => [%{"address" => formatted_implementation_address_hash_string, "name" => nil}], "is_verified_via_eth_bytecode_db" => target_contract.verified_via_eth_bytecode_db, "is_verified_via_verifier_alliance" => target_contract.verified_via_verifier_alliance, "language" => smart_contract_language(target_contract), @@ -159,8 +171,6 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do "is_blueprint" => false } - implementation_address = insert(:address) - implementation_address_hash_string = to_string(implementation_address.hash) TestHelper.get_eip1967_implementation_non_zero_address(implementation_address_hash_string) request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(target_contract.address_hash)}") @@ -257,6 +267,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do "creation_bytecode" => "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029", "abi" => target_contract.abi, + "proxy_type" => nil, + "implementations" => [], "is_verified_via_eth_bytecode_db" => target_contract.verified_via_eth_bytecode_db, "is_verified_via_verifier_alliance" => target_contract.verified_via_verifier_alliance, "language" => smart_contract_language(target_contract), @@ -363,6 +375,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do "creation_bytecode" => "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029", "abi" => target_contract.abi, + "proxy_type" => nil, + "implementations" => [], "is_verified_via_eth_bytecode_db" => target_contract.verified_via_eth_bytecode_db, "is_verified_via_verifier_alliance" => target_contract.verified_via_verifier_alliance, "language" => smart_contract_language(target_contract), @@ -379,7 +393,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do assert correct_response == response end - test "get smart-contract multiple additional sources from EIP-1167 implementation", %{conn: conn} do + test "doesn't get smart-contract multiple additional sources from EIP-1167 implementation", %{conn: conn} do implementation_contract = insert(:smart_contract, external_libraries: [], @@ -449,48 +463,18 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do |> with_block(status: :ok) correct_response = %{ - "verified_twin_address_hash" => Address.checksum(implementation_contract.address_hash), - "is_verified" => false, - "is_changed_bytecode" => false, - "is_partially_verified" => implementation_contract.partially_verified, - "is_fully_verified" => false, - "is_verified_via_sourcify" => false, - "is_vyper_contract" => implementation_contract.is_vyper_contract, - "has_methods_read" => true, - "has_methods_write" => true, - "has_methods_read_proxy" => true, - "has_methods_write_proxy" => true, "has_custom_methods_read" => false, "has_custom_methods_write" => false, - "minimal_proxy_address_hash" => Address.checksum(implementation_contract.address_hash), - "sourcify_repo_url" => nil, - "can_be_visualized_via_sol2uml" => false, - "name" => implementation_contract && implementation_contract.name, - "compiler_version" => implementation_contract.compiler_version, - "optimization_enabled" => implementation_contract.optimization, - "optimization_runs" => implementation_contract.optimization_runs, - "evm_version" => implementation_contract.evm_version, - "verified_at" => implementation_contract.inserted_at |> to_string() |> String.replace(" ", "T"), - "source_code" => implementation_contract.contract_source_code, - "file_path" => implementation_contract.file_path, - "additional_sources" => [ - %{"file_path" => "test1", "source_code" => "test2"}, - %{"file_path" => "test3", "source_code" => "test4"} - ], - "compiler_settings" => implementation_contract.compiler_settings, - "external_libraries" => [], - "constructor_args" => nil, - "decoded_constructor_args" => nil, "is_self_destructed" => false, "deployed_bytecode" => proxy_deployed_bytecode, "creation_bytecode" => proxy_tx_input, - "abi" => implementation_contract.abi, - "is_verified_via_eth_bytecode_db" => implementation_contract.verified_via_eth_bytecode_db, - "is_verified_via_verifier_alliance" => implementation_contract.verified_via_verifier_alliance, - "language" => smart_contract_language(implementation_contract), - "license_type" => "bsd_3_clause", - "certified" => false, - "is_blueprint" => false + "proxy_type" => "eip1167", + "implementations" => [ + %{ + "address" => Address.checksum(implementation_contract.address_hash), + "name" => implementation_contract.name + } + ] } request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(proxy_address.hash)}") @@ -551,6 +535,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do "creation_bytecode" => "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029", "abi" => target_contract.abi, + "proxy_type" => nil, + "implementations" => [], "is_verified_via_eth_bytecode_db" => target_contract.verified_via_eth_bytecode_db, "is_verified_via_verifier_alliance" => target_contract.verified_via_verifier_alliance, "language" => smart_contract_language(target_contract), @@ -568,7 +554,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do end end - test "get smart-contract implementation for 'Clones with immutable arguments' pattern", %{conn: conn} do + test "doesn't get smart-contract implementation for 'Clones with immutable arguments' pattern", %{conn: conn} do implementation_contract = insert(:smart_contract, external_libraries: [], @@ -631,48 +617,18 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do ) |> with_block(status: :ok) + formatted_implementation_address_hash_string = to_string(Address.checksum(implementation_contract.address_hash)) + correct_response = %{ - "verified_twin_address_hash" => Address.checksum(implementation_contract.address_hash), - "is_verified" => false, - "is_changed_bytecode" => false, - "is_partially_verified" => implementation_contract.partially_verified, - "is_fully_verified" => false, - "is_verified_via_sourcify" => false, - "is_vyper_contract" => implementation_contract.is_vyper_contract, - "has_methods_read" => true, - "has_methods_write" => true, - "has_methods_read_proxy" => true, - "has_methods_write_proxy" => true, + "proxy_type" => "clone_with_immutable_arguments", + "implementations" => [ + %{"address" => formatted_implementation_address_hash_string, "name" => implementation_contract.name} + ], "has_custom_methods_read" => false, "has_custom_methods_write" => false, - "minimal_proxy_address_hash" => Address.checksum(implementation_contract.address_hash), - "sourcify_repo_url" => nil, - "can_be_visualized_via_sol2uml" => false, - "name" => implementation_contract && implementation_contract.name, - "compiler_version" => implementation_contract.compiler_version, - "optimization_enabled" => implementation_contract.optimization, - "optimization_runs" => implementation_contract.optimization_runs, - "evm_version" => implementation_contract.evm_version, - "verified_at" => implementation_contract.inserted_at |> to_string() |> String.replace(" ", "T"), - "source_code" => implementation_contract.contract_source_code, - "file_path" => implementation_contract.file_path, - "additional_sources" => [ - %{"file_path" => "test1", "source_code" => "test2"} - ], - "compiler_settings" => implementation_contract.compiler_settings, - "external_libraries" => [], - "constructor_args" => nil, - "decoded_constructor_args" => nil, "is_self_destructed" => false, "deployed_bytecode" => proxy_deployed_bytecode, - "creation_bytecode" => proxy_tx_input, - "abi" => implementation_contract.abi, - "is_verified_via_eth_bytecode_db" => implementation_contract.verified_via_eth_bytecode_db, - "is_verified_via_verifier_alliance" => implementation_contract.verified_via_verifier_alliance, - "language" => smart_contract_language(implementation_contract), - "license_type" => "bsd_3_clause", - "certified" => false, - "is_blueprint" => false + "creation_bytecode" => proxy_tx_input } request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(proxy_address.hash)}") @@ -752,6 +708,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do assert response == %{ + "proxy_type" => nil, + "implementations" => [], "has_custom_methods_read" => false, "has_custom_methods_write" => false, "is_self_destructed" => false, @@ -837,6 +795,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do assert response == %{ + "proxy_type" => nil, + "implementations" => [], "has_custom_methods_read" => false, "has_custom_methods_write" => false, "is_self_destructed" => false, @@ -969,6 +929,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do assert response == %{ + "proxy_type" => nil, + "implementations" => [], "has_custom_methods_read" => false, "has_custom_methods_write" => false, "is_self_destructed" => false, @@ -1034,7 +996,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do Conn.resp(conn, 200, eth_bytecode_response) end) - TestHelper.get_eip1967_implementation_error_response() + TestHelper.get_eip1967_implementation_zero_addresses() request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") @@ -1056,6 +1018,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do assert response == %{ + "proxy_type" => nil, + "implementations" => [], "has_custom_methods_read" => false, "has_custom_methods_write" => false, "is_self_destructed" => false, @@ -1064,8 +1028,6 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" } - TestHelper.get_eip1967_implementation_zero_addresses() - request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") assert response = json_response(request, 200) @@ -1148,6 +1110,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do implementation_address = insert(:address) implementation_address_hash_string = to_string(implementation_address.hash) + formatted_implementation_address_hash_string = to_string(Address.checksum(implementation_address.hash)) TestHelper.get_eip1967_implementation_non_zero_address(implementation_address_hash_string) request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") @@ -1170,6 +1133,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do assert response == %{ + "proxy_type" => nil, + "implementations" => [], "has_custom_methods_read" => false, "has_custom_methods_write" => false, "is_self_destructed" => false, @@ -1180,6 +1145,11 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") assert response = json_response(request, 200) + assert %{"proxy_type" => "eip1967"} = response + + assert %{"implementations" => [%{"address" => ^formatted_implementation_address_hash_string, "name" => nil}]} = + response + assert %{"is_verified" => true} = response assert %{"is_verified_via_eth_bytecode_db" => true} = response assert %{"is_partially_verified" => true} = response @@ -1265,6 +1235,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do implementation_address = insert(:address) implementation_address_hash_string = to_string(implementation_address.hash) + formatted_implementation_address_hash_string = to_string(Address.checksum(implementation_address.hash)) TestHelper.get_eip1967_implementation_non_zero_address(implementation_address_hash_string) request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") @@ -1287,6 +1258,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do assert response == %{ + "proxy_type" => nil, + "implementations" => [], "has_custom_methods_read" => false, "has_custom_methods_write" => false, "is_self_destructed" => false, @@ -1297,6 +1270,11 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") assert response = json_response(request, 200) + assert %{"proxy_type" => "eip1967"} = response + + assert %{"implementations" => [%{"address" => ^formatted_implementation_address_hash_string, "name" => nil}]} = + response + assert %{"is_verified" => true} = response assert %{"is_verified_via_eth_bytecode_db" => true} = response assert %{"is_partially_verified" => false} = response @@ -1382,6 +1360,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do implementation_address = insert(:address) implementation_address_hash_string = to_string(implementation_address.hash) + formatted_implementation_address_hash_string = to_string(Address.checksum(implementation_address.hash)) TestHelper.get_eip1967_implementation_non_zero_address(implementation_address_hash_string) request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") @@ -1404,6 +1383,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do assert response == %{ + "proxy_type" => nil, + "implementations" => [], "has_custom_methods_read" => false, "has_custom_methods_write" => false, "is_self_destructed" => false, @@ -1414,6 +1395,11 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") assert response = json_response(request, 200) + assert %{"proxy_type" => "eip1967"} = response + + assert %{"implementations" => [%{"address" => ^formatted_implementation_address_hash_string, "name" => nil}]} = + response + assert %{"is_verified" => true} = response assert %{"is_verified_via_eth_bytecode_db" => true} = response assert %{"is_partially_verified" => false} = response @@ -2260,8 +2246,6 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do test "return 404 on unverified contract", %{conn: conn} do address = insert(:contract_address) - TestHelper.get_eip1967_implementation_zero_addresses() - request = post(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}/query-read-method", %{ "contract_type" => "regular", @@ -2909,8 +2893,6 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do custom_abi ) - TestHelper.get_eip1967_implementation_zero_addresses() - expect( EthereumJSONRPC.Mox, :json_rpc, @@ -3050,8 +3032,6 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do test "return 404 on unverified contract", %{conn: conn} do address = insert(:contract_address) - TestHelper.get_eip1967_implementation_zero_addresses() - request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}/methods-read-proxy") assert %{"message" => "Not found"} = json_response(request, 404) end @@ -3421,7 +3401,6 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do test "return 404 on unverified contract", %{conn: conn} do address = insert(:contract_address) - TestHelper.get_eip1967_implementation_zero_addresses() request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}/methods-write-proxy") assert %{"message" => "Not found"} = json_response(request, 404) end diff --git a/apps/block_scout_web/test/block_scout_web/views/address_view_test.exs b/apps/block_scout_web/test/block_scout_web/views/address_view_test.exs index c8e2393b0b..e33ea526d7 100644 --- a/apps/block_scout_web/test/block_scout_web/views/address_view_test.exs +++ b/apps/block_scout_web/test/block_scout_web/views/address_view_test.exs @@ -176,23 +176,6 @@ defmodule BlockScoutWeb.AddressViewTest do assert "" = AddressView.balance_percentage(address, nil) end - describe "contract?/1" do - test "with a smart contract" do - {:ok, code} = Data.cast("0x000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef") - address = insert(:address, contract_code: code) - assert AddressView.contract?(address) - end - - test "with an account" do - address = insert(:address, contract_code: nil) - refute AddressView.contract?(address) - end - - test "with nil address" do - assert AddressView.contract?(nil) - end - end - describe "hash/1" do test "gives a string version of an address's hash" do address = %Address{ diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 6ce7ec66df..6af211157c 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -85,8 +85,6 @@ defmodule Explorer.Chain do alias Explorer.Chain.Fetcher.{CheckBytecodeMatchingOnDemand, LookUpSmartContractSourcesOnDemand} alias Explorer.Chain.Import.Runner alias Explorer.Chain.InternalTransaction.{CallType, Type} - alias Explorer.Chain.SmartContract.Proxy - alias Explorer.Chain.SmartContract.Proxy.Models.Implementation alias Explorer.Market.MarketHistoryCache alias Explorer.{PagingOptions, Repo} @@ -1136,7 +1134,8 @@ defmodule Explorer.Chain do options |> Keyword.get(:necessity_by_association, %{}) |> Map.merge(%{ - [smart_contract: :smart_contract_additional_sources] => :optional + [smart_contract: :smart_contract_additional_sources] => :optional, + :proxy_implementations => :optional }) query = @@ -1171,20 +1170,7 @@ defmodule Explorer.Chain do nil ) - {implementation_address_hashes, _} = - Implementation.get_implementation( - %{ - updated: %SmartContract{ - address_hash: hash - }, - implementation_updated_at: nil, - implementation_address_fetched?: false, - refetch_necessity_checked?: false - }, - Keyword.put(options, :proxy_without_abi?, true) - ) - - add_implementation_and_bytecode_twin_to_result(address_result, implementation_address_hashes, hash, options) + add_bytecode_twin_to_result(address_result, hash, options) end _ -> @@ -1206,31 +1192,12 @@ defmodule Explorer.Chain do end end - defp add_implementation_and_bytecode_twin_to_result(address_result, implementation_address_hashes, hash, options) do - # implementation is added only in the case when mapping proxy to implementation is 1:1 (excluding Diamond proxy) - {implementation_smart_contract, implementation_address_hash} = - if implementation_address_hashes && Enum.count(implementation_address_hashes) == 1 do - implementation_address_hash = implementation_address_hashes |> Enum.at(0) - - implementation_smart_contract = - implementation_address_hash - |> Proxy.implementation_to_smart_contract(options) - - {implementation_smart_contract, implementation_address_hash} - else - {nil, nil} - end - + defp add_bytecode_twin_to_result(address_result, hash, options) do address_verified_bytecode_twin_contract = - implementation_smart_contract || - SmartContract.get_address_verified_bytecode_twin_contract(hash, options).verified_contract + SmartContract.get_address_verified_bytecode_twin_contract(hash, options).verified_contract address_result |> SmartContract.add_bytecode_twin_info_to_contract(address_verified_bytecode_twin_contract, hash) - |> (&if(is_nil(implementation_smart_contract), - do: &1, - else: SmartContract.add_implementation_info_to_contract(&1, implementation_address_hash) - )).() end @spec find_decompiled_contract_address(Hash.Address.t()) :: {:ok, Address.t()} | {:error, :not_found} @@ -1849,10 +1816,6 @@ defmodule Explorer.Chain do Decimal.mult(tokens, fiat_value) end - def contract?(%{contract_code: nil}), do: false - - def contract?(%{contract_code: _}), do: true - @doc """ Returns a stream of unfetched `t:Explorer.Chain.Address.CoinBalance.t/0`. diff --git a/apps/explorer/lib/explorer/chain/address.ex b/apps/explorer/lib/explorer/chain/address.ex index a4749aab9c..ad8f66980d 100644 --- a/apps/explorer/lib/explorer/chain/address.ex +++ b/apps/explorer/lib/explorer/chain/address.ex @@ -26,6 +26,7 @@ defmodule Explorer.Chain.Address do } alias Explorer.Chain.Cache.{Accounts, NetVersion} + alias Explorer.Chain.SmartContract.Proxy alias Explorer.Chain.SmartContract.Proxy.Models.Implementation @optional_attrs ~w(contract_code fetched_coin_balance fetched_coin_balance_block_number nonce decompiled verified gas_used transactions_count token_transfers_count)a @@ -93,6 +94,8 @@ defmodule Explorer.Chain.Address do field(:gas_used, :integer) field(:ens_domain_name, :string, virtual: true) field(:metadata, :any, virtual: true) + + # todo: remove virtual field for a single implementation when frontend is bound to "implementations" object value in API field(:implementation, :any, virtual: true) has_one(:smart_contract, SmartContract, references: :hash) @@ -460,4 +463,24 @@ defmodule Explorer.Chain.Address do timeout: @timeout ) end + + @doc """ + Prepares implementations object and proxy type from address + """ + @spec parse_implementation_and_proxy_type(__MODULE__.t()) :: {list(), String.t() | nil} + def parse_implementation_and_proxy_type(address) do + with %__MODULE__{ + proxy_implementations: %Implementation{ + address_hashes: address_hashes, + names: names, + proxy_type: proxy_type + } + } <- address, + false <- address_hashes && Enum.empty?(address_hashes) do + {Proxy.proxy_object_info(address_hashes, names), proxy_type} + else + _ -> + {[], nil} + end + end end diff --git a/apps/explorer/lib/explorer/chain/address/counters.ex b/apps/explorer/lib/explorer/chain/address/counters.ex index d58a55ed73..9ac501b434 100644 --- a/apps/explorer/lib/explorer/chain/address/counters.ex +++ b/apps/explorer/lib/explorer/chain/address/counters.ex @@ -228,7 +228,7 @@ defmodule Explorer.Chain.Address.Counters do @spec address_to_gas_usage_count(Address.t()) :: Decimal.t() | nil def address_to_gas_usage_count(address) do - if Chain.contract?(address) do + if Address.smart_contract?(address) do incoming_transaction_gas_usage = address_to_incoming_transaction_gas_usage(address.hash) cond do diff --git a/apps/explorer/lib/explorer/chain/smart_contract.ex b/apps/explorer/lib/explorer/chain/smart_contract.ex index 121b3c6863..302ed4dda8 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract.ex @@ -594,7 +594,7 @@ defmodule Explorer.Chain.SmartContract do refetch_necessity_checked?: false } - {implementation_address_hash, _} = + {implementation_address_hash, _names, _proxy_type} = Implementation.get_implementation( smart_contract, Keyword.put(options, :proxy_without_abi?, true) @@ -618,7 +618,7 @@ defmodule Explorer.Chain.SmartContract do def compose_address_for_unverified_smart_contract(address_result, _hash, _options), do: address_result def single_implementation_smart_contract_from_proxy(proxy_hash, options) do - {implementation_address_hashes, _} = Implementation.get_implementation(proxy_hash, options) + {implementation_address_hashes, _names, _proxy_type} = Implementation.get_implementation(proxy_hash, options) if implementation_address_hashes && Enum.count(implementation_address_hashes) == 1 do implementation_address_hashes @@ -740,21 +740,6 @@ defmodule Explorer.Chain.SmartContract do |> Map.put(:smart_contract, address_verified_bytecode_twin_contract_updated) end - def add_implementation_info_to_contract(address_result, nil), do: address_result - - def add_implementation_info_to_contract( - %Address{smart_contract: smart_contract} = address_result, - implementation_address_hash - ) - when not is_nil(smart_contract) do - implementation = Map.put(smart_contract, :address_hash, implementation_address_hash) - - address_result - |> Map.put(:implementation, implementation) - end - - def add_implementation_info_to_contract(address_result, _), do: address_result - @doc """ Inserts a new smart contract and associated data into the database. @@ -958,7 +943,7 @@ defmodule Explorer.Chain.SmartContract do with true <- is_nil(current_smart_contract), {:ok, address} <- Chain.hash_to_address(address_hash), - true <- Chain.contract?(address) do + true <- Address.smart_contract?(address) do {implementation_smart_contract, implementation_address_fetched?} = if fetch_implementation? do implementation_smart_contract = diff --git a/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex b/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex index 35c874c6ee..f4ea719eb7 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex @@ -4,8 +4,9 @@ defmodule Explorer.Chain.SmartContract.Proxy do """ alias EthereumJSONRPC.Contract - alias Explorer.Chain.{Hash, SmartContract} + alias Explorer.Chain.{Address, Hash, SmartContract} alias Explorer.Chain.SmartContract.Proxy + alias Explorer.Chain.SmartContract.Proxy.Models.Implementation alias Explorer.Chain.SmartContract.Proxy.{ Basic, @@ -51,7 +52,7 @@ defmodule Explorer.Chain.SmartContract.Proxy do Fetches into DB proxy contract implementation's address and name from different proxy patterns """ @spec fetch_implementation_address_hash(Hash.Address.t(), list(), options) :: - {[String.t()] | :empty | :error, [String.t()] | :empty | :error} + Implementation.t() | :empty | :error def fetch_implementation_address_hash(proxy_address_hash, proxy_abi, options) when not is_nil(proxy_address_hash) do %{implementation_address_hash_strings: implementation_address_hash_strings, proxy_type: proxy_type} = @@ -70,7 +71,7 @@ defmodule Explorer.Chain.SmartContract.Proxy do end def fetch_implementation_address_hash(_, _, _) do - {:empty, :empty} + :empty end @doc """ @@ -79,32 +80,37 @@ defmodule Explorer.Chain.SmartContract.Proxy do @spec proxy_contract?(SmartContract.t(), Keyword.t()) :: boolean() def proxy_contract?(smart_contract, options \\ []) do {:ok, burn_address_hash} = string_to_address_hash(SmartContract.burn_address_hash_string()) - implementation = get_proxy_implementations(smart_contract.address_hash) + proxy_implementations = get_proxy_implementations(smart_contract.address_hash) - with false <- is_nil(implementation), - false <- Enum.empty?(implementation.address_hashes), - implementation_address_hash = Enum.at(implementation.address_hashes, 0), + with false <- is_nil(proxy_implementations), + false <- Enum.empty?(proxy_implementations.address_hashes), + implementation_address_hash = Enum.at(proxy_implementations.address_hashes, 0), false <- implementation_address_hash.bytes == burn_address_hash.bytes do true else _ -> - {implementation_address_hash_strings, _implementation_names} = get_implementation(smart_contract, options) - - with false <- is_nil(implementation_address_hash_strings), - false <- Enum.empty?(implementation_address_hash_strings) do - implementation_address_hash_strings - |> Enum.reduce_while(false, fn implementation_address_hash_string, acc -> - with {:ok, implementation_address_hash} <- string_to_address_hash(implementation_address_hash_string), - false <- implementation_address_hash.bytes == burn_address_hash.bytes do - {:halt, true} - else - _ -> - {:cont, acc} - end - end) + if options[:skip_implementation_fetch?] do + false else - _ -> - false + {implementation_address_hash_strings, _implementation_names, _proxy_type} = + get_implementation(smart_contract, options) + + with false <- is_nil(implementation_address_hash_strings), + false <- Enum.empty?(implementation_address_hash_strings) do + implementation_address_hash_strings + |> Enum.reduce_while(false, fn implementation_address_hash_string, acc -> + with {:ok, implementation_address_hash} <- string_to_address_hash(implementation_address_hash_string), + false <- implementation_address_hash.bytes == burn_address_hash.bytes do + {:halt, true} + else + _ -> + {:cont, acc} + end + end) + else + _ -> + false + end end end end @@ -138,7 +144,7 @@ defmodule Explorer.Chain.SmartContract.Proxy do options ) when not is_nil(proxy_address_hash) and not is_nil(abi) do - {implementation_address_hash_strings, _names} = get_implementation(smart_contract, options) + {implementation_address_hash_strings, _names, _proxy_type} = get_implementation(smart_contract, options) implementation_address_hash_strings |> Enum.reduce([], fn implementation_address_hash_string, acc -> @@ -452,4 +458,39 @@ defmodule Explorer.Chain.SmartContract.Proxy do |> join_associations(necessity_by_association) |> select_repo(options).one(timeout: 10_000) end + + @doc """ + Retrieves formatted proxy object based on its implementation addresses and names. + + ## Parameters + + * `implementation_addresses` - A list of implementation addresses for the proxy object. + * `implementation_names` - A list of implementation names for the proxy object. + + ## Returns + + A list of maps containing information about the proxy object. + + """ + @spec proxy_object_info([String.t() | Hash.Address.t()], [String.t() | nil]) :: [map()] + def proxy_object_info([], []), do: [] + + def proxy_object_info(implementation_addresses, implementation_names) do + implementation_addresses + |> Enum.zip(implementation_names) + |> Enum.reduce([], fn {address, name}, acc -> + case address do + %Hash{} = address_hash -> + [%{"address" => Address.checksum(address_hash), "name" => name} | acc] + + _ -> + with {:ok, address_hash} <- string_to_address_hash(address), + checksummed_address <- Address.checksum(address_hash) do + [%{"address" => checksummed_address, "name" => name} | acc] + else + _ -> acc + end + end + end) + end end diff --git a/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex b/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex index 91496e870b..f6e4d9c501 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex @@ -7,10 +7,17 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do use Explorer.Schema + import Ecto.Query, + only: [ + from: 2, + select: 3 + ] + import Explorer.Chain, only: [select_repo: 1, string_to_address_hash: 1] alias Explorer.Chain.{Address, Hash, SmartContract} alias Explorer.Chain.SmartContract.Proxy + alias Explorer.Chain.SmartContract.Proxy.Models.Implementation alias Explorer.Counters.AverageBlockTime alias Explorer.Repo alias Timex.Duration @@ -78,14 +85,9 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do Returns all implementations for the given smart-contract address hash """ @spec get_proxy_implementations(Hash.Address.t() | nil, Keyword.t()) :: __MODULE__.t() | nil - def get_proxy_implementations(address_hash, options \\ []) do - all_implementations_query = - from( - p in __MODULE__, - where: p.proxy_address_hash == ^address_hash - ) - - all_implementations_query + def get_proxy_implementations(proxy_address_hash, options \\ []) do + proxy_address_hash + |> get_proxy_implementations_query() |> select_repo(options).one() end @@ -93,22 +95,24 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do Returns the last implementation updated_at for the given smart-contract address hash """ @spec get_proxy_implementation_updated_at(Hash.Address.t() | nil, Keyword.t()) :: DateTime.t() - def get_proxy_implementation_updated_at(address_hash, options) do - updated_at_query = - from( - p in __MODULE__, - where: p.proxy_address_hash == ^address_hash, - select: p.updated_at - ) - - updated_at_query + def get_proxy_implementation_updated_at(proxy_address_hash, options) do + proxy_address_hash + |> get_proxy_implementations_query() + |> select([p], p.updated_at) |> select_repo(options).one() end + defp get_proxy_implementations_query(proxy_address_hash) do + from( + p in __MODULE__, + where: p.proxy_address_hash == ^proxy_address_hash + ) + end + @doc """ - Returns implementation address and name of the given SmartContract by hash address + Returns implementation address, name and proxy type for the given SmartContract """ - @spec get_implementation(any(), any()) :: {any(), any()} + @spec get_implementation(any(), any()) :: {any(), any(), atom() | nil} def get_implementation(smart_contract, options \\ []) def get_implementation( @@ -152,6 +156,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do ) end + # credo:disable-for-next-line Credo.Check.Refactor.CyclomaticComplexity def get_implementation( %{ updated: %SmartContract{ @@ -164,14 +169,15 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do }, options ) do - {implementation_addresses_hash_from_db, implementation_names_from_db, implementation_updated_at_from_db} = - implementation_from_db(address_hash, options) + proxy_implementations = get_proxy_implementations(address_hash, options) - implementation_updated_at = implementation_updated_at || implementation_updated_at_from_db + implementation_updated_at = implementation_updated_at || (proxy_implementations && proxy_implementations.updated_at) if fetch_implementation?(implementation_address_fetched?, refetch_necessity_checked?, implementation_updated_at) do get_implementation_address_hash_task = Task.async(fn -> + # Here and only here we fetch implementations for the given address + # using requests to the JSON RPC node for known proxy patterns result = Proxy.fetch_implementation_address_hash(address_hash, abi, options) callback = Keyword.get(options, :callback, nil) @@ -187,41 +193,34 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do case Task.yield(get_implementation_address_hash_task, timeout) || Task.ignore(get_implementation_address_hash_task) do - {:ok, {:empty, :empty}} -> - {[], []} + {:ok, :empty} -> + {[], [], nil} - {:ok, {:error, :error}} -> - {db_implementation_data_converter(implementation_addresses_hash_from_db), - db_implementation_data_converter(implementation_names_from_db)} + {:ok, :error} -> + format_proxy_implementations_response(proxy_implementations) - {:ok, {address_hash, _name} = result} when not is_nil(address_hash) -> - result + {:ok, %Implementation{} = result} -> + format_proxy_implementations_response(result) _ -> - {db_implementation_data_converter(implementation_addresses_hash_from_db), - db_implementation_data_converter(implementation_names_from_db)} + format_proxy_implementations_response(proxy_implementations) end else - {db_implementation_data_converter(implementation_addresses_hash_from_db), - db_implementation_data_converter(implementation_names_from_db)} + format_proxy_implementations_response(proxy_implementations) end end - def get_implementation(_, _), do: {[], []} + def get_implementation(_, _), do: {[], [], nil} defp fetch_implementation?(implementation_address_fetched?, refetch_necessity_checked?, implementation_updated_at) do (!implementation_address_fetched? || !refetch_necessity_checked?) && check_implementation_refetch_necessity(implementation_updated_at) end - defp implementation_from_db(address_hash, options) do - proxy_implementations = get_proxy_implementations(address_hash, options) - - if proxy_implementations do - {proxy_implementations.address_hashes, proxy_implementations.names, proxy_implementations.updated_at} - else - {[], [], nil} - end + defp format_proxy_implementations_response(proxy_implementations) do + {(proxy_implementations && db_implementation_data_converter(proxy_implementations.address_hashes)) || [], + (proxy_implementations && db_implementation_data_converter(proxy_implementations.names)) || [], + proxy_implementations && proxy_implementations.proxy_type} end @doc """ @@ -279,9 +278,9 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do Saves proxy's implementation into the DB """ @spec save_implementation_data([String.t()], Hash.Address.t(), atom() | nil, Keyword.t()) :: - {[String.t()], [String.t()]} | {:empty, :empty} | {:error, :error} + Implementation.t() | :empty | :error def save_implementation_data(:error, _proxy_address_hash, _proxy_type, _options) do - {:error, :error} + :error end def save_implementation_data(implementation_address_hash_strings, proxy_address_hash, proxy_type, options) @@ -289,7 +288,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do implementation_address_hash_strings == [] do upsert_implementation(proxy_address_hash, proxy_type, [], [], options) - {:empty, :empty} + :empty end def save_implementation_data( @@ -301,7 +300,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do when is_burn_signature(empty_implementation_address_hash_string) do upsert_implementation(proxy_address_hash, proxy_type, [], [], options) - {:empty, :empty} + :empty end def save_implementation_data( @@ -331,17 +330,22 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do |> Enum.unzip() if Enum.empty?(implementation_addresses) do - {:empty, :empty} + :empty else - upsert_implementation( - proxy_address_hash, - proxy_type, - implementation_addresses, - implementation_names, - options - ) + case upsert_implementation( + proxy_address_hash, + proxy_type, + implementation_addresses, + implementation_names, + options + ) do + {:ok, result} -> + result - {implementation_addresses, implementation_names} + {:error, error} -> + Logger.error("Error while upserting proxy implementations data into the DB: #{inspect(error)}") + :error + end end end diff --git a/apps/explorer/lib/explorer/chain/transaction.ex b/apps/explorer/lib/explorer/chain/transaction.ex index 05916ff894..e6e48e2fa8 100644 --- a/apps/explorer/lib/explorer/chain/transaction.ex +++ b/apps/explorer/lib/explorer/chain/transaction.ex @@ -1873,7 +1873,7 @@ defmodule Explorer.Chain.Transaction do @spec where_transactions_to_from(Hash.Address.t()) :: any() def where_transactions_to_from(address_hash) do with {:ok, address} <- Chain.hash_to_address(address_hash), - true <- Chain.contract?(address) do + true <- Address.smart_contract?(address) do dynamic([transaction], transaction.to_address_hash == ^address_hash) else _ -> diff --git a/apps/explorer/lib/explorer/smart_contract/helper.ex b/apps/explorer/lib/explorer/smart_contract/helper.ex index 38c2026c61..90530ee756 100644 --- a/apps/explorer/lib/explorer/smart_contract/helper.ex +++ b/apps/explorer/lib/explorer/smart_contract/helper.ex @@ -4,10 +4,14 @@ defmodule Explorer.SmartContract.Helper do """ alias Explorer.{Chain, Helper} - alias Explorer.Chain.{Hash, SmartContract} + alias Explorer.Chain.{Address, Hash, SmartContract} + alias Explorer.Chain.SmartContract.Proxy + alias Explorer.Chain.SmartContract.Proxy.Models.Implementation alias Explorer.SmartContract.Writer alias Phoenix.HTML + @api_true [api?: true] + def queriable_method?(method) do method["constant"] || method["stateMutability"] == "view" || method["stateMutability"] == "pure" end @@ -210,4 +214,46 @@ defmodule Explorer.SmartContract.Helper do def prepare_license_type(binary) when is_binary(binary), do: Helper.parse_integer(binary) || binary def prepare_license_type(_), do: nil + + @doc """ + Pre-fetches implementation for unverified smart contract or verified proxy smart-contract + """ + @spec pre_fetch_implementations(Address.t()) :: {any(), atom() | nil} + def pre_fetch_implementations(address) do + {implementation_address_hashes, implementation_names, proxy_type} = + with {:verified_smart_contract, %SmartContract{}} <- {:verified_smart_contract, address.smart_contract}, + {:proxy?, true} <- {:proxy?, address_is_proxy?(address, @api_true)} do + Implementation.get_implementation(address.smart_contract, @api_true) + else + {:verified_smart_contract, _} -> + if Address.smart_contract?(address) do + smart_contract = %SmartContract{ + address_hash: address.hash + } + + Implementation.get_implementation(smart_contract, @api_true) + else + {[], [], nil} + end + + {:proxy?, false} -> + {[], [], nil} + end + + implementations = Proxy.proxy_object_info(implementation_address_hashes, implementation_names) + + {implementations, proxy_type} + end + + @doc """ + Checks if given address is proxy smart contract + """ + @spec address_is_proxy?(Address.t(), list()) :: boolean() + def address_is_proxy?(address, options \\ []) + + def address_is_proxy?(%Address{smart_contract: %SmartContract{} = smart_contract}, options) do + Proxy.proxy_contract?(smart_contract, options) + end + + def address_is_proxy?(%Address{smart_contract: _}, _), do: false end diff --git a/apps/explorer/test/explorer/chain/smart_contract/proxy/models/implementation_test.exs b/apps/explorer/test/explorer/chain/smart_contract/proxy/models/implementation_test.exs index 477bb788e4..7893139d5b 100644 --- a/apps/explorer/test/explorer/chain/smart_contract/proxy/models/implementation_test.exs +++ b/apps/explorer/test/explorer/chain/smart_contract/proxy/models/implementation_test.exs @@ -28,7 +28,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do # fetch nil implementation and don't save it to db TestHelper.get_eip1967_implementation_zero_addresses() - assert {[], []} = Implementation.get_implementation(smart_contract) + assert {[], [], nil} = Implementation.get_implementation(smart_contract) verify!(EthereumJSONRPC.Mox) assert_empty_implementation(smart_contract.address_hash) @@ -44,7 +44,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do expect_address_in_oz_slot_response(string_implementation_address_hash) - assert {[^string_implementation_address_hash], ["implementation"]} = + assert {[^string_implementation_address_hash], ["implementation"], :eip1967} = Implementation.get_implementation(smart_contract) verify!(EthereumJSONRPC.Mox) @@ -57,7 +57,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do TestHelper.get_eip1967_implementation_error_response() - assert {[^string_implementation_address_hash], ["implementation"]} = + assert {[^string_implementation_address_hash], ["implementation"], :eip1967} = Implementation.get_implementation(smart_contract) verify!(EthereumJSONRPC.Mox) @@ -78,7 +78,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do Application.put_env(:explorer, :proxy, proxy) - assert {[^string_implementation_address_hash], ["implementation"]} = + assert {[^string_implementation_address_hash], ["implementation"], :eip1967} = Implementation.get_implementation(smart_contract) {contract_2, _} = SmartContract.address_hash_to_smart_contract_with_bytecode_twin(smart_contract.address_hash) @@ -96,7 +96,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do TestHelper.get_eip1967_implementation_zero_addresses() - assert {[], []} = Implementation.get_implementation(smart_contract) + assert {[], [], nil} = Implementation.get_implementation(smart_contract) verify!(EthereumJSONRPC.Mox) @@ -106,7 +106,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do test "get_implementation/1 for twins contract" do # return nils for nil - assert {[], []} = Implementation.get_implementation(nil) + assert {[], [], nil} = Implementation.get_implementation(nil) smart_contract = insert(:smart_contract) twin_address = insert(:contract_address) @@ -123,11 +123,11 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do Application.put_env(:explorer, :proxy, proxy) # fetch nil implementation - assert {[], []} = Implementation.get_implementation(bytecode_twin) + assert {[], [], :unknown} = Implementation.get_implementation(bytecode_twin) verify!(EthereumJSONRPC.Mox) refute_implementations(smart_contract.address_hash) - assert {[], []} = Implementation.get_implementation(bytecode_twin) + assert {[], [], :unknown} = Implementation.get_implementation(bytecode_twin) verify!(EthereumJSONRPC.Mox) refute_implementations(smart_contract.address_hash) @@ -143,7 +143,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do expect_address_in_oz_slot_response(string_implementation_address_hash) - assert {[^string_implementation_address_hash], ["implementation"]} = + assert {[^string_implementation_address_hash], ["implementation"], :eip1967} = Implementation.get_implementation(bytecode_twin) verify!(EthereumJSONRPC.Mox) @@ -158,7 +158,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do refute_implementations(smart_contract.address_hash) - assert {[^string_implementation_address_hash], ["implementation"]} = + assert {[^string_implementation_address_hash], ["implementation"], :eip1967} = Implementation.get_implementation(bytecode_twin) verify!(EthereumJSONRPC.Mox) @@ -171,11 +171,11 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do _implementation_smart_contract = insert(:smart_contract, name: "implementation") # fetch nil implementation - assert {[], []} = Implementation.get_implementation(bytecode_twin) + assert {[], [], nil} = Implementation.get_implementation(bytecode_twin) verify!(EthereumJSONRPC.Mox) refute_implementations(smart_contract.address_hash) - assert {[], []} = Implementation.get_implementation(bytecode_twin) + assert {[], [], nil} = Implementation.get_implementation(bytecode_twin) verify!(EthereumJSONRPC.Mox) refute_implementations(smart_contract.address_hash) @@ -192,7 +192,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do # expect_address_in_oz_slot_response(string_implementation_address_hash) - # assert {^string_implementation_address_hash, "implementation"} = Implementation.get_implementation(bytecode_twin) + # assert {^string_implementation_address_hash, "implementation", :eip1967} = Implementation.get_implementation(bytecode_twin) # verify!(EthereumJSONRPC.Mox) @@ -200,7 +200,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do # TestHelper.get_eip1967_implementation_error_response() - # assert {^string_implementation_address_hash, "implementation"} = Implementation.get_implementation(bytecode_twin) + # assert {^string_implementation_address_hash, "implementation", :eip1967} = Implementation.get_implementation(bytecode_twin) # verify!(EthereumJSONRPC.Mox) @@ -233,12 +233,12 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do # # fetch nil implementation # TestHelper.get_eip1967_implementation_zero_addresses() - # assert {nil, nil} = Implementation.get_implementation(bytecode_twin) + # assert {[], [], nil} = Implementation.get_implementation(bytecode_twin) # verify!(EthereumJSONRPC.Mox) # refute_implementations(smart_contract.address_hash) # TestHelper.get_eip1967_implementation_zero_addresses() - # assert {nil, nil} = Implementation.get_implementation(bytecode_twin) + # assert {[], [], nil} = Implementation.get_implementation(bytecode_twin) # verify!(EthereumJSONRPC.Mox) # refute_implementations(smart_contract.address_hash) @@ -246,7 +246,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do # expect_address_in_oz_slot_response(string_implementation_address_hash) - # assert {^string_implementation_address_hash, "implementation"} = Implementation.get_implementation(bytecode_twin) + # assert {^string_implementation_address_hash, "implementation", :eip1967} = Implementation.get_implementation(bytecode_twin) # verify!(EthereumJSONRPC.Mox) @@ -254,7 +254,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do # TestHelper.get_eip1967_implementation_zero_addresses() - # assert {nil, nil} = Implementation.get_implementation(bytecode_twin) + # assert {[], [], nil} = Implementation.get_implementation(bytecode_twin) # verify!(EthereumJSONRPC.Mox) diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs index 3f242e7267..a57dac010c 100644 --- a/apps/explorer/test/explorer/chain_test.exs +++ b/apps/explorer/test/explorer/chain_test.exs @@ -2599,7 +2599,8 @@ defmodule Explorer.ChainTest do :contracts_creation_internal_transaction, :contracts_creation_transaction, :token, - [smart_contract: :smart_contract_additional_sources] + [smart_contract: :smart_contract_additional_sources], + :proxy_implementations ]) options = [ @@ -2612,8 +2613,6 @@ defmodule Explorer.ChainTest do } ] - TestHelper.get_eip1967_implementation_zero_addresses() - response = Chain.find_contract_address(address.hash, options, true) assert response == {:ok, address}
- <%= render BlockScoutWeb.AddressView, "_link.html", address: from_address, contract: BlockScoutWeb.AddressView.contract?(from_address), use_custom_tooltip: false, trimmed: false %> + <%= render BlockScoutWeb.AddressView, "_link.html", address: from_address, contract: Address.smart_contract?(from_address), use_custom_tooltip: false, trimmed: false %> <%= render BlockScoutWeb.AddressView, "_labels.html", tags: from_tags %> @@ -24,7 +24,7 @@ style: "position: relative;" %> To - <%= render BlockScoutWeb.AddressView, "_link.html", address: to_address, contract: BlockScoutWeb.AddressView.contract?(to_address), use_custom_tooltip: false, trimmed: false %> + <%= render BlockScoutWeb.AddressView, "_link.html", address: to_address, contract: Address.smart_contract?(to_address), use_custom_tooltip: false, trimmed: false %> <%= render BlockScoutWeb.AddressView, "_labels.html", tags: to_tags %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex index 3ba1c80494..b529f940a2 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex @@ -245,7 +245,7 @@ text: gettext("Address (external or contract) sending the transaction.") %> <%= gettext "From" %>
- <%= render BlockScoutWeb.AddressView, "_link.html", address: from_address, contract: BlockScoutWeb.AddressView.contract?(from_address), use_custom_tooltip: false, trimmed: false %> + <%= render BlockScoutWeb.AddressView, "_link.html", address: from_address, contract: Address.smart_contract?(from_address), use_custom_tooltip: false, trimmed: false %> <%= render BlockScoutWeb.AddressView, "_labels.html", tags: @from_tags %> <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html", additional_classes: ["btn-copy-icon-small", "btn-copy-icon-custom", "btn-copy-icon-no-borders"], @@ -261,7 +261,7 @@
<%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", text: gettext("Address (external or contract) receiving the transaction.") %> - <%= if BlockScoutWeb.AddressView.contract?(to_address) && !created_address_hash do %> + <%= if Address.smart_contract?(to_address) && !created_address_hash do %> <%= gettext "Interacted With (To)" %> <% else %> <%= gettext "To" %> @@ -271,7 +271,7 @@ <%= cond do %> <% created_address_hash -> %> [<%= gettext("Contract") %>  - <%= render BlockScoutWeb.AddressView, "_link.html", address: to_address, contract: BlockScoutWeb.AddressView.contract?(to_address), use_custom_tooltip: false, trimmed: false %> + <%= render BlockScoutWeb.AddressView, "_link.html", address: to_address, contract: Address.smart_contract?(to_address), use_custom_tooltip: false, trimmed: false %> <%= render BlockScoutWeb.AddressView, "_labels.html", tags: @to_tags %>  <%= gettext("created") %>] <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html", @@ -280,7 +280,7 @@ aria_label: gettext("Copy To Address"), title: gettext("Copy To Address") %> <% recipient_address_hash -> %> - <%= render BlockScoutWeb.AddressView, "_link.html", address: to_address, contract: BlockScoutWeb.AddressView.contract?(to_address), use_custom_tooltip: false, trimmed: false %> + <%= render BlockScoutWeb.AddressView, "_link.html", address: to_address, contract: Address.smart_contract?(to_address), use_custom_tooltip: false, trimmed: false %> <%= render BlockScoutWeb.AddressView, "_labels.html", tags: @to_tags %> <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html", additional_classes: ["btn-copy-icon-small", "btn-copy-icon-custom", "btn-copy-icon-no-borders"], diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction_state/_state_change.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction_state/_state_change.html.eex index 67a8e63ea3..bd4abe9c19 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction_state/_state_change.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction_state/_state_change.html.eex @@ -9,7 +9,7 @@
- <%= render BlockScoutWeb.AddressView, "_link.html", address: @address, contract: BlockScoutWeb.AddressView.contract?(@address), use_custom_tooltip: false %> + <%= render BlockScoutWeb.AddressView, "_link.html", address: @address, contract: Address.smart_contract?(@address), use_custom_tooltip: false %> - <%= render BlockScoutWeb.AddressView, "_link.html", address: @address, contract: BlockScoutWeb.AddressView.contract?(@address), use_custom_tooltip: false %> + <%= render BlockScoutWeb.AddressView, "_link.html", address: @address, contract: Address.smart_contract?(@address), use_custom_tooltip: false %>