Merge pull request #4641 from blockscout/np-add-subclass-of-read-methods

Improve categorization of smart-contracts' methods
pull/4667/head
Victor Baranov 3 years ago committed by GitHub
commit 8e1b5519e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .dialyzer-ignore
  2. 1
      CHANGELOG.md
  3. 12
      apps/block_scout_web/assets/js/lib/smart_contract/functions.js
  4. 26
      apps/block_scout_web/assets/js/lib/smart_contract/write.js
  5. 51
      apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex
  6. 10
      apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex
  7. 4
      apps/block_scout_web/lib/block_scout_web/views/address_view.ex
  8. 54
      apps/block_scout_web/priv/gettext/default.pot
  9. 58
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  10. 2
      apps/block_scout_web/test/block_scout_web/controllers/smart_contract_controller_test.exs
  11. 15
      apps/explorer/lib/explorer/smart_contract/helper.ex
  12. 138
      apps/explorer/lib/explorer/smart_contract/reader.ex
  13. 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:440
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 Read Contract page logic
- [#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

@ -1,6 +1,6 @@
import $ from 'jquery'
import { getContractABI, getMethodInputs, prepareMethodArgs } from './common_helpers'
import { walletEnabled, connectToWallet, shouldHideConnectButton, callMethod } from './write'
import { walletEnabled, connectToWallet, shouldHideConnectButton, callMethod, queryMethod } from './write'
import '../../pages/address'
const loadFunctions = (element) => {
@ -120,14 +120,10 @@ const readWriteFunction = (element) => {
const inputs = getMethodInputs(contractAbi, functionName)
const $methodId = $form.find('input[name=method_id]')
const args = prepareMethodArgs($functionInputs, inputs)
const type = $('[data-smart-contract-functions]').data('type')
const data = {
function_name: functionName,
method_id: $methodId.val(),
args
}
$.get(url, data, response => $responseContainer.html(response))
walletEnabled()
.then((isWalletEnabled) => queryMethod(isWalletEnabled, url, $methodId, args, type, functionName, $responseContainer))
} else if (action === 'write') {
const explorerChainId = $form.data('chainId')
walletEnabled()

@ -1,4 +1,5 @@
import Web3 from 'web3'
import $ from 'jquery'
import { openErrorModal, openWarningModal, openSuccessModal, openModalWithMessage } from '../modals'
import { compareChainIDs, formatError, formatTitleAndError, getContractABI, getCurrentAccount, getMethodInputs, prepareMethodArgs } from './common_helpers'
@ -129,6 +130,31 @@ export function callMethod (isWalletEnabled, $functionInputs, explorerChainId, $
})
}
export function queryMethod (isWalletEnabled, url, $methodId, args, type, functionName, $responseContainer) {
var data = {
function_name: functionName,
method_id: $methodId.val(),
type: type,
args
}
if (isWalletEnabled) {
getCurrentAccount()
.then((currentAccount) => {
data = {
function_name: functionName,
method_id: $methodId.val(),
type: type,
from: currentAccount,
args
}
$.get(url, data, response => $responseContainer.html(response))
}
)
} else {
$.get(url, data, response => $responseContainer.html(response))
}
}
function onTransactionHash (txHash, $element, functionName) {
openModalWithMessage($element.find('#pending-contract-write'), true, txHash)
const getTxReceipt = (txHash) => {

@ -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,
@ -93,16 +102,26 @@ 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.hash, address.smart_contract.abi), do: :proxy, else: :regular
{:ok, _address} <- Chain.find_contract_address(address_hash, address_options, true) do
contract_type = if params["method_id"] == "proxy", do: :proxy, else: :regular
%{output: outputs, names: names} =
Reader.query_function_with_names(
address_hash,
%{method_id: params["method_id"], args: params["args"]},
contract_type,
params["function_name"]
)
if params["from"] do
Reader.query_function_with_names(
address_hash,
%{method_id: params["method_id"], args: params["args"]},
contract_type,
params["function_name"],
params["from"]
)
else
Reader.query_function_with_names(
address_hash,
%{method_id: params["method_id"], args: params["args"]},
contract_type,
params["function_name"]
)
end
conn
|> put_status(200)

@ -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

@ -108,16 +108,6 @@ msgstr ""
msgid "64-bit hash of value verifying proof-of-work (note: null for POA chains)."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/stakes/_stakes_modal_withdraw.html.eex:76
msgid "<p>Pending stake (stake placed on a candidate pool or placed during the current staking epoch) may be withdrawn now.</p>\n <p>Active stake (stake available after the current epoch) can be ordered for withdrawal from the pool, and will be available to claim after the current staking epoch is complete.</p>\n <p>If you have already ordered (and the staking window is still open), you may increase your current order by entering a positive value, or decrease your current order by entering a negative value in the box and clicking 'Order Withdrawal'. You must either keep the minimum stake amount in the pool, or order your entire stake for withdrawal.</p>\n"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/stakes/_stakes_modal_become_candidate.html.eex:36
msgid "<p>To become a candidate, your staking address must be funded with %{tokenSymbol} tokens <strong>and</strong> %{coinSymbol} coins, and your OpenEthereum node must be active and configured with the mining address you specify here.</p>\n <p>To become a delegator, close this window and select an address from the list of pools you would like to place stake on. Click the <strong>Stake</strong> button next to the address to begin the process.</p>"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:91
msgid "A block producer who successfully included the block onto the blockchain."
@ -408,7 +398,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:48
#: lib/block_scout_web/templates/address_validation/index.html.eex:15 lib/block_scout_web/views/address_view.ex:354
#: lib/block_scout_web/templates/address_validation/index.html.eex:15 lib/block_scout_web/views/address_view.ex:356
msgid "Blocks Validated"
msgstr ""
@ -534,13 +524,13 @@ msgstr ""
#: lib/block_scout_web/templates/address/_tabs.html.eex:58
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:165 lib/block_scout_web/templates/api_docs/_action_tile.html.eex:187
#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:126 lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:149
#: lib/block_scout_web/views/address_view.ex:347
#: lib/block_scout_web/views/address_view.ex:349
msgid "Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:34
#: lib/block_scout_web/views/address_view.ex:353
#: lib/block_scout_web/views/address_view.ex:355
msgid "Coin Balance History"
msgstr ""
@ -868,7 +858,7 @@ msgid "Decoded"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/address_view.ex:348
#: lib/block_scout_web/views/address_view.ex:350
msgid "Decompiled Code"
msgstr ""
@ -1203,6 +1193,11 @@ msgstr ""
msgid "If you have just submitted this transaction please wait for at least 30 seconds before refreshing this page."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:65
msgid "Implementation:"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/stakes/_table.html.eex:12
msgid "Inactive Pool Addresses. Current validator pools are specified by a checkmark."
@ -1246,7 +1241,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:28
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:21 lib/block_scout_web/templates/transaction/_tabs.html.eex:11
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:6 lib/block_scout_web/views/address_view.ex:344
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:6 lib/block_scout_web/views/address_view.ex:346
#: lib/block_scout_web/views/transaction_view.ex:515
msgid "Internal Transactions"
msgstr ""
@ -1372,7 +1367,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:41
#: lib/block_scout_web/templates/address_logs/index.html.eex:10 lib/block_scout_web/templates/transaction/_tabs.html.eex:17
#: lib/block_scout_web/templates/transaction_log/index.html.eex:8 lib/block_scout_web/views/address_view.ex:355
#: lib/block_scout_web/templates/transaction_log/index.html.eex:8 lib/block_scout_web/views/address_view.ex:357
#: lib/block_scout_web/views/transaction_view.ex:516
msgid "Logs"
msgstr ""
@ -1745,14 +1740,14 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:81
#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:25 lib/block_scout_web/views/address_view.ex:349
#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:25 lib/block_scout_web/views/address_view.ex:351
#: lib/block_scout_web/views/tokens/overview_view.ex:43
msgid "Read Contract"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:88
#: lib/block_scout_web/views/address_view.ex:350
#: lib/block_scout_web/views/address_view.ex:352
msgid "Read Proxy"
msgstr ""
@ -2404,7 +2399,7 @@ msgstr ""
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:19 lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex:3
#: lib/block_scout_web/templates/tokens/instance/transfer/index.html.eex:16 lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:3
#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:14 lib/block_scout_web/templates/transaction/_tabs.html.eex:4
#: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:7 lib/block_scout_web/views/address_view.ex:346
#: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:7 lib/block_scout_web/views/address_view.ex:348
#: lib/block_scout_web/views/tokens/instance/overview_view.ex:177 lib/block_scout_web/views/tokens/overview_view.ex:41
#: lib/block_scout_web/views/transaction_view.ex:514
msgid "Token Transfers"
@ -2414,7 +2409,7 @@ msgstr ""
#: lib/block_scout_web/templates/address/_tabs.html.eex:21
#: lib/block_scout_web/templates/address_token/index.html.eex:10 lib/block_scout_web/templates/address_token_transfer/index.html.eex:13
#: lib/block_scout_web/templates/layout/_topnav.html.eex:75 lib/block_scout_web/templates/layout/_topnav.html.eex:101
#: lib/block_scout_web/templates/tokens/index.html.eex:10 lib/block_scout_web/views/address_view.ex:343
#: lib/block_scout_web/templates/tokens/index.html.eex:10 lib/block_scout_web/views/address_view.ex:345
msgid "Tokens"
msgstr ""
@ -2510,6 +2505,11 @@ msgstr ""
msgid "Transaction %{transaction}, %{subnetwork} %{transaction}"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:389
msgid "Transaction Burnt Fee"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:40
msgid "Transaction Details"
@ -2550,7 +2550,7 @@ msgstr ""
#: lib/block_scout_web/templates/address/_tabs.html.eex:7
#: lib/block_scout_web/templates/address_transaction/index.html.eex:17 lib/block_scout_web/templates/block/overview.html.eex:74
#: lib/block_scout_web/templates/block_transaction/index.html.eex:10 lib/block_scout_web/templates/chain/show.html.eex:236
#: lib/block_scout_web/templates/layout/_topnav.html.eex:41 lib/block_scout_web/views/address_view.ex:345
#: lib/block_scout_web/templates/layout/_topnav.html.eex:41 lib/block_scout_web/views/address_view.ex:347
msgid "Transactions"
msgstr ""
@ -2838,13 +2838,13 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:95
#: lib/block_scout_web/views/address_view.ex:351
#: lib/block_scout_web/views/address_view.ex:353
msgid "Write Contract"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:102
#: lib/block_scout_web/views/address_view.ex:352
#: lib/block_scout_web/views/address_view.ex:354
msgid "Write Proxy"
msgstr ""
@ -3026,11 +3026,11 @@ msgid "xDai burned from transactions included in the block (Base fee (per unit o
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:65
msgid "Implementation:"
#: lib/block_scout_web/templates/stakes/_stakes_modal_withdraw.html.eex:76
msgid "<p>Pending stake (stake placed on a candidate pool or placed during the current staking epoch) may be withdrawn now.</p>\n <p>Active stake (stake available after the current epoch) can be ordered for withdrawal from the pool, and will be available to claim after the current staking epoch is complete.</p>\n <p>If you have already ordered (and the staking window is still open), you may increase your current order by entering a positive value, or decrease your current order by entering a negative value in the box and clicking 'Order Withdrawal'. You must either keep the minimum stake amount in the pool, or order your entire stake for withdrawal.</p>\n"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:389
msgid "Transaction Burnt Fee"
#: lib/block_scout_web/templates/stakes/_stakes_modal_become_candidate.html.eex:36
msgid "<p>To become a candidate, your staking address must be funded with %{tokenSymbol} tokens <strong>and</strong> %{coinSymbol} coins, and your OpenEthereum node must be active and configured with the mining address you specify here.</p>\n <p>To become a delegator, close this window and select an address from the list of pools you would like to place stake on. Click the <strong>Stake</strong> button next to the address to begin the process.</p>"
msgstr ""

@ -108,16 +108,6 @@ msgstr ""
msgid "64-bit hash of value verifying proof-of-work (note: null for POA chains)."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/stakes/_stakes_modal_withdraw.html.eex:76
msgid "<p>Pending stake (stake placed on a candidate pool or placed during the current staking epoch) may be withdrawn now.</p>\n <p>Active stake (stake available after the current epoch) can be ordered for withdrawal from the pool, and will be available to claim after the current staking epoch is complete.</p>\n <p>If you have already ordered (and the staking window is still open), you may increase your current order by entering a positive value, or decrease your current order by entering a negative value in the box and clicking 'Order Withdrawal'. You must either keep the minimum stake amount in the pool, or order your entire stake for withdrawal.</p>\n"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/stakes/_stakes_modal_become_candidate.html.eex:36
msgid "<p>To become a candidate, your staking address must be funded with %{tokenSymbol} tokens <strong>and</strong> %{coinSymbol} coins, and your OpenEthereum node must be active and configured with the mining address you specify here.</p>\n <p>To become a delegator, close this window and select an address from the list of pools you would like to place stake on. Click the <strong>Stake</strong> button next to the address to begin the process.</p>"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:91
msgid "A block producer who successfully included the block onto the blockchain."
@ -408,7 +398,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:48
#: lib/block_scout_web/templates/address_validation/index.html.eex:15 lib/block_scout_web/views/address_view.ex:354
#: lib/block_scout_web/templates/address_validation/index.html.eex:15 lib/block_scout_web/views/address_view.ex:356
msgid "Blocks Validated"
msgstr ""
@ -534,13 +524,13 @@ msgstr ""
#: lib/block_scout_web/templates/address/_tabs.html.eex:58
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:165 lib/block_scout_web/templates/api_docs/_action_tile.html.eex:187
#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:126 lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:149
#: lib/block_scout_web/views/address_view.ex:347
#: lib/block_scout_web/views/address_view.ex:349
msgid "Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:34
#: lib/block_scout_web/views/address_view.ex:353
#: lib/block_scout_web/views/address_view.ex:355
msgid "Coin Balance History"
msgstr ""
@ -868,7 +858,7 @@ msgid "Decoded"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/address_view.ex:348
#: lib/block_scout_web/views/address_view.ex:350
msgid "Decompiled Code"
msgstr ""
@ -1203,6 +1193,11 @@ msgstr ""
msgid "If you have just submitted this transaction please wait for at least 30 seconds before refreshing this page."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:65
msgid "Implementation:"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/stakes/_table.html.eex:12
msgid "Inactive Pool Addresses. Current validator pools are specified by a checkmark."
@ -1246,7 +1241,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:28
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:21 lib/block_scout_web/templates/transaction/_tabs.html.eex:11
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:6 lib/block_scout_web/views/address_view.ex:344
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:6 lib/block_scout_web/views/address_view.ex:346
#: lib/block_scout_web/views/transaction_view.ex:515
msgid "Internal Transactions"
msgstr ""
@ -1372,7 +1367,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:41
#: lib/block_scout_web/templates/address_logs/index.html.eex:10 lib/block_scout_web/templates/transaction/_tabs.html.eex:17
#: lib/block_scout_web/templates/transaction_log/index.html.eex:8 lib/block_scout_web/views/address_view.ex:355
#: lib/block_scout_web/templates/transaction_log/index.html.eex:8 lib/block_scout_web/views/address_view.ex:357
#: lib/block_scout_web/views/transaction_view.ex:516
msgid "Logs"
msgstr ""
@ -1745,14 +1740,14 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:81
#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:25 lib/block_scout_web/views/address_view.ex:349
#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:25 lib/block_scout_web/views/address_view.ex:351
#: lib/block_scout_web/views/tokens/overview_view.ex:43
msgid "Read Contract"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:88
#: lib/block_scout_web/views/address_view.ex:350
#: lib/block_scout_web/views/address_view.ex:352
msgid "Read Proxy"
msgstr ""
@ -2404,7 +2399,7 @@ msgstr ""
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:19 lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex:3
#: lib/block_scout_web/templates/tokens/instance/transfer/index.html.eex:16 lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:3
#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:14 lib/block_scout_web/templates/transaction/_tabs.html.eex:4
#: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:7 lib/block_scout_web/views/address_view.ex:346
#: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:7 lib/block_scout_web/views/address_view.ex:348
#: lib/block_scout_web/views/tokens/instance/overview_view.ex:177 lib/block_scout_web/views/tokens/overview_view.ex:41
#: lib/block_scout_web/views/transaction_view.ex:514
msgid "Token Transfers"
@ -2414,7 +2409,7 @@ msgstr ""
#: lib/block_scout_web/templates/address/_tabs.html.eex:21
#: lib/block_scout_web/templates/address_token/index.html.eex:10 lib/block_scout_web/templates/address_token_transfer/index.html.eex:13
#: lib/block_scout_web/templates/layout/_topnav.html.eex:75 lib/block_scout_web/templates/layout/_topnav.html.eex:101
#: lib/block_scout_web/templates/tokens/index.html.eex:10 lib/block_scout_web/views/address_view.ex:343
#: lib/block_scout_web/templates/tokens/index.html.eex:10 lib/block_scout_web/views/address_view.ex:345
msgid "Tokens"
msgstr ""
@ -2510,6 +2505,11 @@ msgstr ""
msgid "Transaction %{transaction}, %{subnetwork} %{transaction}"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:389
msgid "Transaction Burnt Fee"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:40
msgid "Transaction Details"
@ -2550,7 +2550,7 @@ msgstr ""
#: lib/block_scout_web/templates/address/_tabs.html.eex:7
#: lib/block_scout_web/templates/address_transaction/index.html.eex:17 lib/block_scout_web/templates/block/overview.html.eex:74
#: lib/block_scout_web/templates/block_transaction/index.html.eex:10 lib/block_scout_web/templates/chain/show.html.eex:236
#: lib/block_scout_web/templates/layout/_topnav.html.eex:41 lib/block_scout_web/views/address_view.ex:345
#: lib/block_scout_web/templates/layout/_topnav.html.eex:41 lib/block_scout_web/views/address_view.ex:347
msgid "Transactions"
msgstr ""
@ -2838,13 +2838,13 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:95
#: lib/block_scout_web/views/address_view.ex:351
#: lib/block_scout_web/views/address_view.ex:353
msgid "Write Contract"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:102
#: lib/block_scout_web/views/address_view.ex:352
#: lib/block_scout_web/views/address_view.ex:354
msgid "Write Proxy"
msgstr ""
@ -3025,12 +3025,12 @@ msgstr ""
msgid "xDai burned from transactions included in the block (Base fee (per unit of gas) * Gas Used)."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:65
msgid "Implementation:"
#, elixir-format, fuzzy
#: lib/block_scout_web/templates/stakes/_stakes_modal_withdraw.html.eex:76
msgid "<p>Pending stake (stake placed on a candidate pool or placed during the current staking epoch) may be withdrawn now.</p>\n <p>Active stake (stake available after the current epoch) can be ordered for withdrawal from the pool, and will be available to claim after the current staking epoch is complete.</p>\n <p>If you have already ordered (and the staking window is still open), you may increase your current order by entering a positive value, or decrease your current order by entering a negative value in the box and clicking 'Order Withdrawal'. You must either keep the minimum stake amount in the pool, or order your entire stake for withdrawal.</p>\n"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:389
msgid "Transaction Burnt Fee"
#, elixir-format, fuzzy
#: lib/block_scout_web/templates/stakes/_stakes_modal_become_candidate.html.eex:36
msgid "<p>To become a candidate, your staking address must be funded with %{tokenSymbol} tokens <strong>and</strong> %{coinSymbol} coins, and your OpenEthereum node must be active and configured with the mining address you specify here.</p>\n <p>To become a delegator, close this window and select an address from the list of pools you would like to place stake on. Click the <strong>Stake</strong> button next to the address to begin the process.</p>"
msgstr ""

@ -232,8 +232,6 @@ defmodule BlockScoutWeb.SmartContractControllerTest do
address = insert(:contract_address)
smart_contract = insert(:smart_contract, address_hash: address.hash)
get_eip1967_implementation()
blockchain_get_function_mock()
path =

@ -11,6 +11,21 @@ defmodule Explorer.SmartContract.Helper do
def event?(function), do: function["type"] == "event"
def error?(function), do: function["type"] == "error"
@doc """
Checks whether the function which is not queriable can be consider as read function or not.
"""
@spec read_with_wallet_method?(%{}) :: true | false
def read_with_wallet_method?(function),
do:
!error?(function) && !event?(function) && !constructor?(function) && nonpayable?(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

@ -60,24 +60,35 @@ defmodule Explorer.SmartContract.Reader do
)
# => %{"sum" => {:error, "Data overflow encoding int, data `abc` cannot fit in 256 bits"}}
"""
@spec query_verified_contract(Hash.Address.t(), functions(), String.t() | nil, SmartContract.abi()) ::
functions_results()
def query_verified_contract(address_hash, functions, from, mabi) do
query_verified_contract_inner(address_hash, functions, mabi, from)
end
@spec query_verified_contract(Hash.Address.t(), functions(), SmartContract.abi() | nil) :: functions_results()
def query_verified_contract(address_hash, functions, mabi \\ nil) do
query_verified_contract_inner(address_hash, functions, mabi, nil)
end
@spec query_verified_contract_inner(Hash.Address.t(), functions(), SmartContract.abi() | nil, String.t() | nil) ::
functions_results()
defp query_verified_contract_inner(address_hash, functions, mabi, from) do
contract_address = Hash.to_string(address_hash)
abi =
case mabi do
nil ->
address_hash
|> Chain.address_hash_to_smart_contract()
|> Map.get(:abi)
abi = prepare_abi(mabi, address_hash)
_ ->
mabi
end
query_contract(contract_address, from, abi, functions)
end
query_contract(contract_address, abi, functions)
defp prepare_abi(nil, address_hash) do
address_hash
|> Chain.address_hash_to_smart_contract()
|> Map.get(:abi)
end
defp prepare_abi(mabi, _address_hash), do: mabi
@doc """
Runs contract functions on a given address for smart contract with an expected ABI and functions.
@ -100,7 +111,7 @@ defmodule Explorer.SmartContract.Reader do
@spec query_contract(
String.t(),
String.t(),
String.t() | nil,
term(),
functions()
) :: functions_results()
@ -225,6 +236,47 @@ defmodule Explorer.SmartContract.Reader do
end
end
@doc """
Returns abi for not queriable functions of proxy's implementation which can be considered as read-only
"""
@spec read_functions_required_wallet_proxy(String.t()) :: [%{}]
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
@doc """
Returns abi for not queriable functions of the smart contract which can be considered as read-only
"""
@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
@ -317,12 +369,38 @@ defmodule Explorer.SmartContract.Reader do
Map.replace!(function, "outputs", values)
end
@doc """
Method performs query of read functions of a smart contract.
`type` could be :proxy or :reqular
"""
@spec query_function_with_names(Hash.t(), %{method_id: String.t(), args: [term()] | nil}, atom(), String.t()) :: %{
:names => [any()],
:output => [%{}]
}
def query_function_with_names(contract_address_hash, %{method_id: method_id, args: args}, type, function_name) do
outputs = query_function(contract_address_hash, %{method_id: method_id, args: args}, type)
names = parse_names_from_abi(get_abi(contract_address_hash, type), function_name)
%{output: outputs, names: names}
end
@doc """
Method performs query of read functions of a smart contract.
`type` could be :proxy or :reqular
`from` is a address of a function caller
"""
@spec query_function_with_names(
Hash.t(),
%{method_id: String.t(), args: [term()] | nil},
atom(),
String.t(),
String.t()
) :: %{:names => [any()], :output => [%{}]}
def query_function_with_names(contract_address_hash, %{method_id: method_id, args: args}, type, function_name, from) do
outputs = query_function(contract_address_hash, %{method_id: method_id, args: args}, type, from)
names = parse_names_from_abi(get_abi(contract_address_hash, type), function_name)
%{output: outputs, names: names}
end
@doc """
Fetches the blockchain value of a function that requires arguments.
"""
@ -333,29 +411,45 @@ defmodule Explorer.SmartContract.Reader do
@spec query_function(Hash.t(), %{method_id: String.t(), args: [term()]}, atom()) :: [%{}]
def query_function(contract_address_hash, %{method_id: method_id, args: args}, type) do
query_function_inner(contract_address_hash, method_id, args, type, nil)
end
@spec query_function(String.t(), %{method_id: String.t(), args: nil}, atom(), String.t() | nil) :: [%{}]
def query_function(contract_address_hash, %{method_id: method_id, args: nil}, type, from) do
query_function(contract_address_hash, %{method_id: method_id, args: []}, type, from)
end
@spec query_function(Hash.t(), %{method_id: String.t(), args: [term()]}, atom(), String.t() | nil) :: [%{}]
def query_function(contract_address_hash, %{method_id: method_id, args: args}, type, from) do
query_function_inner(contract_address_hash, method_id, args, type, from)
end
@spec query_function_inner(Hash.t(), String.t(), [term()], atom(), String.t() | nil) :: [%{}]
defp query_function_inner(contract_address_hash, method_id, args, type, from) do
abi = get_abi(contract_address_hash, type)
parsed_final_abi =
abi
|> ABI.parse_specification()
%{outputs: outputs, method_id: method_id} =
case parsed_final_abi do
nil ->
nil
%{outputs: outputs, method_id: method_id} = proccess_abi(parsed_final_abi, method_id)
_ ->
function_object = find_function_by_method(parsed_final_abi, method_id)
query_contract_and_link_outputs(contract_address_hash, args, from, abi, outputs, method_id)
end
%ABI.FunctionSelector{returns: returns, method_id: method_id} = function_object
defp proccess_abi(nil, _method_id), do: nil
outputs = extract_outputs(returns)
defp proccess_abi(abi, method_id) do
function_object = find_function_by_method(abi, method_id)
%ABI.FunctionSelector{returns: returns, method_id: method_id} = function_object
outputs = extract_outputs(returns)
%{outputs: outputs, method_id: method_id}
end
%{outputs: outputs, method_id: method_id}
end
defp query_contract_and_link_outputs(contract_address_hash, args, from, abi, outputs, method_id) do
contract_address_hash
|> query_verified_contract(%{method_id => normalize_args(args)}, abi)
|> query_verified_contract(%{method_id => normalize_args(args)}, from, abi)
|> link_outputs_and_values(outputs, method_id)
end

@ -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