diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex index 43418c135d..6d3c767a49 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex @@ -16,7 +16,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do alias BlockScoutWeb.AccessHelper alias BlockScoutWeb.API.V2.{BlockView, TransactionView, WithdrawalView} - alias Explorer.{Chain, Market, Repo} + alias Explorer.{Chain, Market} alias Explorer.Chain.Address alias Explorer.Chain.Address.Counters alias Explorer.Chain.Token.Instance @@ -71,7 +71,8 @@ defmodule BlockScoutWeb.API.V2.AddressController 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), - fully_preloaded_address <- maybe_preload_smart_contract_associations(address) do + fully_preloaded_address <- + Address.maybe_preload_smart_contract_associations(address, @contract_address_preloads, @api_true) do CoinBalanceOnDemand.trigger_fetch(fully_preloaded_address) conn @@ -482,9 +483,4 @@ defmodule BlockScoutWeb.API.V2.AddressController do {:ok, address_hash, address} end end - - defp maybe_preload_smart_contract_associations(%Address{contract_code: nil} = address), do: address - - defp maybe_preload_smart_contract_associations(%Address{contract_code: _} = address), - do: Repo.replica().preload(address, @contract_address_preloads) end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs index ebe923dff6..411803430c 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs @@ -1,5 +1,6 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do use BlockScoutWeb.ConnCase + use EthereumJSONRPC.Case, async: false alias BlockScoutWeb.Models.UserFromAuth alias Explorer.{Chain, Repo} @@ -22,6 +23,9 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do alias Explorer.Chain.Address.CurrentTokenBalance import Explorer.Chain, only: [hash_to_lower_case_string: 1] + import Mox + + setup :set_mox_global describe "/addresses/{address_hash}" do test "get 404 on non existing address", %{conn: conn} do @@ -44,7 +48,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do correct_response = %{ "hash" => Address.checksum(address.hash), "is_contract" => false, - "is_verified" => false, + "is_verified" => nil, "name" => nil, "private_tags" => [], "public_tags" => [], @@ -79,6 +83,47 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do assert ^correct_response = json_response(request, 200) end + test "get contract info", %{conn: conn} do + smart_contract = insert(:smart_contract) + + tx = + insert(:transaction, + to_address_hash: nil, + to_address: nil, + created_contract_address_hash: smart_contract.address_hash, + created_contract_address: smart_contract.address + ) + + insert(:address_name, + address: smart_contract.address, + primary: true, + name: smart_contract.name, + address_hash: smart_contract.address_hash + ) + + name = smart_contract.name + from = Address.checksum(tx.from_address_hash) + tx_hash = to_string(tx.hash) + address_hash = Address.checksum(smart_contract.address_hash) + + get_eip1967_implementation_non_zero_address() + + request = get(conn, "/api/v2/addresses/#{Address.checksum(smart_contract.address_hash)}") + + assert %{ + "hash" => ^address_hash, + "is_contract" => true, + "is_verified" => true, + "name" => ^name, + "private_tags" => [], + "public_tags" => [], + "watchlist_names" => [], + "creator_address_hash" => ^from, + "creation_tx_hash" => ^tx_hash, + "implementation_address" => "0x0000000000000000000000000000000000000001" + } = json_response(request, 200) + end + test "get watchlist id", %{conn: conn} do auth = build(:auth) address = insert(:address) @@ -2622,4 +2667,19 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do end def check_total(_, _, _), do: true + + def get_eip1967_implementation_non_zero_address do + expect(EthereumJSONRPC.Mox, :json_rpc, fn %{ + id: 0, + method: "eth_getStorageAt", + params: [ + _, + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc", + "latest" + ] + }, + _options -> + {:ok, "0x0000000000000000000000000000000000000000000000000000000000000001"} + end) + end end diff --git a/apps/explorer/lib/explorer/chain/address.ex b/apps/explorer/lib/explorer/chain/address.ex index 0b38b08014..5803f35732 100644 --- a/apps/explorer/lib/explorer/chain/address.ex +++ b/apps/explorer/lib/explorer/chain/address.ex @@ -8,6 +8,7 @@ defmodule Explorer.Chain.Address do use Explorer.Schema alias Ecto.Changeset + alias Explorer.Chain alias Explorer.Chain.{ Address, @@ -252,6 +253,16 @@ defmodule Explorer.Chain.Address do end) end + @doc """ + Preloads provided contracts associations if address has contract_code which is not nil + """ + @spec maybe_preload_smart_contract_associations(Address.t(), list, list) :: Address.t() + def maybe_preload_smart_contract_associations(%Address{contract_code: nil} = address, _associations, _options), + do: address + + def maybe_preload_smart_contract_associations(%Address{contract_code: _} = address, associations, options), + do: Chain.select_repo(options).preload(address, associations) + @doc """ Counts all the addresses where the `fetched_coin_balance` is > 0. """