From f8b4cb794a480759f9fb1116f985c952e1aee385 Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Mon, 21 Oct 2024 17:54:47 +0300 Subject: [PATCH] feat: Add missing filecoin robust addresses (#10935) * feat: Add missing filecoin robust addresses * Narrow down spec * Refactoring --- .../controllers/api/v2/address_controller.ex | 23 ++- .../views/api/v2/address_view.ex | 36 +++-- .../views/api/v2/filecoin_view.ex | 136 +++++++++++++----- .../views/api/v2/search_view.ex | 20 ++- .../views/api/v2/smart_contract_view.ex | 20 ++- .../views/api/v2/token_view.ex | 14 ++ apps/explorer/lib/explorer/chain/address.ex | 28 +++- 7 files changed, 221 insertions(+), 56 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 012ecc6843..08a8292484 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 @@ -83,12 +83,23 @@ defmodule BlockScoutWeb.API.V2.AddressController do api?: true ] - @contract_address_preloads [ - :smart_contract, - :contracts_creation_internal_transaction, - :contracts_creation_transaction, - :proxy_implementations - ] + case Application.compile_env(:explorer, :chain_type) do + :filecoin -> + @contract_address_preloads [ + :smart_contract, + [contracts_creation_internal_transaction: :from_address], + [contracts_creation_transaction: :from_address], + :proxy_implementations + ] + + _ -> + @contract_address_preloads [ + :smart_contract, + :contracts_creation_internal_transaction, + :contracts_creation_transaction, + :proxy_implementations + ] + end @nft_necessity_by_association [ necessity_by_association: %{ diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex index b39fe2f26f..b3010165ff 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex @@ -91,7 +91,8 @@ defmodule BlockScoutWeb.API.V2.AddressView do balance = address.fetched_coin_balance && address.fetched_coin_balance.value exchange_rate = Market.get_coin_exchange_rate().usd_value - creator_hash = AddressView.from_address_hash(address) + creation_transaction = Address.creation_transaction(address) + creator_hash = creation_transaction && creation_transaction.from_address_hash creation_tx = creator_hash && AddressView.transaction_hash(address) token = address.token && TokenView.render("token.json", %{token: address.token}) @@ -112,14 +113,18 @@ defmodule BlockScoutWeb.API.V2.AddressView do "has_beacon_chain_withdrawals" => Counters.check_if_withdrawals_at_address(address.hash, @api_true) }) - if Enum.empty?(implementations) do - extended_info - else - Map.merge(extended_info, %{ - "proxy_type" => proxy_type, - "implementations" => implementations - }) - end + result = + if Enum.empty?(implementations) do + extended_info + else + Map.merge(extended_info, %{ + "proxy_type" => proxy_type, + "implementations" => implementations + }) + end + + result + |> chain_type_fields(%{address: creation_transaction && creation_transaction.from_address, field_prefix: "creator"}) end @spec prepare_token_balance(Chain.Address.TokenBalance.t(), boolean()) :: map() @@ -238,4 +243,17 @@ defmodule BlockScoutWeb.API.V2.AddressView do token: token }) end + + case Application.compile_env(:explorer, :chain_type) do + :filecoin -> + defp chain_type_fields(result, params) do + # credo:disable-for-next-line Credo.Check.Design.AliasUsage + BlockScoutWeb.API.V2.FilecoinView.put_filecoin_robust_address(result, params) + end + + _ -> + defp chain_type_fields(result, _params) do + result + end + end end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/filecoin_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/filecoin_view.ex index 191fbf6f69..398bc56f67 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/filecoin_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/filecoin_view.ex @@ -1,35 +1,105 @@ -defmodule BlockScoutWeb.API.V2.FilecoinView do - @moduledoc """ - View functions for rendering Filecoin-related data in JSON format. - """ - - alias Explorer.Chain.Address - - @doc """ - Extends the json output with a sub-map containing information related to - Filecoin native addressing. - """ - @spec extend_address_json_response(map(), Address.t()) :: map() - def extend_address_json_response(result, %Address{} = address) do - filecoin_id = Map.get(address, :filecoin_id) - filecoin_robust = Map.get(address, :filecoin_robust) - filecoin_actor_type = Map.get(address, :filecoin_actor_type) - - is_fetched = - Enum.all?( - [ - filecoin_id, - filecoin_robust, - filecoin_actor_type - ], - &(not is_nil(&1)) - ) - - Map.put(result, :filecoin, %{ - is_fetched: is_fetched, - id: filecoin_id, - robust: filecoin_robust, - actor_type: filecoin_actor_type - }) +if Application.compile_env(:explorer, :chain_type) == :filecoin do + defmodule BlockScoutWeb.API.V2.FilecoinView do + @moduledoc """ + View functions for rendering Filecoin-related data in JSON format. + """ + + alias Explorer.Chain + alias Explorer.Chain.Address + + @api_true [api?: true] + + @doc """ + Extends the json output with a sub-map containing information related to + Filecoin native addressing. + """ + @spec extend_address_json_response(map(), Address.t()) :: map() + def extend_address_json_response( + result, + %Address{filecoin_id: filecoin_id, filecoin_robust: filecoin_robust, filecoin_actor_type: filecoin_actor_type} + ) do + Map.put(result, :filecoin, %{ + id: filecoin_id, + robust: filecoin_robust, + actor_type: filecoin_actor_type + }) + end + + @spec preload_and_put_filecoin_robust_address(map(), %{ + address_hash: String.t() | nil, + field_prefix: String.t() | nil + }) :: + map() + def preload_and_put_filecoin_robust_address(result, %{address_hash: address_hash} = params) do + address = address_hash && Address.get(address_hash, @api_true) + + put_filecoin_robust_address(result, Map.put(params, :address, address)) + end + + @doc """ + Adds a Filecoin robust address to the given result. + + ## Parameters + + - result: The initial result to which the Filecoin robust address will be added. + - opts: A map containing the following keys: + - `:address` - A struct containing the `filecoin_robust` address. + - `:field_prefix` - A prefix to be used for the field name in the result. + + ## Returns + + The updated result with the Filecoin robust address added. + """ + @spec put_filecoin_robust_address(map(), %{ + required(:address) => Address.t(), + required(:field_prefix) => String.t() | nil, + optional(any) => any + }) :: map() + def put_filecoin_robust_address(result, %{ + address: %Address{filecoin_robust: filecoin_robust}, + field_prefix: field_prefix + }) do + put_filecoin_robust_address_internal(result, filecoin_robust, field_prefix) + end + + def put_filecoin_robust_address(result, %{field_prefix: field_prefix}) do + put_filecoin_robust_address_internal(result, nil, field_prefix) + end + + defp put_filecoin_robust_address_internal(result, filecoin_robust, field_prefix) do + field_name = (field_prefix && "#{field_prefix}_filecoin_robust_address") || "filecoin_robust_address" + Map.put(result, field_name, filecoin_robust) + end + + @doc """ + Preloads and inserts Filecoin robust addresses into the search results. + + ## Parameters + + - search_results: The search results that need to be enriched with Filecoin robust addresses. + + ## Returns + + - The search results with preloaded Filecoin robust addresses. + """ + @spec preload_and_put_filecoin_robust_address_to_search_results(list()) :: list() + def preload_and_put_filecoin_robust_address_to_search_results(search_results) do + addresses_map = + search_results + |> Enum.map(& &1["address"]) + |> Enum.reject(&is_nil/1) + |> Chain.hashes_to_addresses(@api_true) + |> Enum.group_by(&to_string(&1.hash)) + + search_results + |> Enum.map(fn + %{"address" => address_hash} = result when not is_nil(address_hash) -> + address = addresses_map[String.downcase(address_hash)] |> List.first() + put_filecoin_robust_address(result, %{address: address, field_prefix: nil}) + + other -> + other + end) + end end end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/search_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/search_view.ex index 026f4ba680..24db155525 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/search_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/search_view.ex @@ -6,11 +6,14 @@ defmodule BlockScoutWeb.API.V2.SearchView do alias Explorer.Chain.{Address, Beacon.Blob, Block, Hash, Transaction, UserOperation} def render("search_results.json", %{search_results: search_results, next_page_params: next_page_params}) do - %{"items" => Enum.map(search_results, &prepare_search_result/1), "next_page_params" => next_page_params} + %{ + "items" => search_results |> Enum.map(&prepare_search_result/1) |> chain_type_fields(), + "next_page_params" => next_page_params + } end def render("search_results.json", %{search_results: search_results}) do - Enum.map(search_results, &prepare_search_result/1) + search_results |> Enum.map(&prepare_search_result/1) |> chain_type_fields() end def render("search_results.json", %{result: {:ok, result}}) do @@ -159,4 +162,17 @@ defmodule BlockScoutWeb.API.V2.SearchView do defp redirect_search_results(%Blob{} = item) do %{"type" => "blob", "parameter" => to_string(item.hash)} end + + case Application.compile_env(:explorer, :chain_type) do + :filecoin -> + defp chain_type_fields(result) do + # credo:disable-for-next-line Credo.Check.Design.AliasUsage + BlockScoutWeb.API.V2.FilecoinView.preload_and_put_filecoin_robust_address_to_search_results(result) + end + + _ -> + defp chain_type_fields(result) do + result + end + end end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex index d9f49cb2c8..80c98107e2 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex @@ -184,10 +184,10 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do target_contract = if smart_contract_verified, do: smart_contract, else: bytecode_twin_contract + verified_twin_address_hash = bytecode_twin_contract && Address.checksum(bytecode_twin_contract.address_hash) + %{ - "verified_twin_address_hash" => - bytecode_twin_contract && - Address.checksum(bytecode_twin_contract.address_hash), + "verified_twin_address_hash" => verified_twin_address_hash, "is_verified" => smart_contract_verified, "is_changed_bytecode" => smart_contract_verified && smart_contract.is_changed_bytecode, "is_partially_verified" => smart_contract.partially_verified && smart_contract_verified, @@ -237,6 +237,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do } |> Map.merge(bytecode_info(address)) |> add_zksync_info(target_contract) + |> chain_type_fields(%{address_hash: verified_twin_address_hash, field_prefix: "verified_twin"}) end def prepare_smart_contract(address, implementations, proxy_type, conn) do @@ -447,4 +448,17 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do def render_json(value, _type) do to_string(value) end + + case Application.compile_env(:explorer, :chain_type) do + :filecoin -> + defp chain_type_fields(result, params) do + # credo:disable-for-next-line Credo.Check.Design.AliasUsage + BlockScoutWeb.API.V2.FilecoinView.preload_and_put_filecoin_robust_address(result, params) + end + + _ -> + defp chain_type_fields(result, _address) do + result + end + end end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/token_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/token_view.ex index f1ed2d9953..935a06ff8d 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/token_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/token_view.ex @@ -42,6 +42,7 @@ defmodule BlockScoutWeb.API.V2.TokenView do "circulating_market_cap" => token.circulating_market_cap } |> maybe_append_bridged_info(token) + |> chain_type_fields(%{address: token.contract_address, field_prefix: nil}) end def render("token_balances.json", %{ @@ -139,4 +140,17 @@ defmodule BlockScoutWeb.API.V2.TokenView do map end end + + case Application.compile_env(:explorer, :chain_type) do + :filecoin -> + defp chain_type_fields(result, params) do + # credo:disable-for-next-line Credo.Check.Design.AliasUsage + BlockScoutWeb.API.V2.FilecoinView.put_filecoin_robust_address(result, params) + end + + _ -> + defp chain_type_fields(result, _params) do + result + end + end end diff --git a/apps/explorer/lib/explorer/chain/address.ex b/apps/explorer/lib/explorer/chain/address.ex index 5c68c28b0b..72baeb7bab 100644 --- a/apps/explorer/lib/explorer/chain/address.ex +++ b/apps/explorer/lib/explorer/chain/address.ex @@ -134,7 +134,7 @@ defmodule Explorer.Chain.Address do alias Explorer.Chain.SmartContract.Proxy alias Explorer.Chain.SmartContract.Proxy.EIP7702 alias Explorer.Chain.SmartContract.Proxy.Models.Implementation - alias Explorer.Chain.{Address, Data, Hash} + alias Explorer.Chain.{Address, Data, Hash, InternalTransaction, Transaction} alias Explorer.{Chain, PagingOptions, Repo} @optional_attrs ~w(contract_code fetched_coin_balance fetched_coin_balance_block_number nonce decompiled verified gas_used transactions_count token_transfers_count)a @@ -225,8 +225,8 @@ defmodule Explorer.Chain.Address do |> unique_constraint(:hash) end - @spec get(Hash.Address.t(), [Chain.necessity_by_association_option() | Chain.api?()]) :: t() | nil - def get(hash, options) do + @spec get(Hash.Address.t() | binary(), [Chain.necessity_by_association_option() | Chain.api?()]) :: t() | nil + def get(hash, options \\ []) do necessity_by_association = Keyword.get(options, :necessity_by_association, %{}) query = from(address in Address, where: address.hash == ^hash) @@ -611,4 +611,26 @@ defmodule Explorer.Chain.Address do {[], nil} end end + + @doc """ + Retrieves the creation transaction for a given address. + + ## Parameters + - `address`: The address for which to find the creation transaction. + + ## Returns + - `nil` if no creation transaction is found. + - `%InternalTransaction{}` if the creation transaction is an internal transaction. + - `%Transaction{}` if the creation transaction is a regular transaction. + """ + @spec creation_transaction(any()) :: nil | InternalTransaction.t() | Transaction.t() + def creation_transaction(%__MODULE__{contracts_creation_internal_transaction: %InternalTransaction{}} = address) do + address.contracts_creation_internal_transaction + end + + def creation_transaction(%__MODULE__{contracts_creation_transaction: %Transaction{}} = address) do + address.contracts_creation_transaction + end + + def creation_transaction(_address), do: nil end