Improve categorization of smart-contracts' methods

pull/4641/head
nikitosing 3 years ago
parent 98791132f0
commit 000861c9ac
  1. 2
      .dialyzer-ignore
  2. 1
      CHANGELOG.md
  3. 25
      apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex
  4. 10
      apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex
  5. 4
      apps/block_scout_web/lib/block_scout_web/views/address_view.ex
  6. 3036
      apps/block_scout_web/priv/gettext/default.pot
  7. 3036
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  8. 11
      apps/explorer/lib/explorer/smart_contract/helper.ex
  9. 39
      apps/explorer/lib/explorer/smart_contract/reader.ex
  10. 2
      apps/explorer/lib/explorer/smart_contract/writer.ex

@ -18,7 +18,7 @@ lib/block_scout_web/views/layout_view.ex:145: The call 'Elixir.Poison.Parser':'p
lib/block_scout_web/views/layout_view.ex:237: The call 'Elixir.Poison.Parser':'parse!'
lib/block_scout_web/controllers/api/rpc/transaction_controller.ex:21
lib/block_scout_web/controllers/api/rpc/transaction_controller.ex:22
lib/explorer/smart_contract/reader.ex:344
lib/explorer/smart_contract/reader.ex:378
lib/indexer/fetcher/token_total_supply_on_demand.ex:16
lib/explorer/exchange_rates/source.ex:110
lib/explorer/exchange_rates/source.ex:113

@ -1,6 +1,7 @@
## Current
### Features
- [#4641](https://github.com/blockscout/blockscout/pull/4641) - Improve categorization of smart-contracts' methods
- [#4660](https://github.com/blockscout/blockscout/pull/4660) - Save Sourcify path instead of filename
- [#4655](https://github.com/blockscout/blockscout/pull/4655) - EIP-3091 support
- [#4621](https://github.com/blockscout/blockscout/pull/4621) - Add beacon contract address slot for proxy

@ -26,14 +26,11 @@ defmodule BlockScoutWeb.SmartContractController do
functions =
if action == "write" do
write_functions =
if contract_type == "proxy" do
Writer.write_functions_proxy(implementation_address_hash_string)
else
Writer.write_functions(address_hash)
end
Enum.filter(write_functions, fn x -> x["type"] != "error" end)
if contract_type == "proxy" do
Writer.write_functions_proxy(implementation_address_hash_string)
else
Writer.write_functions(address_hash)
end
else
if contract_type == "proxy" do
Reader.read_only_functions_proxy(address_hash, implementation_address_hash_string)
@ -42,6 +39,17 @@ defmodule BlockScoutWeb.SmartContractController do
end
end
read_functions_required_wallet =
if action == "read" do
if contract_type == "proxy" do
Reader.read_functions_required_wallet_proxy(implementation_address_hash_string)
else
Reader.read_functions_required_wallet(address_hash)
end
else
[]
end
contract_abi = Poison.encode!(address.smart_contract.abi)
implementation_abi =
@ -58,6 +66,7 @@ defmodule BlockScoutWeb.SmartContractController do
|> put_layout(false)
|> render(
"_functions.html",
read_functions_required_wallet: read_functions_required_wallet,
read_only_functions: functions,
address: address,
contract_abi: contract_abi,

@ -23,7 +23,7 @@
<% end %>
<% end %>
<% end %>
<%= if @action== "write" do %>
<%= if @action == "write" or (@read_functions_required_wallet && @read_functions_required_wallet != []) do %>
<%= render BlockScoutWeb.SmartContractView, "_connect_container.html" %>
<% end %>
<%= if @contract_type == "proxy" do %>
@ -34,7 +34,7 @@
) %></h3>
</div>
<% end %>
<%= for {function, counter} <- Enum.with_index(@read_only_functions, 1) do %>
<%= for {function, counter} <- Enum.with_index(@read_only_functions ++ @read_functions_required_wallet, 1) do %>
<div class="d-flex py-2 border-bottom" data-function>
<div class="py-2 pr-2 text-nowrap">
<%= counter %>.
@ -49,7 +49,7 @@
&#8594;
</div>
<%= if queryable?(function["inputs"]) || writable?(function) do %>
<%= if queryable?(function["inputs"]) || writable?(function) || Helper.read_with_wallet_method?(function) do %>
<div style="width: 100%; overflow: hidden;">
<%=
for status <- ["error", "warning", "success", "question"] do
@ -57,7 +57,7 @@
end
%>
<%= render BlockScoutWeb.SmartContractView, "_pending_contract_write.html" %>
<form class="form-inline" data-function-form data-action="<%= if writable?(function), do: :write, else: :read %>" data-type="<%= @contract_type %>" data-url="<%= smart_contract_path(@conn, :show, Address.checksum(@address.hash)) %>" data-contract-address="<%= @address.hash %>" data-contract-abi="<%= @contract_abi %>" data-implementation-abi="<%= @implementation_abi %>" data-chain-id="<%= Explorer.Chain.Cache.NetVersion.get_version() %>">
<form class="form-inline" data-function-form data-action="<%= if @action == "write", do: :write, else: :read %>" data-type="<%= @contract_type %>" data-url="<%= smart_contract_path(@conn, :show, Address.checksum(@address.hash)) %>" data-contract-address="<%= @address.hash %>" data-contract-abi="<%= @contract_abi %>" data-implementation-abi="<%= @implementation_abi %>" data-chain-id="<%= Explorer.Chain.Cache.NetVersion.get_version() %>">
<input type="hidden" name="function_name" value='<%= function["name"] %>' />
<input type="hidden" name="method_id" value='<%= function["method_id"] %>' />
@ -96,7 +96,7 @@
</div>
<% end %>
<input type="submit" value='<%= if writable?(function), do: gettext("Write"), else: gettext("Query")%>' class="button btn-line button-xs py-0 mt-2 write-contract-btn" />
<input type="submit" value='<%= if @action == "write", do: gettext("Write"), else: gettext("Query")%>' class="button btn-line button-xs py-0 mt-2 write-contract-btn" />
</form>
<%= if outputs?(function["outputs"]) do %>

@ -229,11 +229,13 @@ defmodule BlockScoutWeb.AddressView do
def smart_contract_verified?(%Address{smart_contract: nil}), do: false
def smart_contract_with_read_only_functions?(%Address{smart_contract: %SmartContract{}} = address) do
Enum.any?(address.smart_contract.abi, &Helper.queriable_method?(&1))
Enum.any?(address.smart_contract.abi, &is_read_function?(&1))
end
def smart_contract_with_read_only_functions?(%Address{smart_contract: nil}), do: false
def is_read_function?(function), do: Helper.queriable_method?(function) || Helper.read_with_wallet_method?(function)
def smart_contract_is_proxy?(%Address{smart_contract: %SmartContract{}} = address) do
Chain.proxy_contract?(address.hash, address.smart_contract.abi)
end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -11,6 +11,17 @@ defmodule Explorer.SmartContract.Helper do
def event?(function), do: function["type"] == "event"
def error?(function), do: function["type"] == "error"
def read_with_wallet_method?(function),
do:
!error?(function) && !event?(function) && !constructor?(function) && nonpayable?(function) &&
empty_inputs?(function) && !empty_outputs?(function)
def empty_inputs?(function), do: function["inputs"] == []
def empty_outputs?(function), do: function["outputs"] == []
def payable?(function), do: function["stateMutability"] == "payable" || function["payable"]
def nonpayable?(function) do

@ -10,6 +10,7 @@ defmodule Explorer.SmartContract.Reader do
alias Explorer.Chain
alias Explorer.Chain.{Hash, SmartContract}
alias Explorer.SmartContract.Helper
require Logger
@typedoc """
Map of functions to call with the values for the function to be called with.
@ -225,6 +226,40 @@ defmodule Explorer.SmartContract.Reader do
end
end
def read_functions_required_wallet_proxy(implementation_address_hash_string) do
implementation_abi = Chain.get_implementation_abi(implementation_address_hash_string)
case implementation_abi do
nil ->
[]
_ ->
implementation_abi_with_method_id = get_abi_with_method_id(implementation_abi)
implementation_abi_with_method_id
|> Enum.filter(&Helper.read_with_wallet_method?(&1))
end
end
@spec read_functions_required_wallet(Hash.t()) :: [%{}]
def read_functions_required_wallet(contract_address_hash) do
abi =
contract_address_hash
|> Chain.address_hash_to_smart_contract()
|> Map.get(:abi)
case abi do
nil ->
[]
_ ->
abi_with_method_id = get_abi_with_method_id(abi)
abi_with_method_id
|> Enum.filter(&Helper.read_with_wallet_method?(&1))
end
end
defp get_abi_with_method_id(abi) do
parsed_abi =
abi
@ -345,6 +380,7 @@ defmodule Explorer.SmartContract.Reader do
nil
_ ->
Logger.debug(Kernel.inspect(parsed_final_abi))
function_object = find_function_by_method(parsed_final_abi, method_id)
%ABI.FunctionSelector{returns: returns, method_id: method_id} = function_object
@ -396,8 +432,11 @@ defmodule Explorer.SmartContract.Reader do
defp validate_name(name), do: not is_nil(name) and String.length(name) > 0
defp find_function_by_method(parsed_abi, method_id) do
Logger.debug("find fucntion")
Logger.debug(method_id)
parsed_abi
|> Enum.filter(fn %ABI.FunctionSelector{method_id: find_method_id} ->
Logger.debug(Kernel.inspect(Base.encode16(find_method_id, case: :lower)))
if find_method_id do
Base.encode16(find_method_id, case: :lower) == method_id || find_method_id == method_id
else

@ -38,7 +38,7 @@ defmodule Explorer.SmartContract.Writer do
end
def write_function?(function) do
!Helper.event?(function) && !Helper.constructor?(function) &&
!Helper.error?(function) && !Helper.event?(function) && !Helper.constructor?(function) &&
(Helper.payable?(function) || Helper.nonpayable?(function))
end

Loading…
Cancel
Save