feat: Batch read methods requests (#10192)

* feat: Batch read methods requests

* Fix tests

* Process review comments
pull/10216/head
nikitosing 6 months ago committed by GitHub
parent 85a51007f2
commit a8e2e127b5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 18
      apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs
  2. 89
      apps/explorer/lib/explorer/smart_contract/reader.ex

@ -1677,23 +1677,33 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do
target_contract = insert(:smart_contract, abi: abi)
blockchain_eth_call_mock()
address_hash = to_string(target_contract.address_hash)
expect(
EthereumJSONRPC.Mox,
:json_rpc,
fn [
%{
id: id,
id: id_1,
method: "eth_call",
params: [%{to: _address_hash, from: "0xBb36c792B9B45Aaf8b848A1392B0d6559202729E"}, _]
params: [%{to: ^address_hash, from: "0xBb36c792B9B45Aaf8b848A1392B0d6559202729E", data: "0x2e64cec1"}, _]
},
%{
id: id_2,
method: "eth_call",
params: [%{to: ^address_hash, from: "0xBb36c792B9B45Aaf8b848A1392B0d6559202729E", data: "0xab470f05"}, _]
}
],
_opts ->
{:ok,
[
%{
id: id,
id: id_2,
jsonrpc: "2.0",
result: "0x000000000000000000000000fffffffffffffffffffffffffffffffffffffffe"
},
%{
id: id_1,
jsonrpc: "2.0",
result:
"0x0000000000000000000000000000000000000000000000000000000000000020fe6a43fa23a0269092cbf97cb908e1d5a49a18fd6942baf2467fb5b221e39ab200000000000000000000000000000000000000000000000000000000000003e8fe6a43fa23a0269092cbf97cb908e1d5a49a18fd6942baf2467fb5b221e39ab2000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000bb36c792b9b45aaf8b848a1392b0d6559202729e000000000000000000000000bb36c792b9b45aaf8b848a1392b0d6559202729e000000000000000000000000000000000000000000000000000000000001e0f30000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000bb36c792b9b45aaf8b848a1392b0d6559202729e000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000003307830000000000000000000000000000000000000000000000000000000000030783030313132323333000000000000000000000000000000000000000000003078303031313232333331323300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c3078303030303132333132330000000000000000000000000000000000000000000000000000000000000000bb36c792b9b45aaf8b848a1392b0d6559202729e000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000003307830000000000000000000000000000000000000000000000000000000000030783030313132323333000000000000000000000000000000000000000000003078303031313232333331323300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c3078303030303132333132330000000000000000000000000000000000000000000000000000000000000000bb36c792b9b45aaf8b848a1392b0d6559202729e000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000003307830000000000000000000000000000000000000000000000000000000000030783030313132323333000000000000000000000000000000000000000000003078303031313232333331323300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c3078303030303132333132330000000000000000000000000000000000000000"

@ -291,9 +291,7 @@ defmodule Explorer.SmartContract.Reader do
abi_with_method_id
|> Enum.filter(&Helper.queriable_method?(&1))
|> Enum.map(
&fetch_current_value_from_blockchain(&1, abi_with_method_id, contract_address_hash, false, options, from)
)
|> fetch_current_values_from_blockchain(abi_with_method_id, contract_address_hash, false, options, from)
end
def read_only_functions_from_abi_with_sender(_, _, _, _), do: []
@ -356,39 +354,88 @@ defmodule Explorer.SmartContract.Reader do
"tuple[#{tuple_types}]"
end
def fetch_current_value_from_blockchain(
function,
@spec fetch_current_values_from_blockchain(
any(),
[%{optional(binary()) => any()}],
Explorer.Chain.Hash.t(),
boolean(),
keyword(),
nil | binary()
) :: [SmartContract.function_description()]
def fetch_current_values_from_blockchain(
functions,
abi,
contract_address_hash,
leave_error_as_map,
options,
from \\ nil
) do
initial_methods_id_order = Enum.map(functions, &Map.get(&1, "method_id"))
%{to_be_fetched: to_be_fetched, method_id_to_outputs: method_id_to_outputs, unchanged: unchanged} =
Enum.reduce(
functions,
%{to_be_fetched: %{}, method_id_to_outputs: %{}, unchanged: %{}},
fn function,
%{
to_be_fetched: to_be_fetched,
unchanged: unchanged,
method_id_to_outputs: method_id_to_outputs
} ->
case function do
%{"inputs" => []} ->
method_id = function["method_id"]
args = function["inputs"]
[%ABI.FunctionSelector{returns: returns, method_id: _method_id}] = ABI.parse_specification([function])
%{output: outputs, names: names} =
query_function_with_names(
contract_address_hash,
%{method_id: method_id, args: args},
:regular,
from,
abi,
leave_error_as_map,
options
outputs = extract_outputs(returns)
%{
to_be_fetched: Map.put(to_be_fetched, function["method_id"], function),
unchanged: unchanged,
method_id_to_outputs: Map.put(method_id_to_outputs, function["method_id"], {outputs, function})
}
_ ->
%{
to_be_fetched: to_be_fetched,
unchanged:
Map.put(
unchanged,
function["method_id"],
Map.put(function, "abi_outputs", Map.get(function, "outputs", []))
),
method_id_to_outputs: method_id_to_outputs
}
end
end
)
methods = to_be_fetched |> Enum.map(fn {method_id, _function} -> {method_id, []} end) |> Enum.into(%{})
res =
contract_address_hash
|> query_verified_contract(methods, from, leave_error_as_map, abi, options)
method_id_to_abi_with_fetched_value =
res
|> Enum.map(fn {method_id, _result} ->
{outputs, function} = method_id_to_outputs[method_id]
names = outputs_to_list(function["outputs"])
outputs = link_outputs_and_values(res, outputs, method_id)
function = to_be_fetched[method_id]
{method_id,
function
|> Map.replace!("outputs", outputs)
|> Map.put("abi_outputs", Map.get(function, "outputs", []))
|> Map.put("names", names)
|> Map.put("names", names)}
end)
|> Enum.into(%{})
_ ->
function
|> Map.put("abi_outputs", Map.get(function, "outputs", []))
end
Enum.map(initial_methods_id_order, fn method_id ->
unchanged[method_id] || method_id_to_abi_with_fetched_value[method_id]
end)
end
@doc """

Loading…
Cancel
Save