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>
pull/10241/head
Victor Baranov 3 months ago committed by GitHub
parent 5785ea89be
commit 8eb6f08f1e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 18
      apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex
  2. 11
      apps/block_scout_web/lib/block_scout_web/controllers/api/v2/smart_contract_controller.ex
  3. 2
      apps/block_scout_web/lib/block_scout_web/templates/address/_tabs.html.eex
  4. 8
      apps/block_scout_web/lib/block_scout_web/templates/address/overview.html.eex
  5. 2
      apps/block_scout_web/lib/block_scout_web/templates/address_coin_balance/index.html.eex
  6. 22
      apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex
  7. 2
      apps/block_scout_web/lib/block_scout_web/templates/address_decompiled_contract/index.html.eex
  8. 2
      apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/index.html.eex
  9. 2
      apps/block_scout_web/lib/block_scout_web/templates/address_logs/index.html.eex
  10. 2
      apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/index.html.eex
  11. 2
      apps/block_scout_web/lib/block_scout_web/templates/address_read_proxy/index.html.eex
  12. 2
      apps/block_scout_web/lib/block_scout_web/templates/address_token/index.html.eex
  13. 2
      apps/block_scout_web/lib/block_scout_web/templates/address_token_transfer/index.html.eex
  14. 2
      apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex
  15. 2
      apps/block_scout_web/lib/block_scout_web/templates/address_validation/index.html.eex
  16. 2
      apps/block_scout_web/lib/block_scout_web/templates/address_withdrawal/index.html.eex
  17. 2
      apps/block_scout_web/lib/block_scout_web/templates/address_write_contract/index.html.eex
  18. 2
      apps/block_scout_web/lib/block_scout_web/templates/address_write_proxy/index.html.eex
  19. 2
      apps/block_scout_web/lib/block_scout_web/templates/block_withdrawal/_withdrawal.html.eex
  20. 2
      apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/_token_balances.html.eex
  21. 4
      apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex
  22. 2
      apps/block_scout_web/lib/block_scout_web/templates/transaction/_actions.html.eex
  23. 4
      apps/block_scout_web/lib/block_scout_web/templates/transaction/_pending_tile.html.eex
  24. 4
      apps/block_scout_web/lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex
  25. 8
      apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex
  26. 4
      apps/block_scout_web/lib/block_scout_web/templates/transaction_state/_state_change.html.eex
  27. 4
      apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex
  28. 2
      apps/block_scout_web/lib/block_scout_web/templates/withdrawal/_withdrawal.html.eex
  29. 1
      apps/block_scout_web/lib/block_scout_web/views/address_coin_balance_view.ex
  30. 1
      apps/block_scout_web/lib/block_scout_web/views/address_contract_view.ex
  31. 1
      apps/block_scout_web/lib/block_scout_web/views/address_decompiled_contract_view.ex
  32. 1
      apps/block_scout_web/lib/block_scout_web/views/address_internal_transaction_view.ex
  33. 1
      apps/block_scout_web/lib/block_scout_web/views/address_logs_view.ex
  34. 2
      apps/block_scout_web/lib/block_scout_web/views/address_read_contract_view.ex
  35. 2
      apps/block_scout_web/lib/block_scout_web/views/address_read_proxy_view.ex
  36. 1
      apps/block_scout_web/lib/block_scout_web/views/address_token_transfer_view.ex
  37. 1
      apps/block_scout_web/lib/block_scout_web/views/address_token_view.ex
  38. 1
      apps/block_scout_web/lib/block_scout_web/views/address_transaction_view.ex
  39. 2
      apps/block_scout_web/lib/block_scout_web/views/address_validation_view.ex
  40. 35
      apps/block_scout_web/lib/block_scout_web/views/address_view.ex
  41. 2
      apps/block_scout_web/lib/block_scout_web/views/address_withdrawal_view.ex
  42. 2
      apps/block_scout_web/lib/block_scout_web/views/address_write_contract_view.ex
  43. 2
      apps/block_scout_web/lib/block_scout_web/views/address_write_proxy_view.ex
  44. 33
      apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex
  45. 52
      apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex
  46. 96
      apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex
  47. 2
      apps/block_scout_web/lib/block_scout_web/views/block_withdrawal_view.ex
  48. 2
      apps/block_scout_web/lib/block_scout_web/views/tokens/holder_view.ex
  49. 2
      apps/block_scout_web/lib/block_scout_web/views/transaction_state_view.ex
  50. 1
      apps/block_scout_web/lib/block_scout_web/views/transaction_token_transfer_view.ex
  51. 2
      apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex
  52. 2
      apps/block_scout_web/lib/block_scout_web/views/withdrawal_view.ex
  53. 2
      apps/block_scout_web/test/block_scout_web/controllers/account/api/v2/user_controller_test.exs
  54. 2
      apps/block_scout_web/test/block_scout_web/controllers/address_read_contract_controller_test.exs
  55. 2
      apps/block_scout_web/test/block_scout_web/controllers/address_read_proxy_controller_test.exs
  56. 2
      apps/block_scout_web/test/block_scout_web/controllers/address_write_contract_controller_test.exs
  57. 2
      apps/block_scout_web/test/block_scout_web/controllers/address_write_proxy_controller_test.exs
  58. 3
      apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs
  59. 157
      apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs
  60. 17
      apps/block_scout_web/test/block_scout_web/views/address_view_test.exs
  61. 45
      apps/explorer/lib/explorer/chain.ex
  62. 23
      apps/explorer/lib/explorer/chain/address.ex
  63. 2
      apps/explorer/lib/explorer/chain/address/counters.ex
  64. 21
      apps/explorer/lib/explorer/chain/smart_contract.ex
  65. 59
      apps/explorer/lib/explorer/chain/smart_contract/proxy.ex
  66. 100
      apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex
  67. 2
      apps/explorer/lib/explorer/chain/transaction.ex
  68. 48
      apps/explorer/lib/explorer/smart_contract/helper.ex
  69. 36
      apps/explorer/test/explorer/chain/smart_contract/proxy/models/implementation_test.exs
  70. 5
      apps/explorer/test/explorer/chain_test.exs

@ -29,6 +29,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do
alias Explorer.Chain.{Address, Hash, Transaction} alias Explorer.Chain.{Address, Hash, Transaction}
alias Explorer.Chain.Address.Counters alias Explorer.Chain.Address.Counters
alias Explorer.Chain.Token.Instance alias Explorer.Chain.Token.Instance
alias Explorer.SmartContract.Helper, as: SmartContractHelper
alias BlockScoutWeb.API.V2.CeloView alias BlockScoutWeb.API.V2.CeloView
alias Explorer.Chain.Celo.ElectionReward, as: CeloElectionReward alias Explorer.Chain.Celo.ElectionReward, as: CeloElectionReward
@ -118,16 +119,23 @@ defmodule BlockScoutWeb.API.V2.AddressController do
action_fallback(BlockScoutWeb.API.V2.FallbackController) action_fallback(BlockScoutWeb.API.V2.FallbackController)
def address(conn, %{"address_hash_param" => address_hash_string} = params) do def address(conn, %{"address_hash_param" => address_hash_string} = params) do
with {:ok, _address_hash, address} <- validate_address(address_hash_string, params, @address_options), with {:ok, _address_hash, address} <- validate_address(address_hash_string, params, @address_options) do
fully_preloaded_address <- fully_preloaded_address =
Address.maybe_preload_smart_contract_associations(address, @contract_address_preloads, @api_true) do Address.maybe_preload_smart_contract_associations(address, @contract_address_preloads, @api_true)
CoinBalanceOnDemand.trigger_fetch(fully_preloaded_address)
{implementations, proxy_type} =
SmartContractHelper.pre_fetch_implementations(fully_preloaded_address)
CoinBalanceOnDemand.trigger_fetch(address)
ContractCodeOnDemand.trigger_fetch(address) ContractCodeOnDemand.trigger_fetch(address)
conn conn
|> put_status(200) |> 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
end end

@ -14,6 +14,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do
alias Explorer.Chain.{Address, SmartContract} alias Explorer.Chain.{Address, SmartContract}
alias Explorer.Chain.SmartContract.AuditReport alias Explorer.Chain.SmartContract.AuditReport
alias Explorer.Chain.SmartContract.Proxy.Models.Implementation alias Explorer.Chain.SmartContract.Proxy.Models.Implementation
alias Explorer.SmartContract.Helper, as: SmartContractHelper
alias Explorer.SmartContract.{Reader, Writer} alias Explorer.SmartContract.{Reader, Writer}
alias Explorer.SmartContract.Solidity.PublishHelper alias Explorer.SmartContract.Solidity.PublishHelper
alias Explorer.ThirdPartyIntegrations.SolidityScan alias Explorer.ThirdPartyIntegrations.SolidityScan
@ -22,7 +23,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do
necessity_by_association: %{ necessity_by_association: %{
:contracts_creation_internal_transaction => :optional, :contracts_creation_internal_transaction => :optional,
[smart_contract: :smart_contract_additional_sources] => :optional, [smart_contract: :smart_contract_additional_sources] => :optional,
:contracts_creation_transaction => :optional :contracts_creation_transaction => :optional,
:proxy_implementations => :optional
}, },
api?: true api?: true
] ]
@ -37,9 +39,12 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do
_ <- PublishHelper.sourcify_check(address_hash_string), _ <- PublishHelper.sourcify_check(address_hash_string),
{:not_found, {:ok, address}} <- {:not_found, {:ok, address}} <-
{:not_found, Chain.find_contract_address(address_hash, @smart_contract_address_options, false)} do {:not_found, Chain.find_contract_address(address_hash, @smart_contract_address_options, false)} do
{implementations, proxy_type} =
SmartContractHelper.pre_fetch_implementations(address)
conn conn
|> put_status(200) |> put_status(200)
|> render(:smart_contract, %{address: address}) |> render(:smart_contract, %{address: address, implementations: implementations, proxy_type: proxy_type})
end end
end end
@ -206,7 +211,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do
end end
@doc """ @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()) :: @spec solidityscan_report(Plug.Conn.t(), map()) ::
{:address, {:error, :not_found}} {:address, {:error, :not_found}}

@ -59,7 +59,7 @@
to: AccessHelper.get_path(@conn, :address_validation_path, :index, @address.hash) to: AccessHelper.get_path(@conn, :address_validation_path, :index, @address.hash)
) %> ) %>
<% end %> <% end %>
<%= if contract?(@address) do %> <%= if Address.smart_contract?(@address) do %>
<%= link( <%= link(
to: AccessHelper.get_path(@conn, :address_contract_path, :index, @address.hash), to: AccessHelper.get_path(@conn, :address_contract_path, :index, @address.hash),
class: "card-tab #{tab_status("contracts", @conn.request_path)}") do %> class: "card-tab #{tab_status("contracts", @conn.request_path)}") do %>

@ -40,7 +40,7 @@
<%= render BlockScoutWeb.CommonComponentsView, "_btn_qr_code.html" %> <%= render BlockScoutWeb.CommonComponentsView, "_btn_qr_code.html" %>
</span> </span>
</h1> </h1>
<h3 class="address-detail-hash-title mb-4 <%= if BlockScoutWeb.AddressView.contract?(@address) do %>contract-address<% end %>" data-test="address_detail_hash"><%= @address %></h3> <h3 class="address-detail-hash-title mb-4 <%= if Address.smart_contract?(@address) do %>contract-address<% end %>" data-test="address_detail_hash"><%= @address %></h3>
<!-- Verify in other explorers --> <!-- Verify in other explorers -->
<!-- <!--
<%# <%= render "_verify_other_explorers.html", hash: @address.hash, type: "address" %> %> <%# <%= render "_verify_other_explorers.html", hash: @address.hash, type: "address" %> %>
@ -67,7 +67,7 @@
</dd> </dd>
</dl> </dl>
<% address_name -> %> <% address_name -> %>
<%= if contract?(@address) do %> <%= if Address.smart_contract?(@address) do %>
<dl class="row"> <dl class="row">
<dt class="col-sm-4 col-md-4 col-lg-3 text-muted"> <dt class="col-sm-4 col-md-4 col-lg-3 text-muted">
<%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html",
@ -94,7 +94,7 @@
<% end %> <% end %>
<!-- Creator --> <!-- Creator -->
<% from_address_hash = from_address_hash(@address) %> <% from_address_hash = from_address_hash(@address) %>
<%= if contract?(@address) do %> <%= if Address.smart_contract?(@address) do %>
<dl class="row"> <dl class="row">
<dt class="col-sm-4 col-md-4 col-lg-3 text-muted"> <dt class="col-sm-4 col-md-4 col-lg-3 text-muted">
<%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html",
@ -124,7 +124,7 @@
<% end %> <% end %>
<!-- Implementation --> <!-- Implementation -->
<%= if @is_proxy do %> <%= 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) %> <% implementation_address_ = Enum.at(implementation_addresses, 0) %>
<% name = Enum.at(implementation_names, 0) %> <% name = Enum.at(implementation_names, 0) %>
<% implementation_address = implementation_address_ || "0x0000000000000000000000000000000000000000" %> <% implementation_address = implementation_address_ || "0x0000000000000000000000000000000000000000" %>

@ -1,7 +1,7 @@
<section class="container" data-page="coin-balance-history"> <section class="container" data-page="coin-balance-history">
<script defer src="<%= static_path(@conn, "/js/balance-chart-loader.js") %>"></script> <script defer src="<%= static_path(@conn, "/js/balance-chart-loader.js") %>"></script>
<% 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 %> <%= 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 %>

@ -1,12 +1,12 @@
<% contract_creation_code = contract_creation_code(@address) %> <% contract_creation_code = contract_creation_code(@address) %>
<% minimal_proxy_template = EIP1167.get_implementation_smart_contract(@address.hash) %> <% 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) %> <% smart_contract_verified = BlockScoutWeb.AddressView.smart_contract_verified?(@address) %>
<% fully_verified = SmartContract.verified_with_full_match?(@address.hash)%> <% 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?() %> <% visualize_sol2uml_enabled = Explorer.Visualize.Sol2uml.enabled?() %>
<section class="container"> <section class="container">
<% 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 %> <%= 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 @@
<div class="card-body"> <div class="card-body">
<%= unless smart_contract_verified do %> <%= unless smart_contract_verified do %>
<%= if minimal_proxy_template 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 %> <% else %>
<%= if implementation_or_bytecode_twin_contract do %> <%= if bytecode_twin_contract do %>
<% path = address_verify_contract_path(@conn, :new, @address.hash) %> <% path = address_verify_contract_path(@conn, :new, @address.hash) %>
<div class="mb-4"> <div class="mb-4">
<div style="display: inline-block;"> <div style="display: inline-block;">
<%= render BlockScoutWeb.CommonComponentsView, "_info.html" %> <%= render BlockScoutWeb.CommonComponentsView, "_info.html" %>
<span> <%= gettext("Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB") %> <%= link( <span> <%= 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, bytecode_twin_contract.address_hash,
to: address_contract_path(@conn, :index, implementation_or_bytecode_twin_contract.address_hash)) %>.<br/> <%= gettext("All metadata displayed below is from that contract. In order to verify current contract, click") %> <i><%= gettext("Verify & Publish") %></i> <%= gettext("button") %></span> to: address_contract_path(@conn, :index, bytecode_twin_contract.address_hash)) %>.<br/> <%= gettext("All metadata displayed below is from that contract. In order to verify current contract, click") %> <i><%= gettext("Verify & Publish") %></i> <%= gettext("button") %></span>
</div> </div>
<%= link(gettext("Verify & Publish"), to: path, class: "button button-primary button-sm float-right ml-3", "data-test": "verify_and_publish") %> <%= link(gettext("Verify & Publish"), to: path, class: "button button-primary button-sm float-right ml-3", "data-test": "verify_and_publish") %>
</div> </div>
@ -40,8 +40,8 @@
</div> </div>
<% end %> <% end %>
<% end %> <% end %>
<%= if smart_contract_verified || (!smart_contract_verified && implementation_or_bytecode_twin_contract) do %> <%= if smart_contract_verified || (!smart_contract_verified && bytecode_twin_contract) do %>
<% target_contract = if smart_contract_verified, do: @address.smart_contract, else: implementation_or_bytecode_twin_contract %> <% 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 %> <%= if @address.smart_contract && @address.smart_contract.verified_via_sourcify && @address.smart_contract.partially_verified && smart_contract_verified do %>
<div class="mb-4"> <div class="mb-4">
<i style="color: #f7b32b;" class="fa fa-info-circle"></i><span> <%= gettext("This contract has been partially verified via Sourcify.") %> <i style="color: #f7b32b;" class="fa fa-info-circle"></i><span> <%= gettext("This contract has been partially verified via Sourcify.") %>
@ -240,8 +240,8 @@
<% end %> <% end %>
</section> </section>
<%= if smart_contract_verified || (!smart_contract_verified && implementation_or_bytecode_twin_contract) do %> <%= if smart_contract_verified || (!smart_contract_verified && bytecode_twin_contract) do %>
<% target_contract = if smart_contract_verified, do: @address.smart_contract, else: implementation_or_bytecode_twin_contract %> <% target_contract = if smart_contract_verified, do: @address.smart_contract, else: bytecode_twin_contract %>
<%= if target_contract.external_libraries && target_contract.external_libraries != [] do %> <%= if target_contract.external_libraries && target_contract.external_libraries != [] do %>
<section> <section>
<div class="d-flex justify-content-between align-items-baseline"> <div class="d-flex justify-content-between align-items-baseline">

@ -1,5 +1,5 @@
<section class="container"> <section class="container">
<% 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 %> <%= 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 %>
<div class="card"> <div class="card">

@ -1,5 +1,5 @@
<section class="container"> <section class="container">
<% 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 %> <%= 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 %>

@ -1,5 +1,5 @@
<section class="container"> <section class="container">
<% 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 %> <%= 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 %>
<section data-page="address-logs"> <section data-page="address-logs">

@ -1,6 +1,6 @@
<section class="container"> <section class="container">
<% 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 %> <%= 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 %>

@ -1,6 +1,6 @@
<section class="container"> <section class="container">
<% 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 %> <%= 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 %>

@ -1,5 +1,5 @@
<section class="container"> <section class="container">
<% 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 %> <%= 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 %>

@ -1,5 +1,5 @@
<section class="container"> <section class="container">
<% 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 %> <%= 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 %>

@ -1,6 +1,6 @@
<section class="container"> <section class="container">
<% 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 %> <%= 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 %>

@ -1,5 +1,5 @@
<section class="container"> <section class="container">
<% 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 %> <%= 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 %>

@ -1,6 +1,6 @@
<section class="container"> <section class="container">
<% 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 %> <%= 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 %>

@ -1,6 +1,6 @@
<section class="container"> <section class="container">
<% 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 %> <%= 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 %>

@ -1,6 +1,6 @@
<section class="container"> <section class="container">
<% 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 %> <%= 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 %>

@ -12,7 +12,7 @@
<%= render BlockScoutWeb.AddressView, <%= render BlockScoutWeb.AddressView,
"_link.html", "_link.html",
address: @withdrawal.address, address: @withdrawal.address,
contract: BlockScoutWeb.AddressView.contract?(@withdrawal.address), contract: Address.smart_contract?(@withdrawal.address),
use_custom_tooltip: false use_custom_tooltip: false
%> %>
</td> </td>

@ -2,7 +2,7 @@
<div class="row"> <div class="row">
<div class="col-md-7 col-lg-8 d-flex flex-column"> <div class="col-md-7 col-lg-8 d-flex flex-column">
<span> <span>
<%= 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 %>
</span> </span>
<span> <span>

@ -15,7 +15,7 @@
BlockScoutWeb.AddressView, BlockScoutWeb.AddressView,
"_responsive_hash.html", "_responsive_hash.html",
address: @token_transfer.from_address, 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 use_custom_tooltip: false
) %> ) %>
<% end %> <% end %>
@ -25,7 +25,7 @@
BlockScoutWeb.AddressView, BlockScoutWeb.AddressView,
"_responsive_hash.html", "_responsive_hash.html",
address: @token_transfer.to_address, 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 use_custom_tooltip: false
) %> ) %>
<% end %> <% end %>

@ -84,7 +84,7 @@
<% address_string = Map.get(@action.data, "address") %> <% address_string = Map.get(@action.data, "address") %>
<% {address_status, address} = transaction_action_string_to_address(address_string) %> <% {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_address = Map.get(@action.data, "to") %>
<% to_content = raw(render_to_string BlockScoutWeb.TransactionView, "_actions_to.html", address: to_address) %> <% 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 %> <% to = link to: address_path(BlockScoutWeb.Endpoint, :show, to_address), "data-test": "address_hash_link", do: to_content %>

@ -8,10 +8,10 @@
<div class="col-md-7 col-lg-8 d-flex flex-column pr-2 pr-sm-2 pr-md-0"> <div class="col-md-7 col-lg-8 d-flex flex-column pr-2 pr-sm-2 pr-md-0">
<%= render BlockScoutWeb.TransactionView, "_link.html", transaction_hash: @transaction.hash %> <%= render BlockScoutWeb.TransactionView, "_link.html", transaction_hash: @transaction.hash %>
<span class="text-nowrap"> <span class="text-nowrap">
<%= 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 %>
&rarr; &rarr;
<%= if @transaction.to_address_hash do %> <%= 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 %> <% else %>
<%= gettext("Contract Address Pending") %> <%= gettext("Contract Address Pending") %>
<% end %> <% end %>

@ -7,7 +7,7 @@
<b style="margin-right: 15px;">From</b> <b style="margin-right: 15px;">From</b>
</td> </td>
<td class="address-mobile" style="word-break: break-all; width: 1%; white-space: nowrap;"> <td class="address-mobile" style="word-break: break-all; width: 1%; white-space: nowrap;">
<%= 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.AddressView, "_labels.html", tags: from_tags %>
</td> </td>
<td style="word-break: break-all; white-space: nowrap;"> <td style="word-break: break-all; white-space: nowrap;">
@ -24,7 +24,7 @@ style: "position: relative;" %>
<b>To</b> <b>To</b>
</td> </td>
<td class="address-mobile" style="word-break: break-all; width: 1%; white-space: nowrap;"> <td class="address-mobile" style="word-break: break-all; width: 1%; white-space: nowrap;">
<%= 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.AddressView, "_labels.html", tags: to_tags %>
</td> </td>
<td style="word-break: break-all; white-space: nowrap;"> <td style="word-break: break-all; white-space: nowrap;">

@ -245,7 +245,7 @@
text: gettext("Address (external or contract) sending the transaction.") %> text: gettext("Address (external or contract) sending the transaction.") %>
<%= gettext "From" %></dt> <%= gettext "From" %></dt>
<dd class="col-sm-9 col-lg-10"> <dd class="col-sm-9 col-lg-10">
<%= 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.AddressView, "_labels.html", tags: @from_tags %>
<%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html", <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html",
additional_classes: ["btn-copy-icon-small", "btn-copy-icon-custom", "btn-copy-icon-no-borders"], additional_classes: ["btn-copy-icon-small", "btn-copy-icon-custom", "btn-copy-icon-no-borders"],
@ -261,7 +261,7 @@
<dt class="col-sm-3 col-lg-2 text-muted"> <dt class="col-sm-3 col-lg-2 text-muted">
<%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html",
text: gettext("Address (external or contract) receiving the transaction.") %> 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)" %> <%= gettext "Interacted With (To)" %>
<% else %> <% else %>
<%= gettext "To" %> <%= gettext "To" %>
@ -271,7 +271,7 @@
<%= cond do %> <%= cond do %>
<% created_address_hash -> %> <% created_address_hash -> %>
[<%= gettext("Contract") %>&nbsp; [<%= gettext("Contract") %>&nbsp;
<%= 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.AddressView, "_labels.html", tags: @to_tags %>
&nbsp;<%= gettext("created") %>] &nbsp;<%= gettext("created") %>]
<%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html", <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html",
@ -280,7 +280,7 @@
aria_label: gettext("Copy To Address"), aria_label: gettext("Copy To Address"),
title: gettext("Copy To Address") %> title: gettext("Copy To Address") %>
<% recipient_address_hash -> %> <% 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.AddressView, "_labels.html", tags: @to_tags %>
<%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html", <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html",
additional_classes: ["btn-copy-icon-small", "btn-copy-icon-custom", "btn-copy-icon-no-borders"], additional_classes: ["btn-copy-icon-small", "btn-copy-icon-custom", "btn-copy-icon-no-borders"],

@ -9,7 +9,7 @@
</dt> </dt>
</td> </td>
<td class="stakes-td"> <td class="stakes-td">
<%= 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 %>
</td> </td>
<td class="stakes-td"></td> <td class="stakes-td"></td>
<td class="stakes-td"></td> <td class="stakes-td"></td>
@ -28,7 +28,7 @@
<% else %> <% else %>
<td class="stakes-td"></td> <td class="stakes-td"></td>
<td class="stakes-td"> <td class="stakes-td">
<%= 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 %>
</td> </td>
<% end %> <% end %>
<%= if not_negative?(@balance_before) and not_negative?(@balance_after) do %> <%= if not_negative?(@balance_before) and not_negative?(@balance_after) do %>

@ -7,9 +7,9 @@
<div class="col-12 col-md-8 col-lg-10 d-flex flex-column text-nowrap"> <div class="col-12 col-md-8 col-lg-10 d-flex flex-column text-nowrap">
<%= render BlockScoutWeb.TransactionView, "_link.html", transaction_hash: @token_transfer.transaction_hash %> <%= render BlockScoutWeb.TransactionView, "_link.html", transaction_hash: @token_transfer.transaction_hash %>
<span class="text-nowrap"> <span class="text-nowrap">
<%= 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 %>
&rarr; &rarr;
<%= 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 %>
</span> </span>
<span class="tile-title"> <span class="tile-title">

@ -19,7 +19,7 @@
<%= render BlockScoutWeb.AddressView, <%= render BlockScoutWeb.AddressView,
"_link.html", "_link.html",
address: @withdrawal.address, address: @withdrawal.address,
contract: BlockScoutWeb.AddressView.contract?(@withdrawal.address), contract: Address.smart_contract?(@withdrawal.address),
use_custom_tooltip: false use_custom_tooltip: false
%> %>
</td> </td>

@ -3,6 +3,7 @@ defmodule BlockScoutWeb.AddressCoinBalanceView do
alias BlockScoutWeb.AccessHelper alias BlockScoutWeb.AccessHelper
alias Explorer.Chain.Wei alias Explorer.Chain.Wei
alias Explorer.SmartContract.Helper, as: SmartContractHelper
def format(%Wei{} = value) do def format(%Wei{} = value) do
format_wei_value(value, :ether) format_wei_value(value, :ether)

@ -10,6 +10,7 @@ defmodule BlockScoutWeb.AddressContractView do
alias Explorer.Chain.{Address, Data, InternalTransaction, Transaction} alias Explorer.Chain.{Address, Data, InternalTransaction, Transaction}
alias Explorer.Chain.SmartContract alias Explorer.Chain.SmartContract
alias Explorer.Chain.SmartContract.Proxy.EIP1167 alias Explorer.Chain.SmartContract.Proxy.EIP1167
alias Explorer.SmartContract.Helper, as: SmartContractHelper
def render("scripts.html", %{conn: conn}) do def render("scripts.html", %{conn: conn}) do
render_scripts(conn, "address_contract/code_highlighting.js") render_scripts(conn, "address_contract/code_highlighting.js")

@ -1,5 +1,6 @@
defmodule BlockScoutWeb.AddressDecompiledContractView do defmodule BlockScoutWeb.AddressDecompiledContractView do
use BlockScoutWeb, :view use BlockScoutWeb, :view
alias Explorer.SmartContract.Helper, as: SmartContractHelper
@colors %{ @colors %{
"\e[95m" => "", "\e[95m" => "",

@ -3,6 +3,7 @@ defmodule BlockScoutWeb.AddressInternalTransactionView do
alias BlockScoutWeb.AccessHelper alias BlockScoutWeb.AccessHelper
alias Explorer.Chain.Address alias Explorer.Chain.Address
alias Explorer.SmartContract.Helper, as: SmartContractHelper
def format_current_filter(filter) do def format_current_filter(filter) do
case filter do case filter do

@ -2,6 +2,7 @@ defmodule BlockScoutWeb.AddressLogsView do
use BlockScoutWeb, :view use BlockScoutWeb, :view
alias Explorer.Chain.Address alias Explorer.Chain.Address
alias Explorer.SmartContract.Helper, as: SmartContractHelper
import BlockScoutWeb.AddressView, only: [decode: 2] import BlockScoutWeb.AddressView, only: [decode: 2]
end end

@ -1,3 +1,5 @@
defmodule BlockScoutWeb.AddressReadContractView do defmodule BlockScoutWeb.AddressReadContractView do
use BlockScoutWeb, :view use BlockScoutWeb, :view
alias Explorer.SmartContract.Helper, as: SmartContractHelper
end end

@ -1,3 +1,5 @@
defmodule BlockScoutWeb.AddressReadProxyView do defmodule BlockScoutWeb.AddressReadProxyView do
use BlockScoutWeb, :view use BlockScoutWeb, :view
alias Explorer.SmartContract.Helper, as: SmartContractHelper
end end

@ -3,6 +3,7 @@ defmodule BlockScoutWeb.AddressTokenTransferView do
alias BlockScoutWeb.AccessHelper alias BlockScoutWeb.AccessHelper
alias Explorer.Chain.Address alias Explorer.Chain.Address
alias Explorer.SmartContract.Helper, as: SmartContractHelper
def format_current_filter(filter) do def format_current_filter(filter) do
case filter do case filter do

@ -4,4 +4,5 @@ defmodule BlockScoutWeb.AddressTokenView do
alias BlockScoutWeb.{AddressView, ChainView} alias BlockScoutWeb.{AddressView, ChainView}
alias Explorer.Chain alias Explorer.Chain
alias Explorer.Chain.{Address, Wei} alias Explorer.Chain.{Address, Wei}
alias Explorer.SmartContract.Helper, as: SmartContractHelper
end end

@ -3,6 +3,7 @@ defmodule BlockScoutWeb.AddressTransactionView do
alias BlockScoutWeb.AccessHelper alias BlockScoutWeb.AccessHelper
alias Explorer.Chain.Address alias Explorer.Chain.Address
alias Explorer.SmartContract.Helper, as: SmartContractHelper
def format_current_filter(filter) do def format_current_filter(filter) do
case filter do case filter do

@ -1,5 +1,5 @@
defmodule BlockScoutWeb.AddressValidationView do defmodule BlockScoutWeb.AddressValidationView do
use BlockScoutWeb, :view use BlockScoutWeb, :view
# import BlockScoutWeb.AddressView, only: [contract?: 1, smart_contract_verified?: 1] alias Explorer.SmartContract.Helper, as: SmartContractHelper
end end

@ -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(struct_to_render_from, direction, current_address, truncate \\ false)
def address_partial_selector(%Address{} = address, _, current_address, truncate) do 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 end
def address_partial_selector( def address_partial_selector(
@ -58,19 +58,19 @@ defmodule BlockScoutWeb.AddressView do
end end
def address_partial_selector(%InternalTransaction{to_address: address}, :to, current_address, truncate) do 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 end
def address_partial_selector(%InternalTransaction{from_address: address}, :from, current_address, truncate) do 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 end
def address_partial_selector(%TokenTransfer{to_address: address}, :to, current_address, truncate) do 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 end
def address_partial_selector(%TokenTransfer{from_address: address}, :from, current_address, truncate) do 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 end
def address_partial_selector( def address_partial_selector(
@ -92,11 +92,11 @@ defmodule BlockScoutWeb.AddressView do
end end
def address_partial_selector(%Transaction{to_address: address}, :to, current_address, truncate) do 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 end
def address_partial_selector(%Transaction{from_address: address}, :from, current_address, truncate) do 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 end
def address_partial_selector(%Reward{address: address}, _, current_address, truncate) do def address_partial_selector(%Reward{address: address}, _, current_address, truncate) do
@ -104,7 +104,7 @@ defmodule BlockScoutWeb.AddressView do
end end
def address_title(%Address{} = address) do def address_title(%Address{} = address) do
if contract?(address) do if Address.smart_contract?(address) do
gettext("Contract Address") gettext("Contract Address")
else else
gettext("Address") gettext("Address")
@ -167,12 +167,6 @@ defmodule BlockScoutWeb.AddressView do
to_string(fetched_coin_balance_block_number) to_string(fetched_coin_balance_block_number)
end 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?(val) when val > 0, do: true
def validator?(_), do: false 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 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 def smart_contract_with_write_functions?(%Address{smart_contract: %SmartContract{}} = address) do
!contract_interaction_disabled?() && !contract_interaction_disabled?() &&
Enum.any?( Enum.any?(
@ -447,13 +433,14 @@ defmodule BlockScoutWeb.AddressView do
def address_page_title(address) do def address_page_title(address) do
cond do cond do
smart_contract_verified?(address) -> "#{address.smart_contract.name} (#{to_string(address)})" 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)}" true -> "#{to_string(address)}"
end end
end end
def smart_contract_is_gnosis_safe_proxy?(%Address{smart_contract: %SmartContract{}} = address) do 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 end
def smart_contract_is_gnosis_safe_proxy?(_address), do: false def smart_contract_is_gnosis_safe_proxy?(_address), do: false

@ -1,3 +1,5 @@
defmodule BlockScoutWeb.AddressWithdrawalView do defmodule BlockScoutWeb.AddressWithdrawalView do
use BlockScoutWeb, :view use BlockScoutWeb, :view
alias Explorer.SmartContract.Helper, as: SmartContractHelper
end end

@ -1,3 +1,5 @@
defmodule BlockScoutWeb.AddressWriteContractView do defmodule BlockScoutWeb.AddressWriteContractView do
use BlockScoutWeb, :view use BlockScoutWeb, :view
alias Explorer.SmartContract.Helper, as: SmartContractHelper
end end

@ -1,3 +1,5 @@
defmodule BlockScoutWeb.AddressWriteProxyView do defmodule BlockScoutWeb.AddressWriteProxyView do
use BlockScoutWeb, :view use BlockScoutWeb, :view
alias Explorer.SmartContract.Helper, as: SmartContractHelper
end end

@ -5,11 +5,9 @@ defmodule BlockScoutWeb.API.V2.AddressView do
alias BlockScoutWeb.AddressView alias BlockScoutWeb.AddressView
alias BlockScoutWeb.API.V2.{ApiView, Helper, TokenView} alias BlockScoutWeb.API.V2.{ApiView, Helper, TokenView}
alias BlockScoutWeb.API.V2.Helper
alias Explorer.{Chain, Market} alias Explorer.{Chain, Market}
alias Explorer.Chain.Address alias Explorer.Chain.Address
alias Explorer.Chain.Address.Counters alias Explorer.Chain.Address.Counters
alias Explorer.Chain.SmartContract.Proxy.Models.Implementation
alias Explorer.Chain.Token.Instance alias Explorer.Chain.Token.Instance
@api_true [api?: true] @api_true [api?: true]
@ -18,8 +16,8 @@ defmodule BlockScoutWeb.API.V2.AddressView do
ApiView.render("message.json", assigns) ApiView.render("message.json", assigns)
end end
def render("address.json", %{address: address, conn: conn}) do def render("address.json", %{address: address, implementations: implementations, proxy_type: proxy_type, conn: conn}) do
prepare_address(address, conn) prepare_address(address, conn, implementations, proxy_type)
end end
def render("token_balances.json", %{token_balances: token_balances}) do 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)) |> Map.put(:coin_balance, if(address.fetched_coin_balance, do: address.fetched_coin_balance.value))
end 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) 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 balance = address.fetched_coin_balance && address.fetched_coin_balance.value
exchange_rate = Market.get_coin_exchange_rate().usd_value exchange_rate = Market.get_coin_exchange_rate().usd_value
@ -134,6 +116,7 @@ defmodule BlockScoutWeb.API.V2.AddressView do
extended_info extended_info
else else
Map.merge(extended_info, %{ Map.merge(extended_info, %{
"proxy_type" => proxy_type,
"implementations" => implementations "implementations" => implementations
}) })
end end

@ -4,8 +4,8 @@ defmodule BlockScoutWeb.API.V2.Helper do
""" """
alias Ecto.Association.NotLoaded alias Ecto.Association.NotLoaded
alias Explorer.Chain alias Explorer.Chain.Address
alias Explorer.Chain.{Address, Hash} alias Explorer.Chain.SmartContract.Proxy
alias Explorer.Chain.Transaction.History.TransactionStats alias Explorer.Chain.Transaction.History.TransactionStats
import BlockScoutWeb.Account.AuthController, only: [current_user: 1] 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 def address_with_info(%Address{} = address, _address_hash) do
smart_contract? = Address.smart_contract?(address) 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 case address.proxy_implementations do
%NotLoaded{} -> %NotLoaded{} ->
{nil, [], []} {nil, [], [], nil}
nil -> nil ->
{nil, [], []} {nil, [], [], nil}
proxy_implementations -> proxy_implementations ->
address_hashes = proxy_implementations.address_hashes address_hashes = proxy_implementations.address_hashes
names = proxy_implementations.names names = proxy_implementations.names
proxy_type = proxy_implementations.proxy_type
{proxy_implementations, address_hashes, names} {proxy_implementations, address_hashes, names, proxy_type}
end end
%{ %{
"hash" => Address.checksum(address), "hash" => Address.checksum(address),
"is_contract" => smart_contract?, "is_contract" => smart_contract?,
"name" => address_name(address), "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), "is_verified" => verified?(address) || verified_minimal_proxy?(proxy_implementations),
"ens_domain_name" => address.ens_domain_name, "ens_domain_name" => address.ens_domain_name,
"metadata" => address.metadata "metadata" => address.metadata
@ -110,6 +112,7 @@ defmodule BlockScoutWeb.API.V2.Helper do
"hash" => Address.checksum(address_hash), "hash" => Address.checksum(address_hash),
"is_contract" => false, "is_contract" => false,
"name" => nil, "name" => nil,
"proxy_type" => nil,
"implementations" => [], "implementations" => [],
"is_verified" => nil, "is_verified" => nil,
"ens_domain_name" => nil, "ens_domain_name" => nil,
@ -117,41 +120,6 @@ defmodule BlockScoutWeb.API.V2.Helper do
} }
end 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 defp minimal_proxy_pattern?(proxy_implementations) do
proxy_implementations.proxy_type == :eip1167 proxy_implementations.proxy_type == :eip1167
end end

@ -11,18 +11,28 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do
alias Ecto.Changeset alias Ecto.Changeset
alias Explorer.Chain alias Explorer.Chain
alias Explorer.Chain.{Address, SmartContract, SmartContractAdditionalSource} alias Explorer.Chain.{Address, SmartContract, SmartContractAdditionalSource}
alias Explorer.SmartContract.Helper, as: SmartContractHelper
alias Explorer.Visualize.Sol2uml alias Explorer.Visualize.Sol2uml
require Logger require Logger
@api_true [api?: true] @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 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} %{"items" => Enum.map(smart_contracts, &prepare_smart_contract_for_list/1), "next_page_params" => next_page_params}
end end
def render("smart_contract.json", %{address: address, conn: conn}) do def render("smart_contract.json", %{
prepare_smart_contract(address, conn) address: address,
implementations: implementations,
proxy_type: proxy_type,
conn: conn
}) do
prepare_smart_contract(address, implementations, proxy_type, conn)
end end
def render("read_functions.json", %{functions: functions}) do def render("read_functions.json", %{functions: functions}) do
@ -145,15 +155,20 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do
defp prepare_output(output), do: output defp prepare_output(output), do: output
# credo:disable-for-next-line # 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) bytecode_twin = SmartContract.get_address_verified_bytecode_twin_contract(address.hash, @api_true)
minimal_proxy_address_hash = address.implementation 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) smart_contract_verified = AddressView.smart_contract_verified?(address)
fully_verified = SmartContract.verified_with_full_match?(address.hash, @api_true) fully_verified = SmartContract.verified_with_full_match?(address.hash, @api_true)
write_methods? = AddressView.smart_contract_with_write_functions?(address) 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) 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) 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( get_additional_sources(
smart_contract, smart_contract,
smart_contract_verified, smart_contract_verified,
implementation_or_bytecode_twin_contract bytecode_twin_contract
) )
visualize_sol2uml_enabled = Sol2uml.enabled?() visualize_sol2uml_enabled = Sol2uml.enabled?()
target_contract = 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" => "verified_twin_address_hash" =>
implementation_or_bytecode_twin_contract && bytecode_twin_contract &&
Address.checksum(implementation_or_bytecode_twin_contract.address_hash), Address.checksum(bytecode_twin_contract.address_hash),
"is_verified" => smart_contract_verified, "is_verified" => smart_contract_verified,
"is_changed_bytecode" => smart_contract_verified && address.smart_contract.is_changed_bytecode, "is_changed_bytecode" => smart_contract_verified && smart_contract.is_changed_bytecode,
"is_partially_verified" => address.smart_contract.partially_verified && smart_contract_verified, "is_partially_verified" => smart_contract.partially_verified && smart_contract_verified,
"is_fully_verified" => fully_verified, "is_fully_verified" => fully_verified,
"is_verified_via_sourcify" => address.smart_contract.verified_via_sourcify && smart_contract_verified, "is_verified_via_sourcify" => 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_eth_bytecode_db" => smart_contract.verified_via_eth_bytecode_db,
"is_verified_via_verifier_alliance" => address.smart_contract.verified_via_verifier_alliance, "is_verified_via_verifier_alliance" => smart_contract.verified_via_verifier_alliance,
"is_vyper_contract" => target_contract.is_vyper_contract, "is_vyper_contract" => target_contract && target_contract.is_vyper_contract,
"has_custom_methods_read" => read_custom_abi?, "has_custom_methods_read" => read_custom_abi?,
"has_custom_methods_write" => write_custom_abi?, "has_custom_methods_write" => write_custom_abi?,
"has_methods_read" => AddressView.smart_contract_with_read_only_functions?(address), "has_methods_read" => AddressView.smart_contract_with_read_only_functions?(address),
"has_methods_write" => write_methods?, "has_methods_write" => write_methods?,
"has_methods_read_proxy" => is_proxy, "has_methods_read_proxy" => is_proxy,
"has_methods_write_proxy" => is_proxy && write_methods?, "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" =>
minimal_proxy_address_hash && Address.checksum(minimal_proxy_address_hash.address_hash), minimal_proxy_address_hash && Address.checksum(minimal_proxy_address_hash.address_hash),
"proxy_type" => proxy_type,
"implementations" => implementations,
"sourcify_repo_url" => "sourcify_repo_url" =>
if(address.smart_contract.verified_via_sourcify && smart_contract_verified, if(smart_contract.verified_via_sourcify && smart_contract_verified,
do: AddressContractView.sourcify_repo_url(address.hash, address.smart_contract.partially_verified) do: AddressContractView.sourcify_repo_url(address.hash, smart_contract.partially_verified)
), ),
"can_be_visualized_via_sol2uml" => "can_be_visualized_via_sol2uml" =>
visualize_sol2uml_enabled && !target_contract.is_vyper_contract && !is_nil(target_contract.abi), visualize_sol2uml_enabled && target_contract && !target_contract.is_vyper_contract &&
"name" => target_contract.name, !is_nil(target_contract.abi),
"compiler_version" => target_contract.compiler_version, "name" => target_contract && target_contract.name,
"optimization_enabled" => target_contract.optimization, "compiler_version" => target_contract && target_contract.compiler_version,
"optimization_runs" => target_contract.optimization_runs, "optimization_enabled" => target_contract && target_contract.optimization,
"evm_version" => target_contract.evm_version, "optimization_runs" => target_contract && target_contract.optimization_runs,
"verified_at" => target_contract.inserted_at, "evm_version" => target_contract && target_contract.evm_version,
"abi" => target_contract.abi, "verified_at" => target_contract && target_contract.inserted_at,
"source_code" => target_contract.contract_source_code, "abi" => target_contract && target_contract.abi,
"file_path" => target_contract.file_path, "source_code" => target_contract && target_contract.contract_source_code,
"file_path" => target_contract && target_contract.file_path,
"additional_sources" => "additional_sources" =>
(is_list(additional_sources) && Enum.map(additional_sources, &prepare_additional_source/1)) || [], (is_list(additional_sources) && Enum.map(additional_sources, &prepare_additional_source/1)) || [],
"compiler_settings" => target_contract.compiler_settings, "compiler_settings" => target_contract && target_contract.compiler_settings,
"external_libraries" => prepare_external_libraries(target_contract.external_libraries), "external_libraries" => (target_contract && prepare_external_libraries(target_contract.external_libraries)) || [],
"constructor_args" => if(smart_contract_verified, do: target_contract.constructor_arguments), "constructor_args" => if(smart_contract_verified, do: target_contract && target_contract.constructor_arguments),
"decoded_constructor_args" => "decoded_constructor_args" =>
if(smart_contract_verified, 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), "language" => smart_contract_language(smart_contract),
"license_type" => smart_contract.license_type, "license_type" => smart_contract.license_type,
@ -222,26 +242,28 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do
|> Map.merge(bytecode_info(address)) |> Map.merge(bytecode_info(address))
end 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) 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) write_custom_abi? = AddressView.has_address_custom_abi_with_write_functions?(conn, address.hash)
%{ %{
"has_custom_methods_read" => read_custom_abi?, "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)) |> Map.merge(bytecode_info(address))
end end
@doc """ @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) :: @spec get_additional_sources(SmartContract.t(), boolean, SmartContract.t() | nil) ::
[SmartContractAdditionalSource.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 cond do
!is_nil(implementation_or_bytecode_twin_contract) -> !is_nil(bytecode_twin_contract) ->
implementation_or_bytecode_twin_contract.smart_contract_additional_sources bytecode_twin_contract.smart_contract_additional_sources
smart_contract_verified -> smart_contract_verified ->
smart_contract.smart_contract_additional_sources smart_contract.smart_contract_additional_sources

@ -1,3 +1,5 @@
defmodule BlockScoutWeb.BlockWithdrawalView do defmodule BlockScoutWeb.BlockWithdrawalView do
use BlockScoutWeb, :view use BlockScoutWeb, :view
alias Explorer.Chain.Address
end end

@ -2,7 +2,7 @@ defmodule BlockScoutWeb.Tokens.HolderView do
use BlockScoutWeb, :view use BlockScoutWeb, :view
alias BlockScoutWeb.Tokens.OverviewView alias BlockScoutWeb.Tokens.OverviewView
alias Explorer.Chain.Token alias Explorer.Chain.{Address, Token}
@doc """ @doc """
Checks if the total supply percentage must be shown. Checks if the total supply percentage must be shown.

@ -2,7 +2,7 @@ defmodule BlockScoutWeb.TransactionStateView do
use BlockScoutWeb, :view use BlockScoutWeb, :view
alias Explorer.Chain 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] import Explorer.Chain.Transaction.StateChange, only: [from_loss: 1, has_diff?: 1, to_profit: 1]

@ -2,4 +2,5 @@ defmodule BlockScoutWeb.TransactionTokenTransferView do
use BlockScoutWeb, :view use BlockScoutWeb, :view
alias Explorer.Chain alias Explorer.Chain
alias Explorer.Chain.Address
end end

@ -449,7 +449,7 @@ defmodule BlockScoutWeb.TransactionView do
end end
def involves_contract?(%Transaction{from_address: from_address, to_address: to_address}) do 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 end
def involves_token_transfers?(%Transaction{token_transfers: []}), do: false def involves_token_transfers?(%Transaction{token_transfers: []}), do: false

@ -1,3 +1,5 @@
defmodule BlockScoutWeb.WithdrawalView do defmodule BlockScoutWeb.WithdrawalView do
use BlockScoutWeb, :view use BlockScoutWeb, :view
alias Explorer.Chain.Address
end end

@ -151,6 +151,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserControllerTest do
"name" => name, "name" => name,
"address" => %{ "address" => %{
"hash" => Address.checksum(addr), "hash" => Address.checksum(addr),
"proxy_type" => nil,
"implementations" => [], "implementations" => [],
"is_contract" => false, "is_contract" => false,
"is_verified" => false, "is_verified" => false,
@ -207,6 +208,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserControllerTest do
"name" => name, "name" => name,
"address" => %{ "address" => %{
"hash" => Address.checksum(addr), "hash" => Address.checksum(addr),
"proxy_type" => nil,
"implementations" => [], "implementations" => [],
"is_contract" => false, "is_contract" => false,
"is_verified" => false, "is_verified" => false,

@ -78,8 +78,6 @@ defmodule BlockScoutWeb.AddressReadContractControllerTest do
block_index: 0 block_index: 0
) )
TestHelper.get_eip1967_implementation_zero_addresses()
conn = conn =
get(conn, address_read_contract_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash))) get(conn, address_read_contract_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash)))

@ -77,8 +77,6 @@ defmodule BlockScoutWeb.AddressReadProxyControllerTest do
block_index: 0 block_index: 0
) )
TestHelper.get_eip1967_implementation_zero_addresses()
conn = get(conn, address_read_proxy_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash))) conn = get(conn, address_read_proxy_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash)))
assert html_response(conn, 404) assert html_response(conn, 404)

@ -80,8 +80,6 @@ defmodule BlockScoutWeb.AddressWriteContractControllerTest do
block_index: 0 block_index: 0
) )
TestHelper.get_eip1967_implementation_zero_addresses()
conn = conn =
get(conn, address_write_contract_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash))) get(conn, address_write_contract_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash)))

@ -78,8 +78,6 @@ defmodule BlockScoutWeb.AddressWriteProxyControllerTest do
block_index: 0 block_index: 0
) )
TestHelper.get_eip1967_implementation_zero_addresses()
conn = conn =
get(conn, address_write_proxy_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash))) get(conn, address_write_proxy_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash)))

@ -85,6 +85,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do
"creation_tx_hash" => nil, "creation_tx_hash" => nil,
"token" => nil, "token" => nil,
"coin_balance" => nil, "coin_balance" => nil,
"proxy_type" => nil,
"implementations" => [], "implementations" => [],
"block_number_balance_updated_at" => nil, "block_number_balance_updated_at" => nil,
"has_decompiled_code" => false, "has_decompiled_code" => false,
@ -220,6 +221,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do
"watchlist_names" => [], "watchlist_names" => [],
"creator_address_hash" => ^from, "creator_address_hash" => ^from,
"creation_tx_hash" => ^tx_hash, "creation_tx_hash" => ^tx_hash,
"proxy_type" => "eip1167",
"implementations" => [ "implementations" => [
%{"address" => ^checksummed_implementation_contract_address_hash, "name" => ^name} %{"address" => ^checksummed_implementation_contract_address_hash, "name" => ^name}
] ]
@ -265,6 +267,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do
"watchlist_names" => [], "watchlist_names" => [],
"creator_address_hash" => ^from, "creator_address_hash" => ^from,
"creation_tx_hash" => ^tx_hash, "creation_tx_hash" => ^tx_hash,
"proxy_type" => "eip1967",
"implementations" => [%{"address" => ^implementation_address_hash_string, "name" => nil}] "implementations" => [%{"address" => ^implementation_address_hash_string, "name" => nil}]
} = json_response(request, 200) } = json_response(request, 200)
end end

@ -32,13 +32,15 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
test "get unverified smart-contract info", %{conn: conn} do test "get unverified smart-contract info", %{conn: conn} do
address = insert(:contract_address) 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)}") request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}")
response = json_response(request, 200) response = json_response(request, 200)
assert response == assert response ==
%{ %{
"proxy_type" => nil,
"implementations" => [],
"has_custom_methods_read" => false, "has_custom_methods_read" => false,
"has_custom_methods_write" => false, "has_custom_methods_write" => false,
"is_self_destructed" => false, "is_self_destructed" => false,
@ -53,11 +55,15 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
) )
|> with_block() |> with_block()
TestHelper.get_eip1967_implementation_error_response()
request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}")
response = json_response(request, 200) response = json_response(request, 200)
assert response == assert response ==
%{ %{
"proxy_type" => nil,
"implementations" => [],
"has_custom_methods_read" => false, "has_custom_methods_read" => false,
"has_custom_methods_write" => false, "has_custom_methods_write" => false,
"is_self_destructed" => false, "is_self_destructed" => false,
@ -112,6 +118,10 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
) )
|> with_block() |> 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 = %{ correct_response = %{
"verified_twin_address_hash" => nil, "verified_twin_address_hash" => nil,
"is_verified" => true, "is_verified" => true,
@ -151,6 +161,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
"creation_bytecode" => "creation_bytecode" =>
"0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029", "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029",
"abi" => target_contract.abi, "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_eth_bytecode_db" => target_contract.verified_via_eth_bytecode_db,
"is_verified_via_verifier_alliance" => target_contract.verified_via_verifier_alliance, "is_verified_via_verifier_alliance" => target_contract.verified_via_verifier_alliance,
"language" => smart_contract_language(target_contract), "language" => smart_contract_language(target_contract),
@ -159,8 +171,6 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
"is_blueprint" => false "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) TestHelper.get_eip1967_implementation_non_zero_address(implementation_address_hash_string)
request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(target_contract.address_hash)}") 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" => "creation_bytecode" =>
"0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029", "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029",
"abi" => target_contract.abi, "abi" => target_contract.abi,
"proxy_type" => nil,
"implementations" => [],
"is_verified_via_eth_bytecode_db" => target_contract.verified_via_eth_bytecode_db, "is_verified_via_eth_bytecode_db" => target_contract.verified_via_eth_bytecode_db,
"is_verified_via_verifier_alliance" => target_contract.verified_via_verifier_alliance, "is_verified_via_verifier_alliance" => target_contract.verified_via_verifier_alliance,
"language" => smart_contract_language(target_contract), "language" => smart_contract_language(target_contract),
@ -363,6 +375,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
"creation_bytecode" => "creation_bytecode" =>
"0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029", "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029",
"abi" => target_contract.abi, "abi" => target_contract.abi,
"proxy_type" => nil,
"implementations" => [],
"is_verified_via_eth_bytecode_db" => target_contract.verified_via_eth_bytecode_db, "is_verified_via_eth_bytecode_db" => target_contract.verified_via_eth_bytecode_db,
"is_verified_via_verifier_alliance" => target_contract.verified_via_verifier_alliance, "is_verified_via_verifier_alliance" => target_contract.verified_via_verifier_alliance,
"language" => smart_contract_language(target_contract), "language" => smart_contract_language(target_contract),
@ -379,7 +393,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
assert correct_response == response assert correct_response == response
end 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 = implementation_contract =
insert(:smart_contract, insert(:smart_contract,
external_libraries: [], external_libraries: [],
@ -449,48 +463,18 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
|> with_block(status: :ok) |> with_block(status: :ok)
correct_response = %{ 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_read" => false,
"has_custom_methods_write" => 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, "is_self_destructed" => false,
"deployed_bytecode" => proxy_deployed_bytecode, "deployed_bytecode" => proxy_deployed_bytecode,
"creation_bytecode" => proxy_tx_input, "creation_bytecode" => proxy_tx_input,
"abi" => implementation_contract.abi, "proxy_type" => "eip1167",
"is_verified_via_eth_bytecode_db" => implementation_contract.verified_via_eth_bytecode_db, "implementations" => [
"is_verified_via_verifier_alliance" => implementation_contract.verified_via_verifier_alliance, %{
"language" => smart_contract_language(implementation_contract), "address" => Address.checksum(implementation_contract.address_hash),
"license_type" => "bsd_3_clause", "name" => implementation_contract.name
"certified" => false, }
"is_blueprint" => false ]
} }
request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(proxy_address.hash)}") request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(proxy_address.hash)}")
@ -551,6 +535,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
"creation_bytecode" => "creation_bytecode" =>
"0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029", "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029",
"abi" => target_contract.abi, "abi" => target_contract.abi,
"proxy_type" => nil,
"implementations" => [],
"is_verified_via_eth_bytecode_db" => target_contract.verified_via_eth_bytecode_db, "is_verified_via_eth_bytecode_db" => target_contract.verified_via_eth_bytecode_db,
"is_verified_via_verifier_alliance" => target_contract.verified_via_verifier_alliance, "is_verified_via_verifier_alliance" => target_contract.verified_via_verifier_alliance,
"language" => smart_contract_language(target_contract), "language" => smart_contract_language(target_contract),
@ -568,7 +554,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
end end
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 = implementation_contract =
insert(:smart_contract, insert(:smart_contract,
external_libraries: [], external_libraries: [],
@ -631,48 +617,18 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
) )
|> with_block(status: :ok) |> with_block(status: :ok)
formatted_implementation_address_hash_string = to_string(Address.checksum(implementation_contract.address_hash))
correct_response = %{ correct_response = %{
"verified_twin_address_hash" => Address.checksum(implementation_contract.address_hash), "proxy_type" => "clone_with_immutable_arguments",
"is_verified" => false, "implementations" => [
"is_changed_bytecode" => false, %{"address" => formatted_implementation_address_hash_string, "name" => implementation_contract.name}
"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_read" => false,
"has_custom_methods_write" => 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, "is_self_destructed" => false,
"deployed_bytecode" => proxy_deployed_bytecode, "deployed_bytecode" => proxy_deployed_bytecode,
"creation_bytecode" => proxy_tx_input, "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
} }
request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(proxy_address.hash)}") request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(proxy_address.hash)}")
@ -752,6 +708,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
assert response == assert response ==
%{ %{
"proxy_type" => nil,
"implementations" => [],
"has_custom_methods_read" => false, "has_custom_methods_read" => false,
"has_custom_methods_write" => false, "has_custom_methods_write" => false,
"is_self_destructed" => false, "is_self_destructed" => false,
@ -837,6 +795,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
assert response == assert response ==
%{ %{
"proxy_type" => nil,
"implementations" => [],
"has_custom_methods_read" => false, "has_custom_methods_read" => false,
"has_custom_methods_write" => false, "has_custom_methods_write" => false,
"is_self_destructed" => false, "is_self_destructed" => false,
@ -969,6 +929,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
assert response == assert response ==
%{ %{
"proxy_type" => nil,
"implementations" => [],
"has_custom_methods_read" => false, "has_custom_methods_read" => false,
"has_custom_methods_write" => false, "has_custom_methods_write" => false,
"is_self_destructed" => false, "is_self_destructed" => false,
@ -1034,7 +996,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
Conn.resp(conn, 200, eth_bytecode_response) Conn.resp(conn, 200, eth_bytecode_response)
end) end)
TestHelper.get_eip1967_implementation_error_response() TestHelper.get_eip1967_implementation_zero_addresses()
request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}")
@ -1056,6 +1018,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
assert response == assert response ==
%{ %{
"proxy_type" => nil,
"implementations" => [],
"has_custom_methods_read" => false, "has_custom_methods_read" => false,
"has_custom_methods_write" => false, "has_custom_methods_write" => false,
"is_self_destructed" => false, "is_self_destructed" => false,
@ -1064,8 +1028,6 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
"0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029"
} }
TestHelper.get_eip1967_implementation_zero_addresses()
request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}")
assert response = json_response(request, 200) assert response = json_response(request, 200)
@ -1148,6 +1110,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
implementation_address = insert(:address) implementation_address = insert(:address)
implementation_address_hash_string = to_string(implementation_address.hash) 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) TestHelper.get_eip1967_implementation_non_zero_address(implementation_address_hash_string)
request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}")
@ -1170,6 +1133,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
assert response == assert response ==
%{ %{
"proxy_type" => nil,
"implementations" => [],
"has_custom_methods_read" => false, "has_custom_methods_read" => false,
"has_custom_methods_write" => false, "has_custom_methods_write" => false,
"is_self_destructed" => 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)}") request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}")
assert response = json_response(request, 200) 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" => true} = response
assert %{"is_verified_via_eth_bytecode_db" => true} = response assert %{"is_verified_via_eth_bytecode_db" => true} = response
assert %{"is_partially_verified" => true} = response assert %{"is_partially_verified" => true} = response
@ -1265,6 +1235,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
implementation_address = insert(:address) implementation_address = insert(:address)
implementation_address_hash_string = to_string(implementation_address.hash) 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) TestHelper.get_eip1967_implementation_non_zero_address(implementation_address_hash_string)
request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}")
@ -1287,6 +1258,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
assert response == assert response ==
%{ %{
"proxy_type" => nil,
"implementations" => [],
"has_custom_methods_read" => false, "has_custom_methods_read" => false,
"has_custom_methods_write" => false, "has_custom_methods_write" => false,
"is_self_destructed" => 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)}") request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}")
assert response = json_response(request, 200) 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" => true} = response
assert %{"is_verified_via_eth_bytecode_db" => true} = response assert %{"is_verified_via_eth_bytecode_db" => true} = response
assert %{"is_partially_verified" => false} = response assert %{"is_partially_verified" => false} = response
@ -1382,6 +1360,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
implementation_address = insert(:address) implementation_address = insert(:address)
implementation_address_hash_string = to_string(implementation_address.hash) 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) TestHelper.get_eip1967_implementation_non_zero_address(implementation_address_hash_string)
request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}")
@ -1404,6 +1383,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
assert response == assert response ==
%{ %{
"proxy_type" => nil,
"implementations" => [],
"has_custom_methods_read" => false, "has_custom_methods_read" => false,
"has_custom_methods_write" => false, "has_custom_methods_write" => false,
"is_self_destructed" => 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)}") request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}")
assert response = json_response(request, 200) 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" => true} = response
assert %{"is_verified_via_eth_bytecode_db" => true} = response assert %{"is_verified_via_eth_bytecode_db" => true} = response
assert %{"is_partially_verified" => false} = 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 test "return 404 on unverified contract", %{conn: conn} do
address = insert(:contract_address) address = insert(:contract_address)
TestHelper.get_eip1967_implementation_zero_addresses()
request = request =
post(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}/query-read-method", %{ post(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}/query-read-method", %{
"contract_type" => "regular", "contract_type" => "regular",
@ -2909,8 +2893,6 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
custom_abi custom_abi
) )
TestHelper.get_eip1967_implementation_zero_addresses()
expect( expect(
EthereumJSONRPC.Mox, EthereumJSONRPC.Mox,
:json_rpc, :json_rpc,
@ -3050,8 +3032,6 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
test "return 404 on unverified contract", %{conn: conn} do test "return 404 on unverified contract", %{conn: conn} do
address = insert(:contract_address) address = insert(:contract_address)
TestHelper.get_eip1967_implementation_zero_addresses()
request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}/methods-read-proxy") request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}/methods-read-proxy")
assert %{"message" => "Not found"} = json_response(request, 404) assert %{"message" => "Not found"} = json_response(request, 404)
end end
@ -3421,7 +3401,6 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
test "return 404 on unverified contract", %{conn: conn} do test "return 404 on unverified contract", %{conn: conn} do
address = insert(:contract_address) address = insert(:contract_address)
TestHelper.get_eip1967_implementation_zero_addresses()
request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}/methods-write-proxy") request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}/methods-write-proxy")
assert %{"message" => "Not found"} = json_response(request, 404) assert %{"message" => "Not found"} = json_response(request, 404)
end end

@ -176,23 +176,6 @@ defmodule BlockScoutWeb.AddressViewTest do
assert "" = AddressView.balance_percentage(address, nil) assert "" = AddressView.balance_percentage(address, nil)
end 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 describe "hash/1" do
test "gives a string version of an address's hash" do test "gives a string version of an address's hash" do
address = %Address{ address = %Address{

@ -85,8 +85,6 @@ defmodule Explorer.Chain do
alias Explorer.Chain.Fetcher.{CheckBytecodeMatchingOnDemand, LookUpSmartContractSourcesOnDemand} alias Explorer.Chain.Fetcher.{CheckBytecodeMatchingOnDemand, LookUpSmartContractSourcesOnDemand}
alias Explorer.Chain.Import.Runner alias Explorer.Chain.Import.Runner
alias Explorer.Chain.InternalTransaction.{CallType, Type} alias Explorer.Chain.InternalTransaction.{CallType, Type}
alias Explorer.Chain.SmartContract.Proxy
alias Explorer.Chain.SmartContract.Proxy.Models.Implementation
alias Explorer.Market.MarketHistoryCache alias Explorer.Market.MarketHistoryCache
alias Explorer.{PagingOptions, Repo} alias Explorer.{PagingOptions, Repo}
@ -1136,7 +1134,8 @@ defmodule Explorer.Chain do
options options
|> Keyword.get(:necessity_by_association, %{}) |> Keyword.get(:necessity_by_association, %{})
|> Map.merge(%{ |> Map.merge(%{
[smart_contract: :smart_contract_additional_sources] => :optional [smart_contract: :smart_contract_additional_sources] => :optional,
:proxy_implementations => :optional
}) })
query = query =
@ -1171,20 +1170,7 @@ defmodule Explorer.Chain do
nil nil
) )
{implementation_address_hashes, _} = add_bytecode_twin_to_result(address_result, hash, options)
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)
end end
_ -> _ ->
@ -1206,31 +1192,12 @@ defmodule Explorer.Chain do
end end
end end
defp add_implementation_and_bytecode_twin_to_result(address_result, implementation_address_hashes, hash, options) do defp add_bytecode_twin_to_result(address_result, 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
address_verified_bytecode_twin_contract = 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 address_result
|> SmartContract.add_bytecode_twin_info_to_contract(address_verified_bytecode_twin_contract, hash) |> 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 end
@spec find_decompiled_contract_address(Hash.Address.t()) :: {:ok, Address.t()} | {:error, :not_found} @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) Decimal.mult(tokens, fiat_value)
end end
def contract?(%{contract_code: nil}), do: false
def contract?(%{contract_code: _}), do: true
@doc """ @doc """
Returns a stream of unfetched `t:Explorer.Chain.Address.CoinBalance.t/0`. Returns a stream of unfetched `t:Explorer.Chain.Address.CoinBalance.t/0`.

@ -26,6 +26,7 @@ defmodule Explorer.Chain.Address do
} }
alias Explorer.Chain.Cache.{Accounts, NetVersion} alias Explorer.Chain.Cache.{Accounts, NetVersion}
alias Explorer.Chain.SmartContract.Proxy
alias Explorer.Chain.SmartContract.Proxy.Models.Implementation 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 @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(:gas_used, :integer)
field(:ens_domain_name, :string, virtual: true) field(:ens_domain_name, :string, virtual: true)
field(:metadata, :any, 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) field(:implementation, :any, virtual: true)
has_one(:smart_contract, SmartContract, references: :hash) has_one(:smart_contract, SmartContract, references: :hash)
@ -460,4 +463,24 @@ defmodule Explorer.Chain.Address do
timeout: @timeout timeout: @timeout
) )
end 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 end

@ -228,7 +228,7 @@ defmodule Explorer.Chain.Address.Counters do
@spec address_to_gas_usage_count(Address.t()) :: Decimal.t() | nil @spec address_to_gas_usage_count(Address.t()) :: Decimal.t() | nil
def address_to_gas_usage_count(address) do 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) incoming_transaction_gas_usage = address_to_incoming_transaction_gas_usage(address.hash)
cond do cond do

@ -594,7 +594,7 @@ defmodule Explorer.Chain.SmartContract do
refetch_necessity_checked?: false refetch_necessity_checked?: false
} }
{implementation_address_hash, _} = {implementation_address_hash, _names, _proxy_type} =
Implementation.get_implementation( Implementation.get_implementation(
smart_contract, smart_contract,
Keyword.put(options, :proxy_without_abi?, true) 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 compose_address_for_unverified_smart_contract(address_result, _hash, _options), do: address_result
def single_implementation_smart_contract_from_proxy(proxy_hash, options) do 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 if implementation_address_hashes && Enum.count(implementation_address_hashes) == 1 do
implementation_address_hashes implementation_address_hashes
@ -740,21 +740,6 @@ defmodule Explorer.Chain.SmartContract do
|> Map.put(:smart_contract, address_verified_bytecode_twin_contract_updated) |> Map.put(:smart_contract, address_verified_bytecode_twin_contract_updated)
end 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 """ @doc """
Inserts a new smart contract and associated data into the database. 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), with true <- is_nil(current_smart_contract),
{:ok, address} <- Chain.hash_to_address(address_hash), {: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?} = {implementation_smart_contract, implementation_address_fetched?} =
if fetch_implementation? do if fetch_implementation? do
implementation_smart_contract = implementation_smart_contract =

@ -4,8 +4,9 @@ defmodule Explorer.Chain.SmartContract.Proxy do
""" """
alias EthereumJSONRPC.Contract alias EthereumJSONRPC.Contract
alias Explorer.Chain.{Hash, SmartContract} alias Explorer.Chain.{Address, Hash, SmartContract}
alias Explorer.Chain.SmartContract.Proxy alias Explorer.Chain.SmartContract.Proxy
alias Explorer.Chain.SmartContract.Proxy.Models.Implementation
alias Explorer.Chain.SmartContract.Proxy.{ alias Explorer.Chain.SmartContract.Proxy.{
Basic, 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 Fetches into DB proxy contract implementation's address and name from different proxy patterns
""" """
@spec fetch_implementation_address_hash(Hash.Address.t(), list(), options) :: @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) def fetch_implementation_address_hash(proxy_address_hash, proxy_abi, options)
when not is_nil(proxy_address_hash) do when not is_nil(proxy_address_hash) do
%{implementation_address_hash_strings: implementation_address_hash_strings, proxy_type: proxy_type} = %{implementation_address_hash_strings: implementation_address_hash_strings, proxy_type: proxy_type} =
@ -70,7 +71,7 @@ defmodule Explorer.Chain.SmartContract.Proxy do
end end
def fetch_implementation_address_hash(_, _, _) do def fetch_implementation_address_hash(_, _, _) do
{:empty, :empty} :empty
end end
@doc """ @doc """
@ -79,16 +80,20 @@ defmodule Explorer.Chain.SmartContract.Proxy do
@spec proxy_contract?(SmartContract.t(), Keyword.t()) :: boolean() @spec proxy_contract?(SmartContract.t(), Keyword.t()) :: boolean()
def proxy_contract?(smart_contract, options \\ []) do def proxy_contract?(smart_contract, options \\ []) do
{:ok, burn_address_hash} = string_to_address_hash(SmartContract.burn_address_hash_string()) {: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), with false <- is_nil(proxy_implementations),
false <- Enum.empty?(implementation.address_hashes), false <- Enum.empty?(proxy_implementations.address_hashes),
implementation_address_hash = Enum.at(implementation.address_hashes, 0), implementation_address_hash = Enum.at(proxy_implementations.address_hashes, 0),
false <- implementation_address_hash.bytes == burn_address_hash.bytes do false <- implementation_address_hash.bytes == burn_address_hash.bytes do
true true
else else
_ -> _ ->
{implementation_address_hash_strings, _implementation_names} = get_implementation(smart_contract, options) if options[:skip_implementation_fetch?] do
false
else
{implementation_address_hash_strings, _implementation_names, _proxy_type} =
get_implementation(smart_contract, options)
with false <- is_nil(implementation_address_hash_strings), with false <- is_nil(implementation_address_hash_strings),
false <- Enum.empty?(implementation_address_hash_strings) do false <- Enum.empty?(implementation_address_hash_strings) do
@ -108,6 +113,7 @@ defmodule Explorer.Chain.SmartContract.Proxy do
end end
end end
end end
end
@doc """ @doc """
Decodes address output into 20 bytes address hash Decodes address output into 20 bytes address hash
@ -138,7 +144,7 @@ defmodule Explorer.Chain.SmartContract.Proxy do
options options
) )
when not is_nil(proxy_address_hash) and not is_nil(abi) do 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 implementation_address_hash_strings
|> Enum.reduce([], fn implementation_address_hash_string, acc -> |> Enum.reduce([], fn implementation_address_hash_string, acc ->
@ -452,4 +458,39 @@ defmodule Explorer.Chain.SmartContract.Proxy do
|> join_associations(necessity_by_association) |> join_associations(necessity_by_association)
|> select_repo(options).one(timeout: 10_000) |> select_repo(options).one(timeout: 10_000)
end 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 end

@ -7,10 +7,17 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do
use Explorer.Schema use Explorer.Schema
import Ecto.Query,
only: [
from: 2,
select: 3
]
import Explorer.Chain, only: [select_repo: 1, string_to_address_hash: 1] import Explorer.Chain, only: [select_repo: 1, string_to_address_hash: 1]
alias Explorer.Chain.{Address, Hash, SmartContract} alias Explorer.Chain.{Address, Hash, SmartContract}
alias Explorer.Chain.SmartContract.Proxy alias Explorer.Chain.SmartContract.Proxy
alias Explorer.Chain.SmartContract.Proxy.Models.Implementation
alias Explorer.Counters.AverageBlockTime alias Explorer.Counters.AverageBlockTime
alias Explorer.Repo alias Explorer.Repo
alias Timex.Duration 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 Returns all implementations for the given smart-contract address hash
""" """
@spec get_proxy_implementations(Hash.Address.t() | nil, Keyword.t()) :: __MODULE__.t() | nil @spec get_proxy_implementations(Hash.Address.t() | nil, Keyword.t()) :: __MODULE__.t() | nil
def get_proxy_implementations(address_hash, options \\ []) do def get_proxy_implementations(proxy_address_hash, options \\ []) do
all_implementations_query = proxy_address_hash
from( |> get_proxy_implementations_query()
p in __MODULE__,
where: p.proxy_address_hash == ^address_hash
)
all_implementations_query
|> select_repo(options).one() |> select_repo(options).one()
end 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 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() @spec get_proxy_implementation_updated_at(Hash.Address.t() | nil, Keyword.t()) :: DateTime.t()
def get_proxy_implementation_updated_at(address_hash, options) do def get_proxy_implementation_updated_at(proxy_address_hash, options) do
updated_at_query = 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( from(
p in __MODULE__, p in __MODULE__,
where: p.proxy_address_hash == ^address_hash, where: p.proxy_address_hash == ^proxy_address_hash
select: p.updated_at
) )
updated_at_query
|> select_repo(options).one()
end end
@doc """ @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(smart_contract, options \\ [])
def get_implementation( def get_implementation(
@ -152,6 +156,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do
) )
end end
# credo:disable-for-next-line Credo.Check.Refactor.CyclomaticComplexity
def get_implementation( def get_implementation(
%{ %{
updated: %SmartContract{ updated: %SmartContract{
@ -164,14 +169,15 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do
}, },
options options
) do ) do
{implementation_addresses_hash_from_db, implementation_names_from_db, implementation_updated_at_from_db} = proxy_implementations = get_proxy_implementations(address_hash, options)
implementation_from_db(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 if fetch_implementation?(implementation_address_fetched?, refetch_necessity_checked?, implementation_updated_at) do
get_implementation_address_hash_task = get_implementation_address_hash_task =
Task.async(fn -> 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) result = Proxy.fetch_implementation_address_hash(address_hash, abi, options)
callback = Keyword.get(options, :callback, nil) 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) || case Task.yield(get_implementation_address_hash_task, timeout) ||
Task.ignore(get_implementation_address_hash_task) do Task.ignore(get_implementation_address_hash_task) do
{:ok, {:empty, :empty}} -> {:ok, :empty} ->
{[], []} {[], [], nil}
{:ok, {:error, :error}} -> {:ok, :error} ->
{db_implementation_data_converter(implementation_addresses_hash_from_db), format_proxy_implementations_response(proxy_implementations)
db_implementation_data_converter(implementation_names_from_db)}
{:ok, {address_hash, _name} = result} when not is_nil(address_hash) -> {:ok, %Implementation{} = result} ->
result format_proxy_implementations_response(result)
_ -> _ ->
{db_implementation_data_converter(implementation_addresses_hash_from_db), format_proxy_implementations_response(proxy_implementations)
db_implementation_data_converter(implementation_names_from_db)}
end end
else else
{db_implementation_data_converter(implementation_addresses_hash_from_db), format_proxy_implementations_response(proxy_implementations)
db_implementation_data_converter(implementation_names_from_db)}
end end
end end
def get_implementation(_, _), do: {[], []} def get_implementation(_, _), do: {[], [], nil}
defp fetch_implementation?(implementation_address_fetched?, refetch_necessity_checked?, implementation_updated_at) do defp fetch_implementation?(implementation_address_fetched?, refetch_necessity_checked?, implementation_updated_at) do
(!implementation_address_fetched? || !refetch_necessity_checked?) && (!implementation_address_fetched? || !refetch_necessity_checked?) &&
check_implementation_refetch_necessity(implementation_updated_at) check_implementation_refetch_necessity(implementation_updated_at)
end end
defp implementation_from_db(address_hash, options) do defp format_proxy_implementations_response(proxy_implementations) do
proxy_implementations = get_proxy_implementations(address_hash, options) {(proxy_implementations && db_implementation_data_converter(proxy_implementations.address_hashes)) || [],
(proxy_implementations && db_implementation_data_converter(proxy_implementations.names)) || [],
if proxy_implementations do proxy_implementations && proxy_implementations.proxy_type}
{proxy_implementations.address_hashes, proxy_implementations.names, proxy_implementations.updated_at}
else
{[], [], nil}
end
end end
@doc """ @doc """
@ -279,9 +278,9 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do
Saves proxy's implementation into the DB Saves proxy's implementation into the DB
""" """
@spec save_implementation_data([String.t()], Hash.Address.t(), atom() | nil, Keyword.t()) :: @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 def save_implementation_data(:error, _proxy_address_hash, _proxy_type, _options) do
{:error, :error} :error
end end
def save_implementation_data(implementation_address_hash_strings, proxy_address_hash, proxy_type, options) 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 implementation_address_hash_strings == [] do
upsert_implementation(proxy_address_hash, proxy_type, [], [], options) upsert_implementation(proxy_address_hash, proxy_type, [], [], options)
{:empty, :empty} :empty
end end
def save_implementation_data( 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 when is_burn_signature(empty_implementation_address_hash_string) do
upsert_implementation(proxy_address_hash, proxy_type, [], [], options) upsert_implementation(proxy_address_hash, proxy_type, [], [], options)
{:empty, :empty} :empty
end end
def save_implementation_data( def save_implementation_data(
@ -331,17 +330,22 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do
|> Enum.unzip() |> Enum.unzip()
if Enum.empty?(implementation_addresses) do if Enum.empty?(implementation_addresses) do
{:empty, :empty} :empty
else else
upsert_implementation( case upsert_implementation(
proxy_address_hash, proxy_address_hash,
proxy_type, proxy_type,
implementation_addresses, implementation_addresses,
implementation_names, implementation_names,
options 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
end end

@ -1873,7 +1873,7 @@ defmodule Explorer.Chain.Transaction do
@spec where_transactions_to_from(Hash.Address.t()) :: any() @spec where_transactions_to_from(Hash.Address.t()) :: any()
def where_transactions_to_from(address_hash) do def where_transactions_to_from(address_hash) do
with {:ok, address} <- Chain.hash_to_address(address_hash), 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) dynamic([transaction], transaction.to_address_hash == ^address_hash)
else else
_ -> _ ->

@ -4,10 +4,14 @@ defmodule Explorer.SmartContract.Helper do
""" """
alias Explorer.{Chain, Helper} 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 Explorer.SmartContract.Writer
alias Phoenix.HTML alias Phoenix.HTML
@api_true [api?: true]
def queriable_method?(method) do def queriable_method?(method) do
method["constant"] || method["stateMutability"] == "view" || method["stateMutability"] == "pure" method["constant"] || method["stateMutability"] == "view" || method["stateMutability"] == "pure"
end 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(binary) when is_binary(binary), do: Helper.parse_integer(binary) || binary
def prepare_license_type(_), do: nil 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 end

@ -28,7 +28,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do
# fetch nil implementation and don't save it to db # fetch nil implementation and don't save it to db
TestHelper.get_eip1967_implementation_zero_addresses() TestHelper.get_eip1967_implementation_zero_addresses()
assert {[], []} = Implementation.get_implementation(smart_contract) assert {[], [], nil} = Implementation.get_implementation(smart_contract)
verify!(EthereumJSONRPC.Mox) verify!(EthereumJSONRPC.Mox)
assert_empty_implementation(smart_contract.address_hash) 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) 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) Implementation.get_implementation(smart_contract)
verify!(EthereumJSONRPC.Mox) verify!(EthereumJSONRPC.Mox)
@ -57,7 +57,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do
TestHelper.get_eip1967_implementation_error_response() TestHelper.get_eip1967_implementation_error_response()
assert {[^string_implementation_address_hash], ["implementation"]} = assert {[^string_implementation_address_hash], ["implementation"], :eip1967} =
Implementation.get_implementation(smart_contract) Implementation.get_implementation(smart_contract)
verify!(EthereumJSONRPC.Mox) verify!(EthereumJSONRPC.Mox)
@ -78,7 +78,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do
Application.put_env(:explorer, :proxy, proxy) Application.put_env(:explorer, :proxy, proxy)
assert {[^string_implementation_address_hash], ["implementation"]} = assert {[^string_implementation_address_hash], ["implementation"], :eip1967} =
Implementation.get_implementation(smart_contract) Implementation.get_implementation(smart_contract)
{contract_2, _} = SmartContract.address_hash_to_smart_contract_with_bytecode_twin(smart_contract.address_hash) {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() TestHelper.get_eip1967_implementation_zero_addresses()
assert {[], []} = Implementation.get_implementation(smart_contract) assert {[], [], nil} = Implementation.get_implementation(smart_contract)
verify!(EthereumJSONRPC.Mox) verify!(EthereumJSONRPC.Mox)
@ -106,7 +106,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do
test "get_implementation/1 for twins contract" do test "get_implementation/1 for twins contract" do
# return nils for nil # return nils for nil
assert {[], []} = Implementation.get_implementation(nil) assert {[], [], nil} = Implementation.get_implementation(nil)
smart_contract = insert(:smart_contract) smart_contract = insert(:smart_contract)
twin_address = insert(:contract_address) twin_address = insert(:contract_address)
@ -123,11 +123,11 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do
Application.put_env(:explorer, :proxy, proxy) Application.put_env(:explorer, :proxy, proxy)
# fetch nil implementation # fetch nil implementation
assert {[], []} = Implementation.get_implementation(bytecode_twin) assert {[], [], :unknown} = Implementation.get_implementation(bytecode_twin)
verify!(EthereumJSONRPC.Mox) verify!(EthereumJSONRPC.Mox)
refute_implementations(smart_contract.address_hash) refute_implementations(smart_contract.address_hash)
assert {[], []} = Implementation.get_implementation(bytecode_twin) assert {[], [], :unknown} = Implementation.get_implementation(bytecode_twin)
verify!(EthereumJSONRPC.Mox) verify!(EthereumJSONRPC.Mox)
refute_implementations(smart_contract.address_hash) 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) 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) Implementation.get_implementation(bytecode_twin)
verify!(EthereumJSONRPC.Mox) verify!(EthereumJSONRPC.Mox)
@ -158,7 +158,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do
refute_implementations(smart_contract.address_hash) refute_implementations(smart_contract.address_hash)
assert {[^string_implementation_address_hash], ["implementation"]} = assert {[^string_implementation_address_hash], ["implementation"], :eip1967} =
Implementation.get_implementation(bytecode_twin) Implementation.get_implementation(bytecode_twin)
verify!(EthereumJSONRPC.Mox) verify!(EthereumJSONRPC.Mox)
@ -171,11 +171,11 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do
_implementation_smart_contract = insert(:smart_contract, name: "implementation") _implementation_smart_contract = insert(:smart_contract, name: "implementation")
# fetch nil implementation # fetch nil implementation
assert {[], []} = Implementation.get_implementation(bytecode_twin) assert {[], [], nil} = Implementation.get_implementation(bytecode_twin)
verify!(EthereumJSONRPC.Mox) verify!(EthereumJSONRPC.Mox)
refute_implementations(smart_contract.address_hash) refute_implementations(smart_contract.address_hash)
assert {[], []} = Implementation.get_implementation(bytecode_twin) assert {[], [], nil} = Implementation.get_implementation(bytecode_twin)
verify!(EthereumJSONRPC.Mox) verify!(EthereumJSONRPC.Mox)
refute_implementations(smart_contract.address_hash) 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) # 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) # verify!(EthereumJSONRPC.Mox)
@ -200,7 +200,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do
# TestHelper.get_eip1967_implementation_error_response() # 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) # verify!(EthereumJSONRPC.Mox)
@ -233,12 +233,12 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do
# # fetch nil implementation # # fetch nil implementation
# TestHelper.get_eip1967_implementation_zero_addresses() # TestHelper.get_eip1967_implementation_zero_addresses()
# assert {nil, nil} = Implementation.get_implementation(bytecode_twin) # assert {[], [], nil} = Implementation.get_implementation(bytecode_twin)
# verify!(EthereumJSONRPC.Mox) # verify!(EthereumJSONRPC.Mox)
# refute_implementations(smart_contract.address_hash) # refute_implementations(smart_contract.address_hash)
# TestHelper.get_eip1967_implementation_zero_addresses() # TestHelper.get_eip1967_implementation_zero_addresses()
# assert {nil, nil} = Implementation.get_implementation(bytecode_twin) # assert {[], [], nil} = Implementation.get_implementation(bytecode_twin)
# verify!(EthereumJSONRPC.Mox) # verify!(EthereumJSONRPC.Mox)
# refute_implementations(smart_contract.address_hash) # 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) # 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) # verify!(EthereumJSONRPC.Mox)
@ -254,7 +254,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do
# TestHelper.get_eip1967_implementation_zero_addresses() # TestHelper.get_eip1967_implementation_zero_addresses()
# assert {nil, nil} = Implementation.get_implementation(bytecode_twin) # assert {[], [], nil} = Implementation.get_implementation(bytecode_twin)
# verify!(EthereumJSONRPC.Mox) # verify!(EthereumJSONRPC.Mox)

@ -2599,7 +2599,8 @@ defmodule Explorer.ChainTest do
:contracts_creation_internal_transaction, :contracts_creation_internal_transaction,
:contracts_creation_transaction, :contracts_creation_transaction,
:token, :token,
[smart_contract: :smart_contract_additional_sources] [smart_contract: :smart_contract_additional_sources],
:proxy_implementations
]) ])
options = [ options = [
@ -2612,8 +2613,6 @@ defmodule Explorer.ChainTest do
} }
] ]
TestHelper.get_eip1967_implementation_zero_addresses()
response = Chain.find_contract_address(address.hash, options, true) response = Chain.find_contract_address(address.hash, options, true)
assert response == {:ok, address} assert response == {:ok, address}

Loading…
Cancel
Save