diff --git a/CHANGELOG.md b/CHANGELOG.md index eb59cc0e50..3e3fe6e3a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ - [#2017](https://github.com/poanetwork/blockscout/pull/2017) - fix: fix to/from filters on tx list pages - [#2008](https://github.com/poanetwork/blockscout/pull/2008) - add new function clause for xDai network beneficiaries - [#2009](https://github.com/poanetwork/blockscout/pull/2009) - addresses page improvements +- [#2052](https://github.com/poanetwork/blockscout/pull/2052) - allow bytes32 for name and symbol - [#2047](https://github.com/poanetwork/blockscout/pull/2047) - fix: show creating internal transactions - [#2014](https://github.com/poanetwork/blockscout/pull/2014) - fix: use better queries for listLogs endpoint - [#2027](https://github.com/poanetwork/blockscout/pull/2027) - fix: `BlocksTransactionsMismatch` ignoring blocks without transactions diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/contract.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/contract.ex index c372c0c979..e1c60f7352 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/contract.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/contract.ex @@ -28,10 +28,11 @@ defmodule EthereumJSONRPC.Contract do @spec execute_contract_functions([call()], [map()], EthereumJSONRPC.json_rpc_named_arguments()) :: [call_result()] def execute_contract_functions(requests, abi, json_rpc_named_arguments) do - functions = + parsed_abi = abi |> ABI.parse_specification() - |> Enum.into(%{}, &{&1.function, &1}) + + functions = Enum.into(parsed_abi, %{}, &{&1.function, &1}) requests_with_index = Enum.with_index(requests) @@ -52,13 +53,15 @@ defmodule EthereumJSONRPC.Contract do |> Enum.into(%{}, &{&1.id, &1}) Enum.map(requests_with_index, fn {%{function_name: function_name}, index} -> + selectors = Enum.filter(parsed_abi, fn p_abi -> p_abi.function == function_name end) + indexed_responses[index] |> case do nil -> {:error, "No result"} response -> - {^index, result} = Encoder.decode_result(response, functions[function_name]) + {^index, result} = Encoder.decode_result(response, selectors) result end end) diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/encoder.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/encoder.ex index c43737cfdb..8cefe10850 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/encoder.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/encoder.ex @@ -35,12 +35,29 @@ defmodule EthereumJSONRPC.Encoder do @doc """ Given a result from the blockchain, and the function selector, returns the result decoded. """ - @spec decode_result(map(), %ABI.FunctionSelector{}) :: + @spec decode_result(map(), %ABI.FunctionSelector{} | [%ABI.FunctionSelector{}]) :: {String.t(), {:ok, any()} | {:error, String.t() | :invalid_data}} def decode_result(%{error: %{code: code, message: message}, id: id}, _selector) do {id, {:error, "(#{code}) #{message}"}} end + def decode_result(result, selectors) when is_list(selectors) do + selectors + |> Enum.map(fn selector -> + try do + decode_result(result, selector) + rescue + _ -> :error + end + end) + |> Enum.find(fn decode -> + case decode do + {_id, {:ok, _}} -> true + _ -> false + end + end) + end + def decode_result(%{id: id, result: result}, function_selector) do types_list = List.wrap(function_selector.returns) diff --git a/apps/explorer/lib/explorer/token/metadata_retriever.ex b/apps/explorer/lib/explorer/token/metadata_retriever.ex index 0e718954d0..2a03358248 100644 --- a/apps/explorer/lib/explorer/token/metadata_retriever.ex +++ b/apps/explorer/lib/explorer/token/metadata_retriever.ex @@ -22,6 +22,16 @@ defmodule Explorer.Token.MetadataRetriever do "payable" => false, "type" => "function" }, + %{ + "constant" => true, + "inputs" => [], + "name" => "name", + "outputs" => [ + %{"name" => "", "type" => "bytes32"} + ], + "payable" => false, + "type" => "function" + }, %{ "constant" => true, "inputs" => [], @@ -60,6 +70,19 @@ defmodule Explorer.Token.MetadataRetriever do ], "payable" => false, "type" => "function" + }, + %{ + "constant" => true, + "inputs" => [], + "name" => "symbol", + "outputs" => [ + %{ + "name" => "", + "type" => "bytes32" + } + ], + "payable" => false, + "type" => "function" } ] diff --git a/apps/explorer/test/explorer/token/metadata_retriever_test.exs b/apps/explorer/test/explorer/token/metadata_retriever_test.exs index 71b3f5ef2e..4ef10ea05e 100644 --- a/apps/explorer/test/explorer/token/metadata_retriever_test.exs +++ b/apps/explorer/test/explorer/token/metadata_retriever_test.exs @@ -426,4 +426,51 @@ defmodule Explorer.Token.MetadataRetrieverTest do on_exit(fn -> Application.put_env(:explorer, :token_functions_reader_max_retries, original) end) end end + + test "returns name and symbol when they are bytes32" do + token = insert(:token, contract_address: build(:contract_address)) + + expect( + EthereumJSONRPC.Mox, + :json_rpc, + 1, + fn requests, _opts -> + {:ok, + Enum.map(requests, fn + %{id: id, method: "eth_call", params: [%{data: "0x313ce567", to: _}, "latest"]} -> + %{ + id: id, + result: "0x0000000000000000000000000000000000000000000000000000000000000012" + } + + %{id: id, method: "eth_call", params: [%{data: "0x06fdde03", to: _}, "latest"]} -> + %{ + id: id, + result: "0x4d616b6572000000000000000000000000000000000000000000000000000000" + } + + %{id: id, method: "eth_call", params: [%{data: "0x95d89b41", to: _}, "latest"]} -> + %{ + id: id, + result: "0x4d4b520000000000000000000000000000000000000000000000000000000000" + } + + %{id: id, method: "eth_call", params: [%{data: "0x18160ddd", to: _}, "latest"]} -> + %{ + id: id, + result: "0x00000000000000000000000000000000000000000000d3c21bcecceda1000000" + } + end)} + end + ) + + expected = %{ + decimals: 18, + name: "Maker", + symbol: "MKR", + total_supply: 1_000_000_000_000_000_000_000_000 + } + + assert MetadataRetriever.get_functions_of(token.contract_address_hash) == expected + end end