From 4c3f9b3de404b97738c4dc812c24c8ae0f2d3af4 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Mon, 7 Dec 2020 17:08:38 +0300 Subject: [PATCH] Allow brakets for arrays for contract input data, fix decoding for multiple inputs --- CHANGELOG.md | 2 + .../assets/js/lib/smart_contract/functions.js | 3 + .../lib/ethereum_jsonrpc/contract.ex | 67 ++++++++++++++----- .../lib/ethereum_jsonrpc/encoder.ex | 14 +++- .../lib/explorer/smart_contract/reader.ex | 21 ++++-- 5 files changed, 81 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e59618e23..9b5283092b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,13 @@ ## Current ### Features +- [#3513](https://github.com/poanetwork/blockscout/pull/3513) - Allow square brackets for an array input data in contracts interaction - [#3480](https://github.com/poanetwork/blockscout/pull/3480) - Add support of Autonity client - [#3470](https://github.com/poanetwork/blockscout/pull/3470) - Display sum of tokens' USD value at tokens holder's address page - [#3462](https://github.com/poanetwork/blockscout/pull/3462) - Display price for bridged tokens ### Fixes +- [#3513](https://github.com/poanetwork/blockscout/pull/3513) - Fix input data processing for method call (array type of data) - [#3509](https://github.com/poanetwork/blockscout/pull/3509) - Fix QR code tooltip appearance in mobile view - [#3507](https://github.com/poanetwork/blockscout/pull/3507), [#3510](https://github.com/poanetwork/blockscout/pull/3510) - Fix left margin of balance card in mobile view - [#3506](https://github.com/poanetwork/blockscout/pull/3506) - Fix token trasfer's tile styles: prevent overlapping of long names diff --git a/apps/block_scout_web/assets/js/lib/smart_contract/functions.js b/apps/block_scout_web/assets/js/lib/smart_contract/functions.js index def0e341c8..bea7eb12ae 100644 --- a/apps/block_scout_web/assets/js/lib/smart_contract/functions.js +++ b/apps/block_scout_web/assets/js/lib/smart_contract/functions.js @@ -135,6 +135,9 @@ function callMethod (isWalletEnabled, $functionInputs, explorerChainId, $form, f let preparedVal if (isNonSpaceInputType(inputType)) { preparedVal = val.replace(/\s/g, '') } else { preparedVal = val } if (isArrayInputType(inputType)) { + if (preparedVal.startsWith('[') && preparedVal.endsWith(']')) { + preparedVal = preparedVal.substring(1, preparedVal.length - 1) + } return preparedVal.split(',') } else { return preparedVal } }) diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/contract.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/contract.ex index 724b66b000..62a582ef4d 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/contract.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/contract.ex @@ -78,10 +78,11 @@ defmodule EthereumJSONRPC.Contract do end defp format_args(function, args) do + types = function.types + args |> Enum.with_index() |> Enum.map(fn {arg, index} -> - types = function.types type = Enum.at(types, index) convert_string_to_array(type, arg) @@ -97,27 +98,55 @@ defmodule EthereumJSONRPC.Contract do convert_int_string_to_array(arg) {:array, _} -> - if arg && arg !== "" do - String.split(arg, ",") - else - [] - end + convert_string_to_array(arg) _ -> arg end end - defp convert_int_string_to_array(arg) do - if arg && arg !== "" do - arg - |> String.split(",") - |> Enum.map(fn el -> - {int, _} = Integer.parse(el) - int - end) - else - [] + defp convert_int_string_to_array(arg) when is_nil(arg), do: true + + defp convert_int_string_to_array(arg) when not is_nil(arg) do + cond do + String.starts_with?(arg, "[") && String.ends_with?(arg, "]") -> + arg + |> String.trim_leading("[") + |> String.trim_trailing("]") + |> convert_int_string_to_array_inner() + + arg !== "" -> + convert_int_string_to_array_inner(arg) + + true -> + [] + end + end + + defp convert_int_string_to_array_inner(arg) do + arg + |> String.split(",") + |> Enum.map(fn el -> + {int, _} = Integer.parse(el) + int + end) + end + + defp convert_string_to_array(arg) when is_nil(arg), do: true + + defp convert_string_to_array(arg) when not is_nil(arg) do + cond do + String.starts_with?(arg, "[") && String.ends_with?(arg, "]") -> + arg + |> String.trim_leading("[") + |> String.trim_trailing("]") + |> String.split(",") + + arg !== "" -> + String.split(arg, ",") + + true -> + [] end end @@ -151,11 +180,13 @@ defmodule EthereumJSONRPC.Contract do block_number -> integer_to_quantity(block_number) end - request(%{ + full_params = %{ id: id, method: "eth_call", params: [%{to: contract_address, data: data, from: from}, block] - }) + } + + request(full_params) end def eth_get_storage_at_request(contract_address, storage_pointer, block_number, json_rpc_named_arguments) do diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/encoder.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/encoder.ex index f5964bdd0b..a792ae728e 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/encoder.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/encoder.ex @@ -13,9 +13,11 @@ defmodule EthereumJSONRPC.Encoder do """ @spec encode_function_call(%ABI.FunctionSelector{}, [term()]) :: String.t() def encode_function_call(function_selector, args) do + parsed_args = parse_args(args) + encoded_args = function_selector - |> ABI.encode(parse_args(args)) + |> ABI.encode(parsed_args) |> Base.encode16(case: :lower) "0x" <> encoded_args @@ -28,7 +30,15 @@ defmodule EthereumJSONRPC.Encoder do Base.decode16!(hexadecimal_digits, case: :mixed) item -> - item + if is_list(item) do + item + |> Enum.map(fn el -> + <<"0x", hexadecimal_digits::binary>> = el + Base.decode16!(hexadecimal_digits, case: :mixed) + end) + else + item + end end) end diff --git a/apps/explorer/lib/explorer/smart_contract/reader.ex b/apps/explorer/lib/explorer/smart_contract/reader.ex index 081c89cf50..304bf8cf0f 100644 --- a/apps/explorer/lib/explorer/smart_contract/reader.ex +++ b/apps/explorer/lib/explorer/smart_contract/reader.ex @@ -259,18 +259,18 @@ defmodule Explorer.SmartContract.Reader do |> Enum.with_index() |> Enum.all?(fn {target_type, index} -> type_to_compare = Map.get(Enum.at(Map.get(target_method, "inputs"), index), "type") - target_type_formatted = format_input_type(target_type) + target_type_formatted = format_type(target_type) target_type_formatted == type_to_compare end) end - defp format_input_type(input_type) do + defp format_type(input_type) do case input_type do {:array, type, array_size} -> - format_input_type(type) <> "[" <> Integer.to_string(array_size) <> "]" + format_type(type) <> "[" <> Integer.to_string(array_size) <> "]" {:array, type} -> - format_input_type(type) <> "[]" + format_type(type) <> "[]" {:tuple, tuple} -> format_tuple_type(tuple) @@ -288,9 +288,9 @@ defmodule Explorer.SmartContract.Reader do tuple |> Enum.reduce(nil, fn tuple_item, acc -> if acc do - acc <> "," <> format_input_type(tuple_item) + acc <> "," <> format_type(tuple_item) else - format_input_type(tuple_item) + format_type(tuple_item) end end) @@ -378,6 +378,15 @@ defmodule Explorer.SmartContract.Reader do returns |> Enum.map(fn output -> case output do + {:array, type, array_size} -> + %{"type" => format_type(type) <> "[" <> Integer.to_string(array_size) <> "]"} + + {:array, type} -> + %{"type" => format_type(type) <> "[]"} + + {:tuple, tuple} -> + %{"type" => format_tuple_type(tuple)} + {type, size} -> full_type = Atom.to_string(type) <> Integer.to_string(size) %{"type" => full_type}