Skip to content

Commit

Permalink
EIP-1967 proxy pattern detection fix
Browse files Browse the repository at this point in the history
  • Loading branch information
vbaranov committed May 3, 2021
1 parent d802e67 commit 8609d8e
Show file tree
Hide file tree
Showing 16 changed files with 59 additions and 37 deletions.
Expand Up @@ -91,7 +91,7 @@ defmodule BlockScoutWeb.SmartContractController do
with true <- ajax?(conn),
{:ok, address_hash} <- Chain.string_to_address_hash(params["id"]),
{:ok, address} <- Chain.find_contract_address(address_hash, address_options, true) do
contract_type = if Chain.proxy_contract?(address.smart_contract.abi), do: :proxy, else: :regular
contract_type = if Chain.proxy_contract?(address.hash, address.smart_contract.abi), do: :proxy, else: :regular

outputs =
Reader.query_function(
Expand Down
Expand Up @@ -83,7 +83,7 @@
class: "card-tab #{tab_status("read-contract", @conn.request_path)}")
%>
<% end %>
<%= if smart_contract_is_proxy?(@address) do %>
<%= if @is_proxy do %>
<%= link(
gettext("Read Proxy"),
to: AccessHelpers.get_path(@conn, :address_read_proxy_path, :index, @address.hash),
Expand All @@ -97,7 +97,7 @@
class: "card-tab #{tab_status("write-contract", @conn.request_path)}")
%>
<% end %>
<%= if smart_contract_with_write_functions?(@address) && smart_contract_is_proxy?(@address) do %>
<%= if smart_contract_with_write_functions?(@address) && @is_proxy do %>
<%= link(
gettext("Write Proxy"),
to: AccessHelpers.get_path(@conn, :address_write_proxy_path, :index, @address.hash),
Expand Down
Expand Up @@ -6,10 +6,12 @@
<% additional_sources = if smart_contract_verified, do: @address.smart_contract_additional_sources, else: additional_sources_from_twin %>

<section class="container">
<%= render BlockScoutWeb.AddressView, "overview.html", assigns %>
<% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %>

<%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path %>

<div class="card">
<%= render BlockScoutWeb.AddressView, "_tabs.html", assigns %>
<%= render BlockScoutWeb.AddressView, "_tabs.html", address: @address, is_proxy: is_proxy, conn: @conn %>
<div class="card-body">
<%= unless smart_contract_verified do %>
<%= if minimal_proxy_template do %>
Expand Down
@@ -1,7 +1,9 @@
<section class="container">
<%= render BlockScoutWeb.AddressView, "overview.html", assigns %>
<% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %>

<%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path %>
<div class="card">
<%= render BlockScoutWeb.AddressView, "_tabs.html", assigns %>
<%= render BlockScoutWeb.AddressView, "_tabs.html", address: @address, is_proxy: is_proxy, conn: @conn %>
<% contract = last_decompiled_contract_version(@address.decompiled_smart_contracts) %>
<%= if contract do %>
<div class="card-body">
Expand Down
@@ -1,9 +1,11 @@
<section class="container">
<%= render BlockScoutWeb.AddressView, "overview.html", assigns %>
<% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %>

<%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path %>

<section data-page="address-internal-transactions">
<div class="card">
<%= render BlockScoutWeb.AddressView, "_tabs.html", assigns %>
<%= render BlockScoutWeb.AddressView, "_tabs.html", address: @address, is_proxy: is_proxy, conn: @conn %>
<div class="card-body" data-async-load data-async-listing="<%= @current_path %>">
<div data-selector="channel-batching-message" style="display: none;">
<div data-selector="reload-button" class="alert alert-info">
Expand Down
@@ -1,8 +1,10 @@
<section class="container">
<%= render BlockScoutWeb.AddressView, "overview.html", assigns %>
<% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %>

<%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path %>
<section data-page="address-logs">
<div class="card">
<%= render BlockScoutWeb.AddressView, "_tabs.html", assigns %>
<%= render BlockScoutWeb.AddressView, "_tabs.html", address: @address, is_proxy: is_proxy, conn: @conn %>

<div class="card-body" data-async-load data-async-listing="<%= @current_path %>">
<h2 class="card-title"><%= gettext "Logs" %></h2>
Expand Down
@@ -1,9 +1,11 @@
<section class="container">

<%= render BlockScoutWeb.AddressView, "overview.html", assigns %>
<% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %>

<%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path %>

<div class="card">
<%= render BlockScoutWeb.AddressView, "_tabs.html", assigns %>
<%= render BlockScoutWeb.AddressView, "_tabs.html", address: @address, is_proxy: is_proxy, conn: @conn %>
<!-- loaded via AJAX -->
<div class="card-body" data-smart-contract-functions data-hash="<%= to_string(@address.hash) %>" data-type="<%= @type %>" data-action="<%= @action %>" data-url="<%= smart_contract_path(@conn, :index) %>">
<div>
Expand Down
@@ -1,9 +1,11 @@
<section class="container">

<%= render BlockScoutWeb.AddressView, "overview.html", assigns %>
<% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %>

<%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path %>

<div class="card">
<%= render BlockScoutWeb.AddressView, "_tabs.html", assigns %>
<%= render BlockScoutWeb.AddressView, "_tabs.html", address: @address, is_proxy: is_proxy, conn: @conn %>
<!-- loaded via AJAX -->
<div class="card-body" data-smart-contract-functions data-hash="<%= to_string(@address.hash) %>" data-type="<%= @type %>" data-action="<%= @action %>" data-url="<%= smart_contract_path(@conn, :index) %>">
<div>
Expand Down
@@ -1,9 +1,11 @@
<section class="container">
<%= render BlockScoutWeb.AddressView, "overview.html", assigns %>
<% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %>

<%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path %>

<section>
<div class="card">
<%= render BlockScoutWeb.AddressView, "_tabs.html", assigns %>
<%= render BlockScoutWeb.AddressView, "_tabs.html", address: @address, is_proxy: is_proxy, conn: @conn %>
<div class="card-body" data-async-load data-async-listing="<%= @current_path %>">
<h2 class="card-title list-title-description"><%= gettext "Tokens" %></h2>

Expand Down
@@ -1,9 +1,11 @@
<section class="container">
<%= render BlockScoutWeb.AddressView, "overview.html", assigns %>
<% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %>

<%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path %>

<section data-page="address-token-transfers">
<div class="card">
<%= render BlockScoutWeb.AddressView, "_tabs.html", assigns %>
<%= render BlockScoutWeb.AddressView, "_tabs.html", address: @address, is_proxy: is_proxy, conn: @conn %>
<div data-async-load data-async-listing="<%= @current_path %>" class="card-body">

<%= if assigns[:token] do %>
Expand Down
@@ -1,10 +1,12 @@
<section class="container">

<%= render BlockScoutWeb.AddressView, "overview.html", assigns %>
<% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %>

<%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path %>

<section data-page="address-transactions">
<div class="card">
<%= render BlockScoutWeb.AddressView, "_tabs.html", assigns %>
<%= render BlockScoutWeb.AddressView, "_tabs.html", address: @address, is_proxy: is_proxy, conn: @conn %>
<div class="card-body" data-async-listing="<%= @current_path %>">
<div data-selector="channel-disconnected-message" style="display: none;">
<div data-selector="reload-button" class="alert alert-danger">
Expand Down
@@ -1,9 +1,11 @@
<section class="container">
<%= render BlockScoutWeb.AddressView, "overview.html", assigns %>
<% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %>

<%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path %>

<section data-page="blocks-validated">
<div class="card">
<%= render BlockScoutWeb.AddressView, "_tabs.html", assigns %>
<%= render BlockScoutWeb.AddressView, "_tabs.html", address: @address, is_proxy: is_proxy, conn: @conn %>
<div data-async-listing="<%= @current_path %>" class="card-body">
<div data-selector="channel-disconnected-message" style="display: none;">
<div data-selector="reload-button" class="alert alert-danger">
Expand Down
@@ -1,9 +1,11 @@
<section class="container">

<%= render BlockScoutWeb.AddressView, "overview.html", assigns %>
<% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %>

<%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path %>

<div class="card">
<%= render BlockScoutWeb.AddressView, "_tabs.html", assigns %>
<%= render BlockScoutWeb.AddressView, "_tabs.html", address: @address, is_proxy: is_proxy, conn: @conn %>
<!-- loaded via AJAX -->
<div class="card-body" data-smart-contract-functions data-hash="<%= to_string(@address.hash) %>" data-type="<%= @type %>" data-action="<%= @action %>" data-url="<%= smart_contract_path(@conn, :index) %>">
<div>
Expand Down
@@ -1,9 +1,11 @@
<section class="container">

<%= render BlockScoutWeb.AddressView, "overview.html", assigns %>
<% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %>

<%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path %>

<div class="card">
<%= render BlockScoutWeb.AddressView, "_tabs.html", assigns %>
<%= render BlockScoutWeb.AddressView, "_tabs.html", address: @address, is_proxy: is_proxy, conn: @conn %>
<!-- loaded via AJAX -->
<div class="card-body" data-smart-contract-functions data-hash="<%= to_string(@address.hash) %>" data-type="<%= @type %>" data-action="<%= @action %>" data-url="<%= smart_contract_path(@conn, :index) %>">
<div>
Expand Down
Expand Up @@ -235,7 +235,7 @@ defmodule BlockScoutWeb.AddressView do
def smart_contract_with_read_only_functions?(%Address{smart_contract: nil}), do: false

def smart_contract_is_proxy?(%Address{smart_contract: %SmartContract{}} = address) do
Chain.proxy_contract?(address.smart_contract.abi)
Chain.proxy_contract?(address.hash, address.smart_contract.abi)
end

def smart_contract_is_proxy?(%Address{smart_contract: nil}), do: false
Expand Down
16 changes: 7 additions & 9 deletions apps/explorer/lib/explorer/chain.ex
Expand Up @@ -5961,18 +5961,21 @@ defmodule Explorer.Chain do
[]
end

def proxy_contract?(abi) when not is_nil(abi) do
def proxy_contract?(address_hash, abi) when not is_nil(abi) do
implementation_method_abi =
abi
|> Enum.find(fn method ->
Map.get(method, "name") == "implementation" ||
master_copy_pattern?(method)
end)

if implementation_method_abi, do: true, else: false
if implementation_method_abi ||
get_implementation_address_hash_eip_1967(address_hash) !== "0x0000000000000000000000000000000000000000",
do: true,
else: false
end

def proxy_contract?(abi) when is_nil(abi), do: false
def proxy_contract?(_address_hash, abi) when is_nil(abi), do: false

def gnosis_safe_contract?(abi) when not is_nil(abi) do
implementation_method_abi =
Expand All @@ -5997,26 +6000,21 @@ defmodule Explorer.Chain do
implementation_method_abi_state_mutability =
implementation_method_abi && Map.get(implementation_method_abi, "stateMutability")

is_eip1967 = if implementation_method_abi_state_mutability == "nonpayable", do: true, else: false

master_copy_method_abi =
abi
|> Enum.find(fn method ->
master_copy_pattern?(method)
end)

cond do
is_eip1967 ->
get_implementation_address_hash_eip_1967(proxy_address_hash)

implementation_method_abi ->
get_implementation_address_hash_basic(proxy_address_hash, abi)

master_copy_method_abi ->
get_implementation_address_hash_from_master_copy_pattern(proxy_address_hash)

true ->
nil
get_implementation_address_hash_eip_1967(proxy_address_hash)
end
end

Expand Down

0 comments on commit 8609d8e

Please sign in to comment.