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 = [