diff --git a/apps/block_scout_web/lib/block_scout_web.ex b/apps/block_scout_web/lib/block_scout_web.ex index 1ce9cca0e7..f914ed426a 100644 --- a/apps/block_scout_web/lib/block_scout_web.ex +++ b/apps/block_scout_web/lib/block_scout_web.ex @@ -29,6 +29,7 @@ defmodule BlockScoutWeb do import BlockScoutWeb.ErrorHelper import BlockScoutWeb.Routers.AccountRouter.Helpers, except: [static_path: 2] import Plug.Conn + import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] alias BlockScoutWeb.Routers.AdminRouter.Helpers, as: AdminRoutes end @@ -63,6 +64,8 @@ defmodule BlockScoutWeb do import Explorer.Chain.CurrencyHelper, only: [divide_decimals: 2] import BlockScoutWeb.Routers.WebRouter.Helpers, except: [static_path: 2] + + import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] end end @@ -80,6 +83,8 @@ defmodule BlockScoutWeb do use Phoenix.Channel use Gettext, backend: BlockScoutWeb.Gettext + + import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] end end diff --git a/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex b/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex index ebc45d00c0..86dec8a3f9 100644 --- a/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex +++ b/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex @@ -49,18 +49,18 @@ defmodule BlockScoutWeb.AddressChannel do end @transaction_associations [ - from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], + from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()], to_address: [ :scam_badge, :names, :smart_contract, - :proxy_implementations + proxy_implementations_association() ], created_contract_address: [ :scam_badge, :names, :smart_contract, - :proxy_implementations + proxy_implementations_association() ] ] ++ @chain_type_transaction_associations @@ -414,8 +414,8 @@ defmodule BlockScoutWeb.AddressChannel do token_transfers |> Repo.preload([ [ - from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], - to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations] + from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()], + to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()] ] ]), conn: nil diff --git a/apps/block_scout_web/lib/block_scout_web/channels/block_channel.ex b/apps/block_scout_web/lib/block_scout_web/channels/block_channel.ex index 2340ab3812..ffefcaf76d 100644 --- a/apps/block_scout_web/lib/block_scout_web/channels/block_channel.ex +++ b/apps/block_scout_web/lib/block_scout_web/channels/block_channel.ex @@ -27,7 +27,7 @@ defmodule BlockScoutWeb.BlockChannel do ) do rendered_block = BlockViewAPI.render("block.json", %{ - block: block |> Repo.preload(miner: [:names, :smart_contract, :proxy_implementations]), + block: block |> Repo.preload(miner: [:names, :smart_contract, proxy_implementations_association()]), socket: nil }) 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 b2a2319cd0..bc09024c98 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 @@ -52,9 +52,10 @@ defmodule BlockScoutWeb.API.V2.AddressController do @transaction_necessity_by_association [ necessity_by_association: %{ - [created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, + [created_contract_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => + :optional, + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, :block => :optional } |> Map.merge(@chain_type_transaction_necessity_by_association), @@ -63,8 +64,8 @@ defmodule BlockScoutWeb.API.V2.AddressController do @token_transfer_necessity_by_association [ necessity_by_association: %{ - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, :block => :optional, :transaction => :optional, :token => :optional @@ -77,7 +78,6 @@ defmodule BlockScoutWeb.API.V2.AddressController do :names => :optional, :scam_badge => :optional, :token => :optional, - :proxy_implementations => :optional, :signed_authorization => :optional }, api?: true @@ -88,16 +88,14 @@ defmodule BlockScoutWeb.API.V2.AddressController do @contract_address_preloads [ :smart_contract, [contracts_creation_internal_transaction: :from_address], - [contracts_creation_transaction: :from_address], - :proxy_implementations + [contracts_creation_transaction: :from_address] ] _ -> @contract_address_preloads [ :smart_contract, :contracts_creation_internal_transaction, - :contracts_creation_transaction, - :proxy_implementations + :contracts_creation_transaction ] end @@ -115,14 +113,14 @@ defmodule BlockScoutWeb.API.V2.AddressController do account_address: [ :names, :smart_contract, - :proxy_implementations + proxy_implementations_association() ] ] => :optional, [ associated_account_address: [ :names, :smart_contract, - :proxy_implementations + proxy_implementations_association() ] ] => :optional }, @@ -136,8 +134,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do fully_preloaded_address = Address.maybe_preload_smart_contract_associations(address, @contract_address_preloads, @api_true) - {implementations, proxy_type} = - SmartContractHelper.pre_fetch_implementations(fully_preloaded_address) + implementations = SmartContractHelper.pre_fetch_implementations(fully_preloaded_address) CoinBalanceOnDemand.trigger_fetch(address) ContractCodeOnDemand.trigger_fetch(address) @@ -145,9 +142,8 @@ defmodule BlockScoutWeb.API.V2.AddressController do conn |> put_status(200) |> render(:address, %{ - address: fully_preloaded_address |> maybe_preload_ens_to_address(), - implementations: implementations, - proxy_type: proxy_type + address: + %Address{fully_preloaded_address | proxy_implementations: implementations} |> maybe_preload_ens_to_address() }) end end @@ -225,8 +221,8 @@ defmodule BlockScoutWeb.API.V2.AddressController do options = [ necessity_by_association: %{ - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, :block => :optional, :token => :optional, :transaction => :optional @@ -297,9 +293,10 @@ defmodule BlockScoutWeb.API.V2.AddressController do full_options = [ necessity_by_association: %{ - [created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional + [created_contract_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => + :optional, + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional } ] |> Keyword.merge(paging_options(params)) @@ -333,7 +330,9 @@ defmodule BlockScoutWeb.API.V2.AddressController do |> paging_options() |> Keyword.merge(topic: formatted_topic) |> Keyword.merge( - necessity_by_association: %{[address: [:names, :smart_contract, :proxy_implementations]] => :optional} + necessity_by_association: %{ + [address: [:names, :smart_contract, proxy_implementations_association()]] => :optional + } ) |> Keyword.merge(@api_true) @@ -378,7 +377,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do full_options = [ necessity_by_association: %{ - [miner: :proxy_implementations] => :optional, + [miner: proxy_implementations_association()] => :optional, miner: :required, nephews: :optional, transactions: :optional, diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex index 28eeeea316..ba6bcd1e3f 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex @@ -84,9 +84,10 @@ defmodule BlockScoutWeb.API.V2.BlockController do @transaction_necessity_by_association [ necessity_by_association: %{ - [created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, + [created_contract_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => + :optional, + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, :block => :optional } |> Map.merge(@chain_type_transaction_necessity_by_association) @@ -94,9 +95,10 @@ defmodule BlockScoutWeb.API.V2.BlockController do @internal_transaction_necessity_by_association [ necessity_by_association: %{ - [created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional + [created_contract_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => + :optional, + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional } ] @@ -105,7 +107,7 @@ defmodule BlockScoutWeb.API.V2.BlockController do @block_params [ necessity_by_association: %{ - [miner: [:names, :smart_contract, :proxy_implementations]] => :optional, + [miner: [:names, :smart_contract, proxy_implementations_association()]] => :optional, :uncles => :optional, :nephews => :optional, :rewards => :optional, @@ -334,7 +336,9 @@ defmodule BlockScoutWeb.API.V2.BlockController do with {:ok, block} <- block_param_to_block(block_hash_or_number) do full_options = [ - necessity_by_association: %{[address: [:names, :smart_contract, :proxy_implementations]] => :optional}, + necessity_by_association: %{ + [address: [:names, :smart_contract, proxy_implementations_association()]] => :optional + }, api?: true ] |> Keyword.merge(paging_options(params)) @@ -415,7 +419,7 @@ defmodule BlockScoutWeb.API.V2.BlockController do with {:ok, reward_type_atom} <- celo_reward_type_to_atom(reward_type), {:ok, block} <- block_param_to_block(block_hash_or_number) do - address_associations = [:names, :smart_contract, :proxy_implementations] + address_associations = [:names, :smart_contract, proxy_implementations_association()] full_options = [ diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/main_page_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/main_page_controller.ex index cb2c41f001..f82b4f476e 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/main_page_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/main_page_controller.ex @@ -1,5 +1,5 @@ defmodule BlockScoutWeb.API.V2.MainPageController do - use Phoenix.Controller + use BlockScoutWeb, :controller alias Explorer.{Chain, PagingOptions} alias BlockScoutWeb.API.V2.{BlockView, OptimismView, TransactionView} @@ -24,9 +24,10 @@ defmodule BlockScoutWeb.API.V2.MainPageController do necessity_by_association: %{ :block => :required, - [created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional + [created_contract_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => + :optional, + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional } |> Map.merge(@chain_type_transaction_necessity_by_association), paging_options: %PagingOptions{page_size: 6}, @@ -39,7 +40,11 @@ defmodule BlockScoutWeb.API.V2.MainPageController do blocks = [paging_options: %PagingOptions{page_size: 4}, api?: true] |> Chain.list_blocks() - |> Repo.replica().preload([[miner: [:names, :smart_contract, :proxy_implementations]], :transactions, :rewards]) + |> Repo.replica().preload([ + [miner: [:names, :smart_contract, proxy_implementations_association()]], + :transactions, + :rewards + ]) conn |> put_status(200) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/mud_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/mud_controller.ex index 9f4b2f440d..bff0df4e29 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/mud_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/mud_controller.ex @@ -37,7 +37,7 @@ defmodule BlockScoutWeb.API.V2.MudController do necessity_by_association: %{ :names => :optional, :smart_contract => :optional, - :proxy_implementations => :optional + proxy_implementations_association() => :optional }, api?: true ) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/account_abstraction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/account_abstraction_controller.ex index 9571069769..ce77a28fd9 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/account_abstraction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/account_abstraction_controller.ex @@ -159,7 +159,7 @@ defmodule BlockScoutWeb.API.V2.Proxy.AccountAbstractionController do necessity_by_association: %{ :names => :optional, :smart_contract => :optional, - :proxy_implementations => :optional + proxy_implementations_association() => :optional }, api?: true ) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/smart_contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/smart_contract_controller.ex index 10e176e60d..3eea72553c 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/smart_contract_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/smart_contract_controller.ex @@ -23,8 +23,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do necessity_by_association: %{ :contracts_creation_internal_transaction => :optional, [smart_contract: :smart_contract_additional_sources] => :optional, - :contracts_creation_transaction => :optional, - :proxy_implementations => :optional + :contracts_creation_transaction => :optional }, api?: true ] @@ -39,12 +38,11 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do _ <- PublishHelper.sourcify_check(address_hash_string), {:not_found, {:ok, address}} <- {:not_found, Chain.find_contract_address(address_hash, @smart_contract_address_options, false)} do - {implementations, proxy_type} = - SmartContractHelper.pre_fetch_implementations(address) + implementations = SmartContractHelper.pre_fetch_implementations(address) conn |> put_status(200) - |> render(:smart_contract, %{address: address, implementations: implementations, proxy_type: proxy_type}) + |> render(:smart_contract, %{address: %Address{address | proxy_implementations: implementations}}) end end @@ -105,11 +103,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do {:not_found, {:ok, address}} <- {:not_found, Chain.find_contract_address(address_hash, @smart_contract_address_options)}, {:not_found, false} <- {:not_found, is_nil(address.smart_contract)} do - implementation_address_hash_strings = - address.smart_contract - |> Implementation.get_implementation(@api_true) - |> Tuple.to_list() - |> List.first() + implementation_address_hash_strings = get_implementations_address_hashes(address) functions = implementation_address_hash_strings @@ -136,11 +130,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do {:not_found, {:ok, address}} <- {:not_found, Chain.find_contract_address(address_hash, @smart_contract_address_options)}, {:not_found, false} <- {:not_found, is_nil(address.smart_contract)} do - implementation_address_hash_strings = - address.smart_contract - |> Implementation.get_implementation(@api_true) - |> Tuple.to_list() - |> List.first() + implementation_address_hash_strings = get_implementations_address_hashes(address) functions = implementation_address_hash_strings @@ -335,4 +325,12 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do {:ok, address_hash, smart_contract} end end + + defp get_implementations_address_hashes(proxy_address) do + implementation = + proxy_address.smart_contract + |> Implementation.get_implementation(@api_true) + + (implementation && implementation.address_hashes) || [] + end end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex index 506db2ab9f..2e72170b14 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex @@ -365,7 +365,7 @@ defmodule BlockScoutWeb.API.V2.TokenController do case Chain.nft_instance_from_token_id_and_token_address(token_id, address_hash, @api_true) do {:ok, token_instance} -> token_instance - |> Chain.select_repo(@api_true).preload(owner: [:names, :smart_contract, :proxy_implementations]) + |> Chain.select_repo(@api_true).preload(owner: [:names, :smart_contract, proxy_implementations_association()]) |> Chain.put_owner_to_token_instance(token, @api_true) {:error, :not_found} -> diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex index 1b92aa8514..4c648221ff 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex @@ -75,38 +75,45 @@ defmodule BlockScoutWeb.API.V2.TransactionController do :names, :token, :smart_contract, - :proxy_implementations + proxy_implementations_association() + ] + ] => :optional, + [ + from_address: [ + :scam_badge, + :names, + :smart_contract, + proxy_implementations_association() ] ] => :optional, - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => - :optional, [ to_address: [ :scam_badge, :names, :smart_contract, - :proxy_implementations + proxy_implementations_association() ] ] => :optional } |> Map.merge(@chain_type_transaction_necessity_by_association) @token_transfers_necessity_by_association %{ - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional } @token_transfers_in_transaction_necessity_by_association %{ - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, token: :required } @internal_transaction_necessity_by_association [ necessity_by_association: %{ - [created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional + [created_contract_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => + :optional, + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional } ] @@ -495,7 +502,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do full_options = [ necessity_by_association: %{ - [address: [:names, :smart_contract, :proxy_implementations]] => :optional + [address: [:names, :smart_contract, proxy_implementations_association()]] => :optional } ] |> Keyword.merge(paging_options(params)) @@ -573,8 +580,8 @@ defmodule BlockScoutWeb.API.V2.TransactionController do options = [ necessity_by_association: %{ - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional } ] |> Keyword.merge(@api_true) @@ -600,8 +607,8 @@ defmodule BlockScoutWeb.API.V2.TransactionController do options = [ necessity_by_association: %{ - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional } ] |> Keyword.merge(@api_true) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/validator_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/validator_controller.ex index 78a59a0ddf..d4946f6e02 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/validator_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/validator_controller.ex @@ -30,7 +30,7 @@ defmodule BlockScoutWeb.API.V2.ValidatorController do options = [ necessity_by_association: %{ - [address: [:names, :smart_contract, :proxy_implementations]] => :optional + [address: [:names, :smart_contract, proxy_implementations_association()]] => :optional } ] |> Keyword.merge(@api_true) @@ -81,7 +81,7 @@ defmodule BlockScoutWeb.API.V2.ValidatorController do options = [ necessity_by_association: %{ - [address: [:names, :smart_contract, :proxy_implementations]] => :optional + [address: [:names, :smart_contract, proxy_implementations_association()]] => :optional } ] |> Keyword.merge(@api_true) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/withdrawal_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/withdrawal_controller.ex index a58a84ed15..a8df1ef7d5 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/withdrawal_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/withdrawal_controller.ex @@ -14,7 +14,7 @@ defmodule BlockScoutWeb.API.V2.WithdrawalController do full_options = [ necessity_by_association: %{ - [address: [:names, :smart_contract, :proxy_implementations]] => :optional, + [address: [:names, :smart_contract, proxy_implementations_association()]] => :optional, block: :optional }, api?: true diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex index ee2d59ae8f..7b360faecf 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex @@ -25,16 +25,7 @@ defmodule BlockScoutWeb.SmartContractController do {:custom_abi, false} <- {:custom_abi, is_custom_abi}, {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), {:ok, address} <- Chain.find_contract_address(address_hash, address_options, true) do - implementation_address_hash_string = - if contract_type == "proxy" do - address.smart_contract - |> Implementation.get_implementation() - |> Tuple.to_list() - |> List.first() - |> List.first() || burn_address_hash_string() - else - burn_address_hash_string() - end + implementation_address_hash_string = implementation_address_hash(contract_type, address) functions = if action == "write" do @@ -107,6 +98,15 @@ defmodule BlockScoutWeb.SmartContractController do def index(conn, _), do: not_found(conn) + defp implementation_address_hash(contract_type, address) do + if contract_type == "proxy" do + implementation = Implementation.get_implementation(address.smart_contract) + (implementation && implementation.address_hashes |> List.first()) || burn_address_hash_string() + else + burn_address_hash_string() + end + end + defp custom_abi_render(conn, %{"hash" => address_hash_string, "type" => contract_type, "action" => action} = params) do with custom_abi <- AddressView.fetch_custom_abi(conn, address_hash_string), false <- is_nil(custom_abi), diff --git a/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex b/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex index 17d732bc64..d2af052ea9 100644 --- a/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex +++ b/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex @@ -9,9 +9,9 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do alias Explorer.Chain.{Data, InternalTransaction, Log, TokenTransfer, Transaction} alias HTTPoison.Response + import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] import Explorer.MicroserviceInterfaces.Metadata, only: [maybe_preload_metadata: 1] import Explorer.Utility.Microservice, only: [base_url: 2, check_enabled: 2] - require Logger @post_timeout :timer.minutes(5) @@ -20,9 +20,10 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do @items_limit 50 @internal_transaction_necessity_by_association [ necessity_by_association: %{ - [created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional + [created_contract_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => + :optional, + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional } ] @@ -176,8 +177,8 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do full_options = [ necessity_by_association: %{ - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional } ] |> Keyword.merge(@api_true) @@ -212,7 +213,7 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do full_options = [ necessity_by_association: %{ - [address: [:names, :smart_contract, :proxy_implementations]] => :optional + [address: [:names, :smart_contract, proxy_implementations_association()]] => :optional } ] |> Keyword.merge(@api_true) @@ -234,7 +235,7 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do log_options = [ necessity_by_association: %{ - [address: [:names, :smart_contract, :proxy_implementations]] => :optional + [address: [:names, :smart_contract, proxy_implementations_association()]] => :optional }, limit: @items_limit ] @@ -254,8 +255,8 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do token_transfer_options = [ necessity_by_association: %{ - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, :token => :optional } ] @@ -303,7 +304,7 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do necessity_by_association: %{ :names => :optional, :smart_contract => :optional, - :proxy_implementations => :optional + proxy_implementations_association() => :optional }, api?: true ], diff --git a/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex b/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex index 4f1a6a2804..2119853443 100644 --- a/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex @@ -5,6 +5,7 @@ defmodule BlockScoutWeb.Models.TransactionStateHelper do import Explorer.PagingOptions, only: [default_paging_options: 0] import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] + import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] alias Explorer.Chain.Transaction.StateChange alias Explorer.{Chain, PagingOptions, Repo} @@ -69,16 +70,16 @@ defmodule BlockScoutWeb.Models.TransactionStateHelper do |> Enum.find(&(&1.hash == transaction.hash)) |> Repo.preload( token_transfers: [ - from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], - to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations] + from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()], + to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()] ], internal_transactions: [ - from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], - to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations] + from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()], + to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()] ], - block: [miner: [:names, :smart_contract, :proxy_implementations]], - from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], - to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations] + block: [miner: [:names, :smart_contract, proxy_implementations_association()]], + from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()], + to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()] ) previous_block_number = BlockNumberHelper.previous_block_number(transaction.block_number) diff --git a/apps/block_scout_web/lib/block_scout_web/notifier.ex b/apps/block_scout_web/lib/block_scout_web/notifier.ex index 8c353b68ef..dead3bb875 100644 --- a/apps/block_scout_web/lib/block_scout_web/notifier.ex +++ b/apps/block_scout_web/lib/block_scout_web/notifier.ex @@ -27,6 +27,8 @@ defmodule BlockScoutWeb.Notifier do alias Explorer.SmartContract.{CompilerVersion, Solidity.CodeCompiler} alias Phoenix.View + import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] + @check_broadcast_sequence_period 500 case Application.compile_env(:explorer, :chain_type) do @@ -181,8 +183,18 @@ defmodule BlockScoutWeb.Notifier do DenormalizationHelper.extend_transaction_preload([ :token, :transaction, - from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], - to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations] + from_address: [ + :scam_badge, + :names, + :smart_contract, + proxy_implementations_association() + ], + to_address: [ + :scam_badge, + :names, + :smart_contract, + proxy_implementations_association() + ] ]) ) @@ -205,9 +217,9 @@ defmodule BlockScoutWeb.Notifier do def handle_event({:chain_event, :transactions, :realtime, transactions}) do base_preloads = [ :block, - created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], - from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], - to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations] + created_contract_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()], + from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()], + to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()] ] preloads = if API_V2.enabled?(), do: [:token_transfers | base_preloads], else: base_preloads diff --git a/apps/block_scout_web/lib/block_scout_web/paging_helper.ex b/apps/block_scout_web/lib/block_scout_web/paging_helper.ex index 483e4150f1..8360aadc5e 100644 --- a/apps/block_scout_web/lib/block_scout_web/paging_helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/paging_helper.ex @@ -3,6 +3,8 @@ defmodule BlockScoutWeb.PagingHelper do Helper for fetching filters and other url query parameters """ import Explorer.Chain, only: [string_to_transaction_hash: 1] + import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] + alias Explorer.Chain.Stability.Validator, as: ValidatorStability alias Explorer.Chain.Transaction alias Explorer.{Helper, PagingOptions, SortingHelper} @@ -158,7 +160,7 @@ defmodule BlockScoutWeb.PagingHelper do [ necessity_by_association: %{ :transactions => :optional, - [miner: [:names, :smart_contract, :proxy_implementations]] => :optional, + [miner: [:names, :smart_contract, proxy_implementations_association()]] => :optional, :nephews => :required, :rewards => :optional }, @@ -169,7 +171,7 @@ defmodule BlockScoutWeb.PagingHelper do [ necessity_by_association: %{ :transactions => :optional, - [miner: [:names, :smart_contract, :proxy_implementations]] => :optional, + [miner: [:names, :smart_contract, proxy_implementations_association()]] => :optional, :rewards => :optional }, block_type: "Reorg" @@ -184,7 +186,7 @@ defmodule BlockScoutWeb.PagingHelper do do: [ necessity_by_association: %{ :transactions => :optional, - [miner: [:names, :smart_contract, :proxy_implementations]] => :optional, + [miner: [:names, :smart_contract, proxy_implementations_association()]] => :optional, :rewards => :optional }, block_type: "Block" diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/overview.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/overview.html.eex index 0e4e200001..b71337de6c 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address/overview.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/overview.html.eex @@ -124,9 +124,9 @@ <% end %> <%= if @is_proxy do %> - <% {implementation_addresses, implementation_names, _proxy_type} = Implementation.get_implementation(@address.smart_contract) %> - <% implementation_address_ = Enum.at(implementation_addresses, 0) %> - <% name = Enum.at(implementation_names, 0) %> + <% implementation = Implementation.get_implementation(@address.smart_contract) %> + <% implementation_address_ = implementation && Enum.at(implementation.address_hashes, 0) %> + <% name = implementation && Enum.at(implementation.names, 0) %> <% implementation_address = implementation_address_ || "0x0000000000000000000000000000000000000000" %>
diff --git a/apps/block_scout_web/lib/block_scout_web/views/account/api/v2/user_view.ex b/apps/block_scout_web/lib/block_scout_web/views/account/api/v2/user_view.ex index f9b6a6a307..b5e9834d7c 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/account/api/v2/user_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/account/api/v2/user_view.ex @@ -1,4 +1,6 @@ defmodule BlockScoutWeb.Account.API.V2.UserView do + use BlockScoutWeb, :view + alias BlockScoutWeb.Account.API.V2.AccountView alias BlockScoutWeb.API.V2.Helper alias Ecto.Changeset @@ -198,7 +200,9 @@ defmodule BlockScoutWeb.Account.API.V2.UserView do defp get_address(address_hash) do case Chain.hash_to_address( address_hash, - [necessity_by_association: %{smart_contract: :optional, proxy_implementations: :optional}], + [ + necessity_by_association: %{:smart_contract => :optional, proxy_implementations_association() => :optional} + ], false ) do {:ok, address} -> address 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 90611f3b5e..11ae926433 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 @@ -16,8 +16,8 @@ defmodule BlockScoutWeb.API.V2.AddressView do ApiView.render("message.json", assigns) end - def render("address.json", %{address: address, implementations: implementations, proxy_type: proxy_type, conn: conn}) do - prepare_address(address, conn, implementations, proxy_type) + def render("address.json", %{address: address, conn: conn}) do + prepare_address(address, conn) end def render("token_balances.json", %{token_balances: token_balances}) do @@ -90,8 +90,8 @@ defmodule BlockScoutWeb.API.V2.AddressView 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 + @spec prepare_address(Address.t(), Plug.Conn.t() | nil) :: map() + def prepare_address(address, conn \\ nil) do base_info = Helper.address_with_info(conn, address, address.hash, true) balance = address.fetched_coin_balance && address.fetched_coin_balance.value @@ -121,17 +121,7 @@ defmodule BlockScoutWeb.API.V2.AddressView do "has_beacon_chain_withdrawals" => Counters.check_if_withdrawals_at_address(address.hash, @api_true) }) - result = - if Enum.empty?(implementations) do - extended_info - else - Map.merge(extended_info, %{ - "proxy_type" => proxy_type, - "implementations" => implementations - }) - end - - result + extended_info |> chain_type_fields(%{address: creation_transaction && creation_transaction.from_address, field_prefix: "creator"}) end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/celo_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/celo_view.ex index b013d67009..2f17c7bd84 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/celo_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/celo_view.ex @@ -2,6 +2,7 @@ defmodule BlockScoutWeb.API.V2.CeloView do @moduledoc """ View functions for rendering Celo-related data in JSON format. """ + use BlockScoutWeb, :view require Logger @@ -20,7 +21,7 @@ defmodule BlockScoutWeb.API.V2.CeloView do necessity_by_association: %{ :names => :optional, :smart_contract => :optional, - :proxy_implementations => :optional + proxy_implementations_association() => :optional }, api?: true ] diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex index 935fdc087d..88b3e41f53 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex @@ -64,20 +64,16 @@ defmodule BlockScoutWeb.API.V2.Helper do def address_with_info(%Address{} = address, _address_hash) do smart_contract? = Address.smart_contract?(address) - {proxy_implementations, implementation_address_hashes, implementation_names, proxy_type} = + proxy_implementations = case address.proxy_implementations do %NotLoaded{} -> - {nil, [], [], nil} + nil nil -> - {nil, [], [], nil} + nil proxy_implementations -> - address_hashes = proxy_implementations.address_hashes - names = proxy_implementations.names - proxy_type = proxy_implementations.proxy_type - - {proxy_implementations, address_hashes, names, proxy_type} + proxy_implementations end %{ @@ -85,8 +81,8 @@ defmodule BlockScoutWeb.API.V2.Helper do "is_contract" => smart_contract?, "name" => address_name(address), "is_scam" => address_marked_as_scam?(address), - "proxy_type" => proxy_type, - "implementations" => Proxy.proxy_object_info(implementation_address_hashes, implementation_names), + "proxy_type" => proxy_implementations && proxy_implementations.proxy_type, + "implementations" => Proxy.proxy_object_info(proxy_implementations), "is_verified" => verified?(address) || verified_minimal_proxy?(proxy_implementations), "ens_domain_name" => address.ens_domain_name, "metadata" => address.metadata diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/optimism_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/optimism_view.ex index 62e43585aa..f0217f268c 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/optimism_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/optimism_view.ex @@ -232,7 +232,7 @@ defmodule BlockScoutWeb.API.V2.OptimismView do necessity_by_association: %{ :names => :optional, :smart_contract => :optional, - :proxy_implementations => :optional + proxy_implementations_association() => :optional }, api?: true ], diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/polygon_edge_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/polygon_edge_view.ex index 51b2515997..be28baa483 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/polygon_edge_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/polygon_edge_view.ex @@ -90,7 +90,7 @@ defmodule BlockScoutWeb.API.V2.PolygonEdgeView do necessity_by_association: %{ :names => :optional, :smart_contract => :optional, - :proxy_implementations => :optional + proxy_implementations_association() => :optional }, api?: true ], diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/shibarium_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/shibarium_view.ex index 45d46b7a72..de9101961f 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/shibarium_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/shibarium_view.ex @@ -62,7 +62,7 @@ defmodule BlockScoutWeb.API.V2.ShibariumView do necessity_by_association: %{ :names => :optional, :smart_contract => :optional, - :proxy_implementations => :optional + proxy_implementations_association() => :optional }, api?: true ) 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 f685fe2833..d895db108f 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 @@ -11,6 +11,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do alias Ecto.Changeset alias Explorer.Chain alias Explorer.Chain.{Address, SmartContract, SmartContractAdditionalSource} + alias Explorer.Chain.SmartContract.Proxy alias Explorer.SmartContract.Helper, as: SmartContractHelper alias Explorer.Visualize.Sol2uml @@ -26,13 +27,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do %{"items" => Enum.map(smart_contracts, &prepare_smart_contract_for_list/1), "next_page_params" => next_page_params} end - def render("smart_contract.json", %{ - address: address, - implementations: implementations, - proxy_type: proxy_type, - conn: conn - }) do - prepare_smart_contract(address, implementations, proxy_type, conn) + def render("smart_contract.json", %{address: address, conn: conn}) do + prepare_smart_contract(address, conn) end def render("read_functions.json", %{functions: functions}) do @@ -156,9 +152,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do # credo:disable-for-next-line def prepare_smart_contract( - %Address{smart_contract: %SmartContract{} = smart_contract} = address, - implementations, - proxy_type, + %Address{smart_contract: %SmartContract{} = smart_contract, proxy_implementations: implementations} = address, conn ) do bytecode_twin = SmartContract.get_address_verified_bytecode_twin_contract(address.hash, @api_true) @@ -202,8 +196,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do "has_methods_write" => write_methods?, "has_methods_read_proxy" => is_proxy, "has_methods_write_proxy" => is_proxy && write_methods?, - "proxy_type" => proxy_type, - "implementations" => implementations, + "proxy_type" => implementations && implementations.proxy_type, + "implementations" => Proxy.proxy_object_info(implementations), "sourcify_repo_url" => if(smart_contract.verified_via_sourcify && smart_contract_verified, do: AddressContractView.sourcify_repo_url(address.hash, smart_contract.partially_verified) @@ -240,15 +234,15 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do |> chain_type_fields(%{address_hash: verified_twin_address_hash, field_prefix: "verified_twin"}) end - def prepare_smart_contract(address, implementations, proxy_type, conn) do + def prepare_smart_contract(%Address{proxy_implementations: implementations} = address, conn) do 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) %{ "has_custom_methods_read" => read_custom_abi?, "has_custom_methods_write" => write_custom_abi?, - "proxy_type" => proxy_type, - "implementations" => implementations + "proxy_type" => implementations && implementations.proxy_type, + "implementations" => Proxy.proxy_object_info(implementations) } |> Map.merge(bytecode_info(address)) end diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 52b0b788a1..cda1bec0ee 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -85,6 +85,7 @@ defmodule Explorer.Chain do alias Explorer.Chain.Fetcher.{CheckBytecodeMatchingOnDemand, LookUpSmartContractSourcesOnDemand} alias Explorer.Chain.Import.Runner alias Explorer.Chain.InternalTransaction.{CallType, Type} + alias Explorer.Chain.SmartContract.Proxy.Models.Implementation alias Explorer.Market.MarketHistoryCache alias Explorer.{PagingOptions, Repo} @@ -391,7 +392,12 @@ defmodule Explorer.Chain do base else base - |> preload(transaction: [from_address: [:proxy_implementations], to_address: [:proxy_implementations]]) + |> preload( + transaction: [ + from_address: ^Implementation.proxy_implementations_association(), + to_address: ^Implementation.proxy_implementations_association() + ] + ) end preloaded_query @@ -1113,7 +1119,7 @@ defmodule Explorer.Chain do |> Keyword.get(:necessity_by_association, %{}) |> Map.merge(%{ [smart_contract: :smart_contract_additional_sources] => :optional, - :proxy_implementations => :optional + Implementation.proxy_implementations_association() => :optional }) query = @@ -4393,7 +4399,7 @@ defmodule Explorer.Chain do |> Instance.address_to_unique_token_instances() |> Instance.page_token_instance(paging_options) |> limit(^paging_options.page_size) - |> preload([_], owner: [:names, :smart_contract, :proxy_implementations]) + |> preload([_], owner: [:names, :smart_contract, ^Implementation.proxy_implementations_association()]) |> select_repo(options).all() |> Enum.map(&put_owner_to_token_instance(&1, token, options)) end @@ -4424,7 +4430,11 @@ defmodule Explorer.Chain do owner_address_hash, options |> Keyword.merge( - necessity_by_association: %{names: :optional, smart_contract: :optional, proxy_implementations: :optional} + necessity_by_association: %{ + :names => :optional, + :smart_contract => :optional, + Implementation.proxy_implementations_association() => :optional + } ) ) diff --git a/apps/explorer/lib/explorer/chain/address.ex b/apps/explorer/lib/explorer/chain/address.ex index 1c86455392..da1ea6eda5 100644 --- a/apps/explorer/lib/explorer/chain/address.ex +++ b/apps/explorer/lib/explorer/chain/address.ex @@ -138,12 +138,12 @@ defmodule Explorer.Chain.Address do alias Ecto.Changeset alias Explorer.Helper, as: ExplorerHelper alias Explorer.Chain.Cache.{Accounts, NetVersion} - alias Explorer.Chain.SmartContract.Proxy alias Explorer.Chain.SmartContract.Proxy.EIP7702 - alias Explorer.Chain.SmartContract.Proxy.Models.Implementation alias Explorer.Chain.{Address, Data, Hash, InternalTransaction, Transaction} alias Explorer.{Chain, PagingOptions, Repo} + import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] + @optional_attrs ~w(contract_code fetched_coin_balance fetched_coin_balance_block_number nonce decompiled verified gas_used transactions_count token_transfers_count)a @chain_type_optional_attrs (case Application.compile_env(:explorer, :chain_type) do :filecoin -> @@ -552,7 +552,7 @@ defmodule Explorer.Chain.Address do from(a in Address, where: a.fetched_coin_balance > ^0, order_by: [desc: a.fetched_coin_balance, asc: a.hash], - preload: [:names, :smart_contract, :proxy_implementations], + preload: [:names, :smart_contract, ^proxy_implementations_association()], select: {a, a.transactions_count} ) @@ -653,26 +653,6 @@ defmodule Explorer.Chain.Address do ) 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 - @doc """ Retrieves the creation transaction for a given address. diff --git a/apps/explorer/lib/explorer/chain/address/current_token_balance.ex b/apps/explorer/lib/explorer/chain/address/current_token_balance.ex index 32f01a388c..a40fc51b11 100644 --- a/apps/explorer/lib/explorer/chain/address/current_token_balance.ex +++ b/apps/explorer/lib/explorer/chain/address/current_token_balance.ex @@ -11,6 +11,7 @@ defmodule Explorer.Chain.Address.CurrentTokenBalance do import Ecto.Changeset import Ecto.Query, only: [from: 2, limit: 2, offset: 2, order_by: 3, preload: 2, dynamic: 2] import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] + import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] alias Explorer.{Chain, PagingOptions, Repo} alias Explorer.Chain.{Address, Block, CurrencyHelper, Hash, Token} @@ -85,7 +86,7 @@ defmodule Explorer.Chain.Address.CurrentTokenBalance do def token_holders_ordered_by_value(token_contract_address_hash, options \\ []) do token_contract_address_hash |> token_holders_ordered_by_value_query_without_address_preload(options) - |> preload(address: [:names, :smart_contract, :proxy_implementations]) + |> preload(address: [:names, :smart_contract, ^proxy_implementations_association()]) end @doc """ @@ -131,7 +132,7 @@ defmodule Explorer.Chain.Address.CurrentTokenBalance do _ -> token_contract_address_hash |> token_holders_by_token_id_query(token_id) - |> preload(address: [:names, :smart_contract, :proxy_implementations]) + |> preload(address: [:names, :smart_contract, ^proxy_implementations_association()]) |> order_by([tb], desc: :value, desc: :address_hash) |> Chain.page_token_balances(paging_options) |> limit(^paging_options.page_size) diff --git a/apps/explorer/lib/explorer/chain/advanced_filter.ex b/apps/explorer/lib/explorer/chain/advanced_filter.ex index 62cdc1657b..5924084c64 100644 --- a/apps/explorer/lib/explorer/chain/advanced_filter.ex +++ b/apps/explorer/lib/explorer/chain/advanced_filter.ex @@ -6,6 +6,7 @@ defmodule Explorer.Chain.AdvancedFilter do use Explorer.Schema import Ecto.Query + import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] alias Explorer.{Chain, Helper, PagingOptions} alias Explorer.Chain.{Address, Data, DenormalizationHelper, Hash, InternalTransaction, TokenTransfer, Transaction} @@ -115,9 +116,9 @@ defmodule Explorer.Chain.AdvancedFilter do |> Enum.sort(&sort_function/2) |> take_page_size(paging_options) |> Chain.select_repo(options).preload( - from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], - to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], - created_contract_address: [:names, :smart_contract, :proxy_implementations] + from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()], + to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()], + created_contract_address: [:names, :smart_contract, proxy_implementations_association()] ) end diff --git a/apps/explorer/lib/explorer/chain/celo/epoch_reward.ex b/apps/explorer/lib/explorer/chain/celo/epoch_reward.ex index f08d59f336..85ed60fb1f 100644 --- a/apps/explorer/lib/explorer/chain/celo/epoch_reward.ex +++ b/apps/explorer/lib/explorer/chain/celo/epoch_reward.ex @@ -8,6 +8,7 @@ defmodule Explorer.Chain.Celo.EpochReward do import Ecto.Query, only: [from: 2] import Explorer.Chain, only: [select_repo: 1] + import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] alias Explorer.Chain.Celo.EpochReward alias Explorer.Chain.{Block, Hash, TokenTransfer} @@ -101,8 +102,8 @@ defmodule Explorer.Chain.Celo.EpochReward do select: {tt.log_index, tt}, preload: [ :token, - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]], - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] + [from_address: [:scam_badge, :names, :smart_contract, ^proxy_implementations_association()]], + [to_address: [:scam_badge, :names, :smart_contract, ^proxy_implementations_association()]] ] ) diff --git a/apps/explorer/lib/explorer/chain/internal_transaction.ex b/apps/explorer/lib/explorer/chain/internal_transaction.ex index 5d6b452470..25c93324d5 100644 --- a/apps/explorer/lib/explorer/chain/internal_transaction.ex +++ b/apps/explorer/lib/explorer/chain/internal_transaction.ex @@ -8,6 +8,8 @@ defmodule Explorer.Chain.InternalTransaction do alias Explorer.Chain.DenormalizationHelper alias Explorer.Chain.InternalTransaction.{Action, CallType, Result, Type} + import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] + @typep paging_options :: {:paging_options, PagingOptions.t()} @typep api? :: {:api?, true | false} @@ -832,8 +834,8 @@ defmodule Explorer.Chain.InternalTransaction do preloads = DenormalizationHelper.extend_transaction_preload([ :block, - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]], - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]], + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] ]) __MODULE__ diff --git a/apps/explorer/lib/explorer/chain/smart_contract.ex b/apps/explorer/lib/explorer/chain/smart_contract.ex index 6cfe68473f..09f8d297bb 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract.ex @@ -644,28 +644,8 @@ defmodule Explorer.Chain.SmartContract do @spec compose_address_for_unverified_smart_contract(map(), any()) :: map() def compose_address_for_unverified_smart_contract(%{smart_contract: smart_contract} = address_result, options) when is_nil(smart_contract) do - smart_contract = %{ - updated: %__MODULE__{ - address_hash: address_result.hash - }, - implementation_updated_at: nil, - implementation_address_fetched?: false, - refetch_necessity_checked?: false - } - - {implementation_address_hash, _names, _proxy_type} = - Implementation.get_implementation( - smart_contract, - Keyword.put(options, :proxy_without_abi?, true) - ) - - implementation_smart_contract = - implementation_address_hash - |> Proxy.implementation_to_smart_contract(options) - address_verified_bytecode_twin_contract = - implementation_smart_contract || - get_address_verified_bytecode_twin_contract(address_result.hash, options).verified_contract + get_address_verified_bytecode_twin_contract(address_result.hash, options).verified_contract if address_verified_bytecode_twin_contract do add_bytecode_twin_info_to_contract(address_result, address_verified_bytecode_twin_contract, address_result.hash) @@ -677,10 +657,10 @@ defmodule Explorer.Chain.SmartContract do 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 - {implementation_address_hashes, _names, _proxy_type} = Implementation.get_implementation(proxy_hash, options) + implementation = Implementation.get_implementation(proxy_hash, options) - if implementation_address_hashes && Enum.count(implementation_address_hashes) == 1 do - implementation_address_hashes + if implementation && Enum.count(implementation.address_hashes) == 1 do + implementation.address_hashes |> Enum.at(0) |> Proxy.implementation_to_smart_contract(options) else @@ -1117,24 +1097,24 @@ defmodule Explorer.Chain.SmartContract do @doc """ Gets smart-contract ABI from the DB for the given address hash of smart-contract """ - @spec get_smart_contract_abi(String.t(), any()) :: any() - def get_smart_contract_abi(address_hash_string, options \\ []) - - def get_smart_contract_abi(address_hash_string, options) - when not is_nil(address_hash_string) do - with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), - {smart_contract, _} = - address_hash - |> address_hash_to_smart_contract_with_bytecode_twin(options, false), - false <- is_nil(smart_contract) do - smart_contract - |> Map.get(:abi) - else + @spec get_smart_contract_abi(String.t() | Hash.Address.t(), any()) :: any() + def get_smart_contract_abi(address_hash, options \\ []) + + def get_smart_contract_abi(address_hash_string, options) when is_binary(address_hash_string) do + case Chain.string_to_address_hash(address_hash_string) do + {:ok, address_hash} -> + get_smart_contract_abi(address_hash, options) + _ -> [] end end + def get_smart_contract_abi(%Hash{} = address_hash, options) do + {smart_contract, _} = address_hash_to_smart_contract_with_bytecode_twin(address_hash, options, false) + (smart_contract && smart_contract.abi) || [] + end + def get_smart_contract_abi(address_hash_string, _) when is_nil(address_hash_string) do [] end diff --git a/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex b/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex index 2412874ccb..8f67e8d8f1 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex @@ -93,21 +93,11 @@ defmodule Explorer.Chain.SmartContract.Proxy do 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), - false <- Enum.empty?(implementation_address_hash_strings) do - implementation_address_hash_strings - |> Enum.reduce_while(false, fn implementation_address_hash_string, acc -> - with {:ok, implementation_address_hash} <- string_to_address_hash(implementation_address_hash_string), - false <- implementation_address_hash.bytes == burn_address_hash.bytes do - {:halt, true} - else - _ -> - {:cont, acc} - end - end) + implementation = get_implementation(smart_contract, options) + + with false <- is_nil(implementation), + false <- Enum.empty?(implementation.address_hashes) do + has_not_burn_address_hash?(implementation.address_hashes, burn_address_hash) else _ -> false @@ -116,6 +106,14 @@ defmodule Explorer.Chain.SmartContract.Proxy do end end + @spec has_not_burn_address_hash?([Hash.Address.t()], Hash.Address.t()) :: boolean() + defp has_not_burn_address_hash?(address_hashes, burn_address_hash) do + address_hashes + |> Enum.reduce_while(false, fn implementation_address_hash, acc -> + if implementation_address_hash.bytes == burn_address_hash.bytes, do: {:cont, acc}, else: {:halt, true} + end) + end + @doc """ Decodes and formats an address output from a smart contract ABI. @@ -158,11 +156,12 @@ defmodule Explorer.Chain.SmartContract.Proxy do options ) when not is_nil(proxy_address_hash) and not is_nil(abi) do - {implementation_address_hash_strings, _names, _proxy_type} = get_implementation(smart_contract, options) + implementation = get_implementation(smart_contract, options) - implementation_address_hash_strings - |> Enum.reduce([], fn implementation_address_hash_string, acc -> - SmartContract.get_smart_contract_abi(implementation_address_hash_string) ++ acc + ((implementation && implementation.address_hashes) || + []) + |> Enum.reduce([], fn implementation_address_hash, acc -> + SmartContract.get_smart_contract_abi(implementation_address_hash) ++ acc end) end @@ -556,25 +555,54 @@ defmodule Explorer.Chain.SmartContract.Proxy do 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: [] + @spec proxy_object_info(Implementation.t() | nil) :: [map()] + def proxy_object_info(nil), do: [] + + def proxy_object_info(proxy_implementation) do + implementations_info = prepare_implementations(proxy_implementation) + implementation_addresses = proxy_implementation.address_hashes + implementation_names = proxy_implementation.names - 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] + [ + %{"address" => Address.checksum(address_hash), "name" => name} |> chain_type_fields(implementations_info) + | acc + ] _ -> with {:ok, address_hash} <- string_to_address_hash(address), checksummed_address <- Address.checksum(address_hash) do - [%{"address" => checksummed_address, "name" => name} | acc] + [%{"address" => checksummed_address, "name" => name} |> chain_type_fields(implementations_info) | acc] else _ -> acc end end end) end + + if Application.compile_env(:explorer, :chain_type) == :filecoin do + def chain_type_fields(%{"address" => address_hash} = address, implementations_info) do + Map.put(address, "filecoin_robust_address", implementations_info[address_hash]) + end + + def prepare_implementations(%Implementation{addresses: [_ | _] = addresses}) do + Enum.into(addresses, %{}, fn address -> {Address.checksum(address.hash), address.filecoin_robust} end) + end + + def prepare_implementations(_) do + %{} + end + else + def chain_type_fields(address, _proxy_implementations) do + address + end + + def prepare_implementations(_implementations_info) do + :ignore + end + end end diff --git a/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex b/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex index a60fb55887..41e82383b0 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex @@ -13,12 +13,10 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do select: 3 ] - import Explorer.Chain, only: [select_repo: 1, string_to_address_hash: 1] - + alias Explorer.{Chain, Repo} alias Explorer.Chain.{Address, Hash, SmartContract} alias Explorer.Chain.SmartContract.Proxy alias Explorer.Counters.AverageBlockTime - alias Explorer.Repo alias Timex.Duration @burn_address_hash_string "0x0000000000000000000000000000000000000000" @@ -58,6 +56,8 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do field(:address_hashes, {:array, Hash.Address}, null: false) field(:names, {:array, :string}, null: false) + has_many(:addresses, Address, foreign_key: :hash, references: :address_hashes) + belongs_to( :address, Address, @@ -88,7 +88,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do def get_proxy_implementations(proxy_address_hash, options \\ []) do proxy_address_hash |> get_proxy_implementations_query() - |> select_repo(options).one() + |> Chain.select_repo(options).one() end @doc """ @@ -102,7 +102,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do def get_proxy_implementations_for_multiple_proxies(proxy_address_hashes, options) do proxy_address_hashes |> get_proxy_implementations_by_multiple_hashes_query() - |> select_repo(options).all() + |> Chain.select_repo(options).all() end @doc """ @@ -113,7 +113,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do proxy_address_hash |> get_proxy_implementations_query() |> select([p], p.updated_at) - |> select_repo(options).one() + |> Chain.select_repo(options).one() end defp get_proxy_implementations_query(proxy_address_hash) do @@ -133,7 +133,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do @doc """ Returns implementation address, name and proxy type for the given SmartContract """ - @spec get_implementation(any(), any()) :: {any(), any(), atom() | nil} + @spec get_implementation(any(), any()) :: t() | nil def get_implementation(smart_contract, options \\ []) def get_implementation( @@ -226,35 +226,29 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do case Task.yield(get_implementation_address_hash_task, timeout) || Task.ignore(get_implementation_address_hash_task) do {:ok, :empty} -> - {[], [], nil} + nil {:ok, :error} -> - format_proxy_implementations_response(proxy_implementations) + proxy_implementations {:ok, %__MODULE__{} = result} -> - format_proxy_implementations_response(result) + result _ -> - format_proxy_implementations_response(proxy_implementations) + proxy_implementations end else - format_proxy_implementations_response(proxy_implementations) + proxy_implementations end end - def get_implementation(_, _), do: {[], [], nil} + def get_implementation(_, _), do: nil defp fetch_implementation?(implementation_address_fetched?, refetch_necessity_checked?, implementation_updated_at) do (!implementation_address_fetched? || !refetch_necessity_checked?) && check_implementation_refetch_necessity(implementation_updated_at) end - defp format_proxy_implementations_response(proxy_implementations) do - {(proxy_implementations && db_implementation_data_converter(proxy_implementations.address_hashes)) || [], - (proxy_implementations && db_implementation_data_converter(proxy_implementations.names)) || [], - proxy_implementations && proxy_implementations.proxy_type} - end - @doc """ Function checks by timestamp if new implementation fetching needed """ @@ -344,7 +338,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do {implementation_addresses, implementation_names} = implementation_address_hash_strings |> Enum.map(fn implementation_address_hash_string -> - with {:ok, implementation_address_hash} <- string_to_address_hash(implementation_address_hash_string), + with {:ok, implementation_address_hash} <- Chain.string_to_address_hash(implementation_address_hash_string), {:implementation, {%SmartContract{name: name}, _}} <- { :implementation, SmartContract.address_hash_to_smart_contract_with_bytecode_twin(implementation_address_hash, options) @@ -415,14 +409,6 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do |> Repo.update() end - defp db_implementation_data_converter(nil), do: nil - - defp db_implementation_data_converter(list) when is_list(list), - do: list |> Enum.map(&db_implementation_data_converter(&1)) - - defp db_implementation_data_converter(string) when is_binary(string), do: string - defp db_implementation_data_converter(other), do: to_string(other) - @doc """ Returns proxy's implementation names """ @@ -440,4 +426,99 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do end def names(_, _), do: [] + + if Application.compile_env(:explorer, :chain_type) == :filecoin do + @doc """ + Fetches associated addresses for Filecoin based on the provided nested IDs. + + This function is used in Ecto preload to retrieve addresses for proxy implementations. + + ## Parameters + + - nested_ids: A list of nested IDs for which the associated addresses need to be fetched. + + ## Returns + + - A list of associated addresses for the given nested IDs. + """ + def addresses_association_for_filecoin(nested_ids) do + query = from(address in Address, where: address.hash in ^List.flatten(nested_ids)) + + addresses_map = + query + |> Repo.replica().all() + |> Map.new(&{&1.hash, &1}) + + for ids <- nested_ids, + address <- ids |> Enum.map(&addresses_map[&1]) do + {ids, address} + end + end + + @doc """ + Returns the association for proxy implementations. + + This function is used to retrieve the proxy_implementations associations for address + + ## Examples + + iex> Explorer.Chain.SmartContract.Proxy.Models.Implementation.proxy_implementations_association() + [proxy_implementations: [addresses: &Explorer.Chain.SmartContract.Proxy.Models.Implementation.addresses_association_for_filecoin/1]] + """ + @spec proxy_implementations_association() :: [ + proxy_implementations: [addresses: fun()] + ] + def proxy_implementations_association do + [proxy_implementations: proxy_implementations_addresses_association()] + end + + @doc """ + Returns the association of proxy implementation addresses. + + This function is used to retrieve the addresses associations for proxy + + ## Examples + + iex> Explorer.Chain.SmartContract.Proxy.Models.Implementation.proxy_implementations_addresses_association() + [addresses: &Explorer.Chain.SmartContract.Proxy.Models.Implementation.addresses_association_for_filecoin/1] + + """ + @spec proxy_implementations_association() :: [addresses: fun()] + def proxy_implementations_addresses_association do + [addresses: &__MODULE__.addresses_association_for_filecoin/1] + end + else + @doc """ + Returns the association for proxy implementations. + + This function is used to retrieve the proxy_implementations associations for address + + ## Examples + + iex> Explorer.Chain.SmartContract.Proxy.Models.Implementation.proxy_implementations_association() + :proxy_implementations + + """ + @spec proxy_implementations_association() :: :proxy_implementations + def proxy_implementations_association do + :proxy_implementations + end + + @doc """ + Returns the association of proxy implementation addresses. + + This function is used to retrieve the addresses associations for proxy. + (Returns [] since in chain types other than Filecoin, the addresses are not needed to preload) + + ## Examples + + iex> Explorer.Chain.SmartContract.Proxy.Models.Implementation.proxy_implementations_addresses_association() + [] + + """ + @spec proxy_implementations_addresses_association() :: [] + def proxy_implementations_addresses_association do + [] + end + end end diff --git a/apps/explorer/lib/explorer/chain/token_transfer.ex b/apps/explorer/lib/explorer/chain/token_transfer.ex index 674efce8ff..e5b5efe51a 100644 --- a/apps/explorer/lib/explorer/chain/token_transfer.ex +++ b/apps/explorer/lib/explorer/chain/token_transfer.ex @@ -136,6 +136,7 @@ defmodule Explorer.Chain.TokenTransfer do require Explorer.Chain.TokenTransfer.Schema import Ecto.Changeset + import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] alias Explorer.Chain alias Explorer.Chain.{DenormalizationHelper, Hash, Log, TokenTransfer} @@ -239,8 +240,8 @@ defmodule Explorer.Chain.TokenTransfer do DenormalizationHelper.extend_transaction_preload([ :transaction, :token, - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]], - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]], + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] ]) only_consensus_transfers_query() @@ -266,8 +267,8 @@ defmodule Explorer.Chain.TokenTransfer do DenormalizationHelper.extend_transaction_preload([ :transaction, :token, - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]], - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]], + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] ]) only_consensus_transfers_query() @@ -299,8 +300,8 @@ defmodule Explorer.Chain.TokenTransfer do DenormalizationHelper.extend_transaction_preload([ :transaction, :token, - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]], - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]], + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] ]) only_consensus_transfers_query() diff --git a/apps/explorer/lib/explorer/etherscan/contracts.ex b/apps/explorer/lib/explorer/etherscan/contracts.ex index 56d4f2e24a..50475f4552 100644 --- a/apps/explorer/lib/explorer/etherscan/contracts.ex +++ b/apps/explorer/lib/explorer/etherscan/contracts.ex @@ -90,14 +90,13 @@ defmodule Explorer.Etherscan.Contracts do def append_proxy_info(%Address{smart_contract: smart_contract} = address) when not is_nil(smart_contract) do updated_smart_contract = if Proxy.proxy_contract?(smart_contract) do + implementation = Implementation.get_implementation(smart_contract) + smart_contract |> Map.put(:is_proxy, true) |> Map.put( :implementation_address_hash_strings, - smart_contract - |> Implementation.get_implementation() - |> Tuple.to_list() - |> List.first() + implementation.address_hashes ) else smart_contract diff --git a/apps/explorer/lib/explorer/microservice_interfaces/metadata.ex b/apps/explorer/lib/explorer/microservice_interfaces/metadata.ex index 60c1476b17..ac3b1f7889 100644 --- a/apps/explorer/lib/explorer/microservice_interfaces/metadata.ex +++ b/apps/explorer/lib/explorer/microservice_interfaces/metadata.ex @@ -9,6 +9,7 @@ defmodule Explorer.MicroserviceInterfaces.Metadata do alias HTTPoison.Response import Explorer.Chain.Address.MetadataPreloader, only: [maybe_preload_meta: 3] + import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] require Logger @request_timeout :timer.seconds(5) @@ -143,7 +144,11 @@ defmodule Explorer.MicroserviceInterfaces.Metadata do "items", addresses |> Chain.hashes_to_addresses( - necessity_by_association: %{names: :optional, smart_contract: :optional, proxy_implementations: :optional} + necessity_by_association: %{ + :names => :optional, + :smart_contract => :optional, + proxy_implementations_association() => :optional + } ) |> Enum.map(fn address -> {address, address.transactions_count} end) )} diff --git a/apps/explorer/lib/explorer/smart_contract/helper.ex b/apps/explorer/lib/explorer/smart_contract/helper.ex index 3196373ad1..e7b6c655ff 100644 --- a/apps/explorer/lib/explorer/smart_contract/helper.ex +++ b/apps/explorer/lib/explorer/smart_contract/helper.ex @@ -242,9 +242,9 @@ defmodule Explorer.SmartContract.Helper do @doc """ Pre-fetches implementation for unverified smart contract or verified proxy smart-contract """ - @spec pre_fetch_implementations(Address.t()) :: {any(), atom() | nil} + @spec pre_fetch_implementations(Address.t()) :: Implementation.t() | nil def pre_fetch_implementations(address) do - {implementation_address_hashes, implementation_names, proxy_type} = + implementation = 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) @@ -256,17 +256,14 @@ defmodule Explorer.SmartContract.Helper do } Implementation.get_implementation(smart_contract, @api_true) - else - {[], [], nil} end {:proxy?, false} -> - {[], [], nil} + nil end - implementations = Proxy.proxy_object_info(implementation_address_hashes, implementation_names) - - {implementations, proxy_type} + implementation + |> Chain.select_repo(@api_true).preload(Implementation.proxy_implementations_addresses_association()) end @doc """ diff --git a/apps/explorer/test/explorer/chain/smart_contract/proxy/models/implementation_test.exs b/apps/explorer/test/explorer/chain/smart_contract/proxy/models/implementation_test.exs index 506ba65a1f..c1074ecc3b 100644 --- a/apps/explorer/test/explorer/chain/smart_contract/proxy/models/implementation_test.exs +++ b/apps/explorer/test/explorer/chain/smart_contract/proxy/models/implementation_test.exs @@ -28,7 +28,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do # fetch nil implementation and don't save it to db TestHelper.get_eip1967_implementation_zero_addresses() - assert {[], [], nil} = Implementation.get_implementation(smart_contract) + assert is_nil(Implementation.get_implementation(smart_contract)) verify!(EthereumJSONRPC.Mox) assert_empty_implementation(smart_contract.address_hash) @@ -43,9 +43,13 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do string_implementation_address_hash = to_string(implementation_smart_contract.address_hash) expect_address_in_oz_slot_response(string_implementation_address_hash) + implementation_address_hash = implementation_smart_contract.address_hash - assert {[^string_implementation_address_hash], ["implementation"], :eip1967} = - Implementation.get_implementation(smart_contract) + assert %Implementation{ + address_hashes: [^implementation_address_hash], + names: ["implementation"], + proxy_type: :eip1967 + } = Implementation.get_implementation(smart_contract) verify!(EthereumJSONRPC.Mox) @@ -57,8 +61,11 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do TestHelper.get_eip1967_implementation_error_response() - assert {[^string_implementation_address_hash], ["implementation"], :eip1967} = - Implementation.get_implementation(smart_contract) + assert %Implementation{ + address_hashes: [^implementation_address_hash], + names: ["implementation"], + proxy_type: :eip1967 + } = Implementation.get_implementation(smart_contract) verify!(EthereumJSONRPC.Mox) @@ -78,8 +85,11 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do Application.put_env(:explorer, :proxy, proxy) - assert {[^string_implementation_address_hash], ["implementation"], :eip1967} = - Implementation.get_implementation(smart_contract) + assert %Implementation{ + address_hashes: [^implementation_address_hash], + names: ["implementation"], + proxy_type: :eip1967 + } = Implementation.get_implementation(smart_contract) {contract_2, _} = SmartContract.address_hash_to_smart_contract_with_bytecode_twin(smart_contract.address_hash) implementation_2 = Implementation.get_proxy_implementations(smart_contract.address_hash) @@ -96,7 +106,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do TestHelper.get_eip1967_implementation_zero_addresses() - assert {[], [], nil} = Implementation.get_implementation(smart_contract) + assert is_nil(Implementation.get_implementation(smart_contract)) verify!(EthereumJSONRPC.Mox) @@ -113,7 +123,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do test "get_implementation/1 for twins contract" do # return nils for nil - assert {[], [], nil} = Implementation.get_implementation(nil) + assert is_nil(Implementation.get_implementation(nil)) smart_contract = insert(:smart_contract) twin_address = insert(:contract_address) @@ -130,11 +140,15 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do Application.put_env(:explorer, :proxy, proxy) # fetch nil implementation - assert {[], [], :unknown} = Implementation.get_implementation(bytecode_twin) + assert %Implementation{address_hashes: [], names: [], proxy_type: :unknown} = + Implementation.get_implementation(bytecode_twin) + verify!(EthereumJSONRPC.Mox) refute_implementations(smart_contract.address_hash) - assert {[], [], :unknown} = Implementation.get_implementation(bytecode_twin) + assert %Implementation{address_hashes: [], names: [], proxy_type: :unknown} = + Implementation.get_implementation(bytecode_twin) + verify!(EthereumJSONRPC.Mox) refute_implementations(smart_contract.address_hash) @@ -149,8 +163,13 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do string_implementation_address_hash = to_string(implementation_smart_contract.address_hash) expect_address_in_oz_slot_response(string_implementation_address_hash) + implementation_address_hash = implementation_smart_contract.address_hash - assert {[^string_implementation_address_hash], ["implementation"], :eip1967} = + assert %Implementation{ + address_hashes: [^implementation_address_hash], + names: ["implementation"], + proxy_type: :eip1967 + } = Implementation.get_implementation(bytecode_twin) verify!(EthereumJSONRPC.Mox) @@ -165,7 +184,11 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do refute_implementations(smart_contract.address_hash) - assert {[^string_implementation_address_hash], ["implementation"], :eip1967} = + assert %Implementation{ + address_hashes: [^implementation_address_hash], + names: ["implementation"], + proxy_type: :eip1967 + } = Implementation.get_implementation(bytecode_twin) verify!(EthereumJSONRPC.Mox) @@ -178,11 +201,11 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do _implementation_smart_contract = insert(:smart_contract, name: "implementation") # fetch nil implementation - assert {[], [], nil} = Implementation.get_implementation(bytecode_twin) + assert is_nil(Implementation.get_implementation(bytecode_twin)) verify!(EthereumJSONRPC.Mox) refute_implementations(smart_contract.address_hash) - assert {[], [], nil} = Implementation.get_implementation(bytecode_twin) + assert is_nil(Implementation.get_implementation(bytecode_twin)) verify!(EthereumJSONRPC.Mox) refute_implementations(smart_contract.address_hash) diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs index ffd74e6520..1fe83e8f1e 100644 --- a/apps/explorer/test/explorer/chain_test.exs +++ b/apps/explorer/test/explorer/chain_test.exs @@ -2597,7 +2597,7 @@ defmodule Explorer.ChainTest do :contracts_creation_transaction, :token, [smart_contract: :smart_contract_additional_sources], - :proxy_implementations + Explorer.Chain.SmartContract.Proxy.Models.Implementation.proxy_implementations_association() ]) options = [