Proxy reader: handle function with params

pull/3157/head
Victor Baranov 5 years ago
parent 7adf3f4220
commit be69a0ce17
  1. 17
      apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex
  2. 10
      apps/block_scout_web/test/block_scout_web/controllers/address_read_proxy_controller_test.exs
  3. 6
      apps/block_scout_web/test/block_scout_web/controllers/smart_contract_controller_test.exs
  4. 23
      apps/explorer/lib/explorer/smart_contract/reader.ex
  5. 6
      apps/explorer/test/explorer/smart_contract/reader_test.exs

@ -38,13 +38,26 @@ defmodule BlockScoutWeb.SmartContractController do
def index(conn, _), do: not_found(conn) def index(conn, _), do: not_found(conn)
def show(conn, params) do 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), with true <- ajax?(conn),
{:ok, address_hash} <- Chain.string_to_address_hash(params["id"]), {:ok, address_hash} <- Chain.string_to_address_hash(params["id"]),
:ok <- Chain.check_contract_address_exists(address_hash) do {:ok, address} <- Chain.find_contract_address(address_hash, address_options, true) do
contract_type = if Chain.is_proxy_contract?(address.smart_contract.abi), do: :proxy, else: :regular
outputs = outputs =
Reader.query_function( Reader.query_function(
address_hash, address_hash,
%{name: params["function_name"], args: params["args"]} %{name: params["function_name"], args: params["args"]},
contract_type
) )
conn conn

@ -21,7 +21,7 @@ defmodule BlockScoutWeb.AddressReadProxyControllerTest do
end end
test "with invalid address hash", %{conn: conn} do test "with invalid address hash", %{conn: conn} do
conn = get(conn, address_read_contract_path(BlockScoutWeb.Endpoint, :index, "invalid_address")) conn = get(conn, address_read_proxy_path(BlockScoutWeb.Endpoint, :index, "invalid_address"))
assert html_response(conn, 404) assert html_response(conn, 404)
end end
@ -29,7 +29,7 @@ defmodule BlockScoutWeb.AddressReadProxyControllerTest do
test "with valid address that is not a contract", %{conn: conn} do test "with valid address that is not a contract", %{conn: conn} do
address = insert(:address) address = insert(:address)
conn = get(conn, address_read_contract_path(BlockScoutWeb.Endpoint, :index, Address.checksum(address.hash))) conn = get(conn, address_read_proxy_path(BlockScoutWeb.Endpoint, :index, Address.checksum(address.hash)))
assert html_response(conn, 404) assert html_response(conn, 404)
end end
@ -50,8 +50,7 @@ defmodule BlockScoutWeb.AddressReadProxyControllerTest do
insert(:smart_contract, address_hash: contract_address.hash) insert(:smart_contract, address_hash: contract_address.hash)
conn = conn = get(conn, address_read_proxy_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash)))
get(conn, address_read_contract_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash)))
assert html_response(conn, 200) assert html_response(conn, 200)
assert contract_address.hash == conn.assigns.address.hash assert contract_address.hash == conn.assigns.address.hash
@ -72,8 +71,7 @@ defmodule BlockScoutWeb.AddressReadProxyControllerTest do
block_index: 0 block_index: 0
) )
conn = conn = get(conn, address_read_proxy_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash)))
get(conn, address_read_contract_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash)))
assert html_response(conn, 404) assert html_response(conn, 404)
end end

@ -22,7 +22,7 @@ defmodule BlockScoutWeb.SmartContractControllerTest do
end end
test "error for invalid address" do test "error for invalid address" do
path = smart_contract_path(BlockScoutWeb.Endpoint, :index, hash: "0x00", type: "regular") path = smart_contract_path(BlockScoutWeb.Endpoint, :index, hash: "0x00", type: :regular)
conn = conn =
build_conn() build_conn()
@ -49,7 +49,7 @@ defmodule BlockScoutWeb.SmartContractControllerTest do
blockchain_get_function_mock() blockchain_get_function_mock()
path = smart_contract_path(BlockScoutWeb.Endpoint, :index, hash: token_contract_address.hash, type: "regular") path = smart_contract_path(BlockScoutWeb.Endpoint, :index, hash: token_contract_address.hash, type: :regular)
conn = conn =
build_conn() build_conn()
@ -65,7 +65,7 @@ defmodule BlockScoutWeb.SmartContractControllerTest do
insert(:smart_contract, address_hash: token_contract_address.hash) insert(:smart_contract, address_hash: token_contract_address.hash)
path = smart_contract_path(BlockScoutWeb.Endpoint, :index, hash: token_contract_address.hash, type: "proxy") path = smart_contract_path(BlockScoutWeb.Endpoint, :index, hash: token_contract_address.hash, type: :proxy)
conn = conn =
build_conn() build_conn()

@ -221,26 +221,33 @@ defmodule Explorer.SmartContract.Reader do
@doc """ @doc """
Fetches the blockchain value of a function that requires arguments. Fetches the blockchain value of a function that requires arguments.
""" """
@spec query_function(String.t(), %{name: String.t(), args: nil}) :: [%{}] @spec query_function(String.t(), %{name: String.t(), args: nil}, atom()) :: [%{}]
def query_function(contract_address_hash, %{name: name, args: nil}) do def query_function(contract_address_hash, %{name: name, args: nil}, type) do
query_function(contract_address_hash, %{name: name, args: []}) query_function(contract_address_hash, %{name: name, args: []}, type)
end end
@spec query_function(Hash.t(), %{name: String.t(), args: [term()]}) :: [%{}] @spec query_function(Hash.t(), %{name: String.t(), args: [term()]}, atom()) :: [%{}]
def query_function(contract_address_hash, %{name: name, args: args}) do def query_function(contract_address_hash, %{name: name, args: args}, type) do
abi = abi =
contract_address_hash contract_address_hash
|> Chain.address_hash_to_smart_contract() |> Chain.address_hash_to_smart_contract()
|> Map.get(:abi) |> Map.get(:abi)
final_abi =
if type == :proxy do
Chain.get_implementation_abi_from_proxy(contract_address_hash, abi)
else
abi
end
outputs = outputs =
case abi do case final_abi do
nil -> nil ->
nil nil
_ -> _ ->
function = function =
abi final_abi
|> Enum.filter(fn function -> function["name"] == name end) |> Enum.filter(fn function -> function["name"] == name end)
|> List.first() |> List.first()
@ -248,7 +255,7 @@ defmodule Explorer.SmartContract.Reader do
end end
contract_address_hash contract_address_hash
|> query_verified_contract(%{name => normalize_args(args)}, abi) |> query_verified_contract(%{name => normalize_args(args)}, final_abi)
|> link_outputs_and_values(outputs, name) |> link_outputs_and_values(outputs, name)
end end

@ -262,7 +262,7 @@ defmodule Explorer.SmartContract.ReaderTest do
end end
end end
describe "query_function/2" do describe "query_function/3" do
test "given the arguments, fetches the function value from the blockchain" do test "given the arguments, fetches the function value from the blockchain" do
smart_contract = insert(:smart_contract) smart_contract = insert(:smart_contract)
@ -274,7 +274,7 @@ defmodule Explorer.SmartContract.ReaderTest do
"type" => "uint256", "type" => "uint256",
"value" => 0 "value" => 0
} }
] = Reader.query_function(smart_contract.address_hash, %{name: "get", args: []}) ] = Reader.query_function(smart_contract.address_hash, %{name: "get", args: []}, :regular)
end end
test "nil arguments is treated as []" do test "nil arguments is treated as []" do
@ -288,7 +288,7 @@ defmodule Explorer.SmartContract.ReaderTest do
"type" => "uint256", "type" => "uint256",
"value" => 0 "value" => 0
} }
] = Reader.query_function(smart_contract.address_hash, %{name: "get", args: nil}) ] = Reader.query_function(smart_contract.address_hash, %{name: "get", args: nil}, :regular)
end end
end end

Loading…
Cancel
Save