feat: Add missing filecoin robust addresses (#10935)

* feat: Add missing filecoin robust addresses

* Narrow down spec

* Refactoring
pull/10985/head
nikitosing 1 month ago committed by GitHub
parent 1b2232a88c
commit f8b4cb794a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 23
      apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex
  2. 36
      apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex
  3. 136
      apps/block_scout_web/lib/block_scout_web/views/api/v2/filecoin_view.ex
  4. 20
      apps/block_scout_web/lib/block_scout_web/views/api/v2/search_view.ex
  5. 20
      apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex
  6. 14
      apps/block_scout_web/lib/block_scout_web/views/api/v2/token_view.ex
  7. 28
      apps/explorer/lib/explorer/chain/address.ex

@ -83,12 +83,23 @@ defmodule BlockScoutWeb.API.V2.AddressController do
api?: true api?: true
] ]
@contract_address_preloads [ case Application.compile_env(:explorer, :chain_type) do
:smart_contract, :filecoin ->
:contracts_creation_internal_transaction, @contract_address_preloads [
:contracts_creation_transaction, :smart_contract,
:proxy_implementations [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 [ @nft_necessity_by_association [
necessity_by_association: %{ necessity_by_association: %{

@ -91,7 +91,8 @@ defmodule BlockScoutWeb.API.V2.AddressView do
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
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) creation_tx = creator_hash && AddressView.transaction_hash(address)
token = address.token && TokenView.render("token.json", %{token: address.token}) 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) "has_beacon_chain_withdrawals" => Counters.check_if_withdrawals_at_address(address.hash, @api_true)
}) })
if Enum.empty?(implementations) do result =
extended_info if Enum.empty?(implementations) do
else extended_info
Map.merge(extended_info, %{ else
"proxy_type" => proxy_type, Map.merge(extended_info, %{
"implementations" => implementations "proxy_type" => proxy_type,
}) "implementations" => implementations
end })
end
result
|> chain_type_fields(%{address: creation_transaction && creation_transaction.from_address, field_prefix: "creator"})
end end
@spec prepare_token_balance(Chain.Address.TokenBalance.t(), boolean()) :: map() @spec prepare_token_balance(Chain.Address.TokenBalance.t(), boolean()) :: map()
@ -238,4 +243,17 @@ defmodule BlockScoutWeb.API.V2.AddressView do
token: token token: token
}) })
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 end

@ -1,35 +1,105 @@
defmodule BlockScoutWeb.API.V2.FilecoinView do if Application.compile_env(:explorer, :chain_type) == :filecoin do
@moduledoc """ defmodule BlockScoutWeb.API.V2.FilecoinView do
View functions for rendering Filecoin-related data in JSON format. @moduledoc """
""" View functions for rendering Filecoin-related data in JSON format.
"""
alias Explorer.Chain.Address
alias Explorer.Chain
@doc """ alias Explorer.Chain.Address
Extends the json output with a sub-map containing information related to
Filecoin native addressing. @api_true [api?: true]
"""
@spec extend_address_json_response(map(), Address.t()) :: map() @doc """
def extend_address_json_response(result, %Address{} = address) do Extends the json output with a sub-map containing information related to
filecoin_id = Map.get(address, :filecoin_id) Filecoin native addressing.
filecoin_robust = Map.get(address, :filecoin_robust) """
filecoin_actor_type = Map.get(address, :filecoin_actor_type) @spec extend_address_json_response(map(), Address.t()) :: map()
def extend_address_json_response(
is_fetched = result,
Enum.all?( %Address{filecoin_id: filecoin_id, filecoin_robust: filecoin_robust, filecoin_actor_type: filecoin_actor_type}
[ ) do
filecoin_id, Map.put(result, :filecoin, %{
filecoin_robust, id: filecoin_id,
filecoin_actor_type robust: filecoin_robust,
], actor_type: filecoin_actor_type
&(not is_nil(&1)) })
) end
Map.put(result, :filecoin, %{ @spec preload_and_put_filecoin_robust_address(map(), %{
is_fetched: is_fetched, address_hash: String.t() | nil,
id: filecoin_id, field_prefix: String.t() | nil
robust: filecoin_robust, }) ::
actor_type: filecoin_actor_type 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
end end

@ -6,11 +6,14 @@ defmodule BlockScoutWeb.API.V2.SearchView do
alias Explorer.Chain.{Address, Beacon.Blob, Block, Hash, Transaction, UserOperation} 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 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 end
def render("search_results.json", %{search_results: search_results}) do 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 end
def render("search_results.json", %{result: {:ok, result}}) do 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 defp redirect_search_results(%Blob{} = item) do
%{"type" => "blob", "parameter" => to_string(item.hash)} %{"type" => "blob", "parameter" => to_string(item.hash)}
end 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 end

@ -184,10 +184,10 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do
target_contract = target_contract =
if smart_contract_verified, do: smart_contract, else: bytecode_twin_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" => "verified_twin_address_hash" => verified_twin_address_hash,
bytecode_twin_contract &&
Address.checksum(bytecode_twin_contract.address_hash),
"is_verified" => smart_contract_verified, "is_verified" => smart_contract_verified,
"is_changed_bytecode" => smart_contract_verified && smart_contract.is_changed_bytecode, "is_changed_bytecode" => smart_contract_verified && smart_contract.is_changed_bytecode,
"is_partially_verified" => smart_contract.partially_verified && smart_contract_verified, "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)) |> Map.merge(bytecode_info(address))
|> add_zksync_info(target_contract) |> add_zksync_info(target_contract)
|> chain_type_fields(%{address_hash: verified_twin_address_hash, field_prefix: "verified_twin"})
end end
def prepare_smart_contract(address, implementations, proxy_type, conn) do 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 def render_json(value, _type) do
to_string(value) to_string(value)
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.preload_and_put_filecoin_robust_address(result, params)
end
_ ->
defp chain_type_fields(result, _address) do
result
end
end
end end

@ -42,6 +42,7 @@ defmodule BlockScoutWeb.API.V2.TokenView do
"circulating_market_cap" => token.circulating_market_cap "circulating_market_cap" => token.circulating_market_cap
} }
|> maybe_append_bridged_info(token) |> maybe_append_bridged_info(token)
|> chain_type_fields(%{address: token.contract_address, field_prefix: nil})
end end
def render("token_balances.json", %{ def render("token_balances.json", %{
@ -139,4 +140,17 @@ defmodule BlockScoutWeb.API.V2.TokenView do
map map
end end
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 end

@ -134,7 +134,7 @@ defmodule Explorer.Chain.Address do
alias Explorer.Chain.SmartContract.Proxy alias Explorer.Chain.SmartContract.Proxy
alias Explorer.Chain.SmartContract.Proxy.EIP7702 alias Explorer.Chain.SmartContract.Proxy.EIP7702
alias Explorer.Chain.SmartContract.Proxy.Models.Implementation 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} 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 @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) |> unique_constraint(:hash)
end end
@spec get(Hash.Address.t(), [Chain.necessity_by_association_option() | Chain.api?()]) :: t() | nil @spec get(Hash.Address.t() | binary(), [Chain.necessity_by_association_option() | Chain.api?()]) :: t() | nil
def get(hash, options) do def get(hash, options \\ []) do
necessity_by_association = Keyword.get(options, :necessity_by_association, %{}) necessity_by_association = Keyword.get(options, :necessity_by_association, %{})
query = from(address in Address, where: address.hash == ^hash) query = from(address in Address, where: address.hash == ^hash)
@ -611,4 +611,26 @@ defmodule Explorer.Chain.Address do
{[], nil} {[], nil}
end end
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 end

Loading…
Cancel
Save