Add field from to the eth_call

pull/4641/head
nikitosing 3 years ago
parent 485fd688ed
commit 40aae23252
  1. 2
      .dialyzer-ignore
  2. 2
      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. 35
      apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.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. 2
      apps/block_scout_web/test/block_scout_web/controllers/smart_contract_controller_test.exs
  9. 82
      apps/explorer/lib/explorer/smart_contract/reader.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:378
lib/explorer/smart_contract/reader.ex:413
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,7 +1,7 @@
## Current
### Features
- [#4641](https://github.com/blockscout/blockscout/pull/4641) - Improve categorization of smart-contracts' methods
- [#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) => {
@ -122,14 +122,8 @@ const readWriteFunction = (element) => {
const args = prepareMethodArgs($functionInputs, inputs)
const type = $('[data-smart-contract-functions]').data('type')
const data = {
function_name: functionName,
method_id: $methodId.val(),
type: type,
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) => {

@ -90,17 +90,38 @@ defmodule BlockScoutWeb.SmartContractController do
def index(conn, _), do: not_found(conn)
def show(conn, params) do
address_options = [
necessity_by_association: %{
:contracts_creation_internal_transaction => :optional,
:names => :optional,
:smart_contract => :optional,
:token => :optional,
:contracts_creation_transaction => :optional
}
]
with true <- ajax?(conn),
{:ok, address_hash} <- Chain.string_to_address_hash(params["id"]) do
{: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 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)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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

@ -10,7 +10,6 @@ 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.
@ -61,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.
@ -101,7 +111,7 @@ defmodule Explorer.SmartContract.Reader do
@spec query_contract(
String.t(),
String.t(),
String.t() | nil,
term(),
functions()
) :: functions_results()
@ -358,6 +368,12 @@ defmodule Explorer.SmartContract.Reader do
%{output: outputs, names: names}
end
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.
"""
@ -368,30 +384,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)
_ ->
Logger.debug(Kernel.inspect(parsed_final_abi))
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
@ -432,11 +463,8 @@ 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

Loading…
Cancel
Save