From 668d5a288953866f0e38491e5592c6fd04cd2c0a Mon Sep 17 00:00:00 2001 From: Nikita Pozdniakov Date: Mon, 13 Nov 2023 13:31:16 +0300 Subject: [PATCH 1/3] Do not preload smart contract info if address has no contract_code --- .../controllers/api/v2/address_controller.ex | 26 +++++++++++++------ .../lib/block_scout_web/views/address_view.ex | 6 ++--- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex index 1ca5db5dcd..43418c135d 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,8 @@ defmodule BlockScoutWeb.API.V2.AddressController do alias BlockScoutWeb.AccessHelper alias BlockScoutWeb.API.V2.{BlockView, TransactionView, WithdrawalView} - alias Explorer.{Chain, Market} + alias Explorer.{Chain, Market, Repo} + alias Explorer.Chain.Address alias Explorer.Chain.Address.Counters alias Explorer.Chain.Token.Instance alias Indexer.Fetcher.{CoinBalanceOnDemand, TokenBalanceOnDemand} @@ -46,15 +47,18 @@ defmodule BlockScoutWeb.API.V2.AddressController do @address_options [ necessity_by_association: %{ - :contracts_creation_internal_transaction => :optional, :names => :optional, - :smart_contract => :optional, - :token => :optional, - :contracts_creation_transaction => :optional + :token => :optional }, api?: true ] + @contract_address_preloads [ + :smart_contract, + :contracts_creation_internal_transaction, + :contracts_creation_transaction + ] + @nft_necessity_by_association [ necessity_by_association: %{ :token => :optional @@ -66,12 +70,13 @@ defmodule BlockScoutWeb.API.V2.AddressController do action_fallback(BlockScoutWeb.API.V2.FallbackController) def address(conn, %{"address_hash_param" => address_hash_string} = params) do - with {:ok, _address_hash, address} <- validate_address(address_hash_string, params, @address_options) do - CoinBalanceOnDemand.trigger_fetch(address) + with {:ok, _address_hash, address} <- validate_address(address_hash_string, params, @address_options), + fully_preloaded_address <- maybe_preload_smart_contract_associations(address) do + CoinBalanceOnDemand.trigger_fetch(fully_preloaded_address) conn |> put_status(200) - |> render(:address, %{address: address}) + |> render(:address, %{address: fully_preloaded_address}) end end @@ -477,4 +482,9 @@ 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/lib/block_scout_web/views/address_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_view.ex index 41ebf80976..3098dfc096 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_view.ex @@ -258,7 +258,7 @@ defmodule BlockScoutWeb.AddressView do Enum.any?(address.smart_contract.abi || [], &is_read_function?(&1)) end - def smart_contract_with_read_only_functions?(%Address{smart_contract: nil}), do: false + def smart_contract_with_read_only_functions?(%Address{smart_contract: _}), do: false def is_read_function?(function), do: Helper.queriable_method?(function) || Helper.read_with_wallet_method?(function) @@ -268,7 +268,7 @@ defmodule BlockScoutWeb.AddressView do SmartContract.proxy_contract?(smart_contract, options) end - def smart_contract_is_proxy?(%Address{smart_contract: nil}, _), do: false + def smart_contract_is_proxy?(%Address{smart_contract: _}, _), do: false def smart_contract_with_write_functions?(%Address{smart_contract: %SmartContract{}} = address) do !contract_interaction_disabled?() && @@ -278,7 +278,7 @@ defmodule BlockScoutWeb.AddressView do ) end - def smart_contract_with_write_functions?(%Address{smart_contract: nil}), do: false + def smart_contract_with_write_functions?(%Address{smart_contract: _}), do: false def has_decompiled_code?(address) do address.has_decompiled_code? || From aee725b151385e971c5e152f323cd96e900d9de8 Mon Sep 17 00:00:00 2001 From: Nikita Pozdniakov Date: Mon, 13 Nov 2023 13:33:30 +0300 Subject: [PATCH 2/3] Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39a7a802eb..72e480d818 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ ### Fixes +- [#8814](https://github.com/blockscout/blockscout/pull/8814) - Improve performance for EOA addresses in `/api/v2/addresses/{address_hash}` - [#8813](https://github.com/blockscout/blockscout/pull/8813) - Force verify twin contracts on `/api/v2/import/smart-contracts/{address_hash}` - [#8784](https://github.com/blockscout/blockscout/pull/8784) - Fix Indexer.Transform.Addresses for non-Suave setup - [#8770](https://github.com/blockscout/blockscout/pull/8770) - Fix for eth_getbalance API v1 endpoint when requesting latest tag From 98210e4bf133c9b95ce3e178163cccaa88f4ef6d Mon Sep 17 00:00:00 2001 From: Nikita Pozdniakov Date: Tue, 14 Nov 2023 11:26:39 +0300 Subject: [PATCH 3/3] Fix after review; Add test --- .../controllers/api/v2/address_controller.ex | 10 +-- .../api/v2/address_controller_test.exs | 62 ++++++++++++++++++- apps/explorer/lib/explorer/chain/address.ex | 11 ++++ 3 files changed, 75 insertions(+), 8 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex index 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. """