From 9cf34bd6c1da9c7c67bdaa0d1bc85bedac1cafd3 Mon Sep 17 00:00:00 2001 From: Nikita Pozdniakov Date: Sun, 10 Sep 2023 14:54:36 +0300 Subject: [PATCH 1/3] Fix contracts' output decoding --- CHANGELOG.md | 1 + .../views/api/v2/smart_contract_view.ex | 27 ++-- .../views/smart_contract_view.ex | 81 +--------- .../api/v2/smart_contract_controller_test.exs | 142 ++++++++++++++++++ .../views/smart_contract_view_test.exs | 49 +++--- .../lib/explorer/smart_contract/reader.ex | 112 ++++++++++++++ 6 files changed, 295 insertions(+), 117 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d874f87ca..c766d2207b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ ### Fixes +- [#8431](https://github.com/blockscout/blockscout/pull/8431) - Fix contracts' output decoding - [#8354](https://github.com/blockscout/blockscout/pull/8354) - Hotfix for proper addresses' tokens displaying - [#8350](https://github.com/blockscout/blockscout/pull/8350) - Add Base Mainnet support for tx actions - [#8282](https://github.com/blockscout/blockscout/pull/8282) - NFT fetcher improvements diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex index 6a35a0c6fb..1c102bf0cc 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex @@ -1,6 +1,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do use BlockScoutWeb, :view + import Explorer.SmartContract.Reader, only: [zip_tuple_values_with_types: 2] + alias ABI.FunctionSelector alias BlockScoutWeb.API.V2.{Helper, TransactionView} alias BlockScoutWeb.SmartContractView @@ -284,28 +286,31 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do %{"type" => type, "value" => render_json(value, type)} end - def render_json(value, type) when type in [:address, "address", "address payable"] do - SmartContractView.cast_address(value) - end - - def render_json(value, type) when type in [:string, "string"] do - to_string(value) - end - def render_json(value, type) when is_tuple(value) do value - |> SmartContractView.zip_tuple_values_with_types(type) + |> zip_tuple_values_with_types(type) |> Enum.map(fn {type, value} -> render_json(value, type) end) end def render_json(value, type) when is_list(value) do + type = + if String.ends_with?(type, "[]") do + String.slice(type, 0..-3) + else + type + end + value |> Enum.map(&render_json(&1, type)) end - def render_json(value, _type) when is_binary(value) do - SmartContractView.binary_to_utf_string(value) + def render_json(value, type) when type in [:address, "address", "address payable"] do + SmartContractView.cast_address(value) + end + + def render_json(value, type) when type in [:string, "string"] do + to_string(value) end def render_json(value, _type) do diff --git a/apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex index 4a96a93adb..f0245adc2d 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex @@ -1,6 +1,8 @@ defmodule BlockScoutWeb.SmartContractView do use BlockScoutWeb, :view + import Explorer.SmartContract.Reader, only: [zip_tuple_values_with_types: 2] + alias Explorer.Chain alias Explorer.Chain.{Address, Transaction} alias Explorer.Chain.Hash.Address, as: HashAddress @@ -72,7 +74,7 @@ defmodule BlockScoutWeb.SmartContractView do String.starts_with?(type, "bytes") -> values = value - |> Enum.map_join(", ", &binary_to_utf_string(&1)) + |> Enum.join(", ") render_array_type_value(type, values, fetch_name(names, index)) @@ -107,6 +109,9 @@ defmodule BlockScoutWeb.SmartContractView do def values_with_type(value, string, names, index, _components) when string in ["string", :string], do: render_type_value("string", Helper.sanitize_input(value), fetch_name(names, index)) + def values_with_type(value, "bytes" <> _ = bytes_type, names, index, _components), + do: render_type_value(bytes_type, Helper.sanitize_input(value), fetch_name(names, index)) + def values_with_type(value, bytes, names, index, _components) when bytes in [:bytes], do: render_type_value("bytes", Helper.sanitize_input(value), fetch_name(names, index)) @@ -114,7 +119,7 @@ defmodule BlockScoutWeb.SmartContractView do do: render_type_value("bool", Helper.sanitize_input(to_string(value)), fetch_name(names, index)) def values_with_type(value, type, names, index, _components), - do: render_type_value(type, Helper.sanitize_input(binary_to_utf_string(value)), fetch_name(names, index)) + do: render_type_value(type, Helper.sanitize_input(value), fetch_name(names, index)) def values_with_type(value, :error, _components), do: render_type_value("error", Helper.sanitize_input(value), "error") @@ -158,78 +163,6 @@ defmodule BlockScoutWeb.SmartContractView do end) end - def zip_tuple_values_with_types(value, type) do - types_string = - type - |> String.slice(6..-2) - - types = - if String.trim(types_string) == "" do - [] - else - types_string - |> String.split(",") - end - - {tuple_types, _} = - types - |> Enum.reduce({[], nil}, fn val, acc -> - {arr, to_merge} = acc - - if to_merge do - compose_array_if_to_merge(arr, val, to_merge) - else - compose_array_else(arr, val, to_merge) - end - end) - - values_list = - value - |> Tuple.to_list() - - Enum.zip(tuple_types, values_list) - end - - def compose_array_if_to_merge(arr, val, to_merge) do - if count_string_symbols(val)["]"] > count_string_symbols(val)["["] do - updated_arr = update_last_list_item(arr, val) - {updated_arr, !to_merge} - else - updated_arr = update_last_list_item(arr, val) - {updated_arr, to_merge} - end - end - - def compose_array_else(arr, val, to_merge) do - if count_string_symbols(val)["["] > count_string_symbols(val)["]"] do - # credo:disable-for-next-line - {arr ++ [val], !to_merge} - else - # credo:disable-for-next-line - {arr ++ [val], to_merge} - end - end - - defp update_last_list_item(arr, new_val) do - arr - |> Enum.with_index() - |> Enum.map(fn {item, index} -> - if index == Enum.count(arr) - 1 do - item <> "," <> new_val - else - item - end - end) - end - - defp count_string_symbols(str) do - str - |> String.graphemes() - |> Enum.reduce(%{"[" => 0, "]" => 0}, fn char, acc -> - Map.update(acc, char, 1, &(&1 + 1)) - end) - end - def binary_to_utf_string(item) do case Integer.parse(to_string(item)) do {item_integer, ""} -> diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs index 863345a903..6799c10396 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs @@ -702,6 +702,148 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do "method_id" => Base.encode16(id, case: :lower) } in response end + + test "get correct bytes value 1", %{conn: conn} do + abi = [ + %{ + "inputs" => [], + "name" => "all_messages_hash", + "outputs" => [ + %{ + "internalType" => "bytes32", + "name" => "", + "type" => "bytes32" + } + ], + "stateMutability" => "view", + "type" => "function" + } + ] + + id_1 = + abi + |> ABI.parse_specification() + |> Enum.at(0) + |> Map.fetch!(:method_id) + + target_contract = insert(:smart_contract, abi: abi) + address_hash_string = to_string(target_contract.address_hash) + + EthereumJSONRPC.Mox + |> expect( + :json_rpc, + fn [ + %{ + id: id, + method: "eth_call", + params: [ + %{data: "0x1dd69d06", to: ^address_hash_string}, + "latest" + ] + } + ], + _opts -> + {:ok, + [ + %{ + id: id, + jsonrpc: "2.0", + result: "0x0000000000000000000000000000000000000000000000000000000000000000" + } + ]} + end + ) + + request = get(conn, "/api/v2/smart-contracts/#{target_contract.address_hash}/methods-read") + assert response = json_response(request, 200) + + assert %{ + "inputs" => [], + "name" => "all_messages_hash", + "outputs" => [ + %{ + "value" => "0x0000000000000000000000000000000000000000000000000000000000000000", + "type" => "bytes32" + } + ], + "stateMutability" => "view", + "type" => "function", + "method_id" => Base.encode16(id_1, case: :lower), + "names" => ["bytes32"] + } in response + end + + test "get correct bytes value 2", %{conn: conn} do + abi = [ + %{ + "inputs" => [], + "name" => "FRAUD_STRING", + "outputs" => [ + %{ + "internalType" => "bytes", + "name" => "", + "type" => "bytes" + } + ], + "stateMutability" => "view", + "type" => "function" + } + ] + + id_2 = + abi + |> ABI.parse_specification() + |> Enum.at(0) + |> Map.fetch!(:method_id) + + target_contract = insert(:smart_contract, abi: abi) + address_hash_string = to_string(target_contract.address_hash) + + EthereumJSONRPC.Mox + |> expect( + :json_rpc, + fn [ + %{ + id: id, + method: "eth_call", + params: [ + %{data: "0x46b2eb9b", to: ^address_hash_string}, + "latest" + ] + } + ], + _opts -> + {:ok, + [ + %{ + id: id, + jsonrpc: "2.0", + result: + "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000322d2d5468697320697320612062616420737472696e672e204e6f626f64792073617973207468697320737472696e672e2d2d0000000000000000000000000000" + } + ]} + end + ) + + request = get(conn, "/api/v2/smart-contracts/#{target_contract.address_hash}/methods-read") + assert response = json_response(request, 200) + + assert %{ + "inputs" => [], + "name" => "FRAUD_STRING", + "outputs" => [ + %{ + "value" => + "0x2d2d5468697320697320612062616420737472696e672e204e6f626f64792073617973207468697320737472696e672e2d2d", + "type" => "bytes" + } + ], + "stateMutability" => "view", + "type" => "function", + "method_id" => Base.encode16(id_2, case: :lower), + "names" => ["bytes"] + } in response + end end describe "/smart-contracts/{address_hash}/query-read-method" do diff --git a/apps/block_scout_web/test/block_scout_web/views/smart_contract_view_test.exs b/apps/block_scout_web/test/block_scout_web/views/smart_contract_view_test.exs index 7ba53f18ce..cb63435730 100644 --- a/apps/block_scout_web/test/block_scout_web/views/smart_contract_view_test.exs +++ b/apps/block_scout_web/test/block_scout_web/views/smart_contract_view_test.exs @@ -8,47 +8,32 @@ defmodule BlockScoutWeb.SmartContractViewTest do describe "values_with_type/1" do test "complex data type case" do value = - {<<156, 209, 70, 119, 249, 170, 85, 105, 179, 187, 179, 81, 252, 214, 125, 17, 21, 170, 86, 58, 225, 98, 66, - 118, 211, 212, 230, 127, 179, 214, 249, 38>>, 23_183_417, true, + {"0x9cd14677f9aa5569b3bbb351fcd67d1115aa563ae1624276d3d4e67fb3d6f926", 23_183_417, true, [ {<<164, 118, 64, 69, 133, 31, 23, 170, 96, 182, 200, 232, 182, 32, 114, 190, 169, 83, 133, 33>>, [ - <<15, 103, 152, 165, 96, 121, 58, 84, 195, 188, 254, 134, 169, 60, 222, 30, 115, 8, 125, 148, 76, 14, 162, - 5, 68, 19, 125, 65, 33, 57, 104, 133>>, - <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 61, 111, 131, 12, 226, 99, 202, 233, 135, 25, 57, 130, 25, 44, - 217, 144, 68, 43, 83>> - ], - <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 178, 96, 212, 241, 78, 0, 0>>}, + "0x0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d4121396885", + "0x000000000000000000000000bf3d6f830ce263cae987193982192cd990442b53" + ], "0x000000000000000000000000000000000000000000000000aab260d4f14e0000"}, {<<164, 118, 64, 69, 133, 31, 23, 170, 96, 182, 200, 232, 182, 32, 114, 190, 169, 83, 133, 33>>, [ - <<221, 242, 82, 173, 27, 226, 200, 155, 105, 194, 176, 104, 252, 55, 141, 170, 149, 43, 167, 241, 99, 196, - 161, 22, 40, 245, 90, 77, 245, 35, 179, 239>>, - <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>>, - <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 61, 111, 131, 12, 226, 99, 202, 233, 135, 25, 57, 130, 25, 44, - 217, 144, 68, 43, 83>> - ], - <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 178, 96, 212, 241, 78, 0, 0>>}, + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000bf3d6f830ce263cae987193982192cd990442b53" + ], "0x000000000000000000000000000000000000000000000000aab260d4f14e0000"}, {<<166, 139, 214, 89, 169, 22, 127, 61, 60, 1, 186, 151, 118, 161, 32, 141, 174, 143, 0, 59>>, [ - <<47, 154, 96, 152, 212, 80, 58, 18, 119, 121, 186, 151, 95, 95, 107, 4, 248, 66, 54, 43, 24, 9, 243, 70, - 152, 158, 154, 188, 11, 77, 237, 182>>, - <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 61, 111, 131, 12, 226, 99, 202, 233, 135, 25, 57, 130, 25, 44, - 217, 144, 68, 43, 83>>, - <<0, 5, 0, 0, 36, 155, 252, 47, 60, 200, 214, 143, 107, 107, 247, 35, 14, 160, 168, 237, 133, 61, 231, 49, - 0, 0, 0, 0, 0, 0, 2, 79>> - ], - <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 178, 96, 212, 241, 78, 0, 0>>}, + "0x2f9a6098d4503a127779ba975f5f6b04f842362b1809f346989e9abc0b4dedb6", + "0x000000000000000000000000bf3d6f830ce263cae987193982192cd990442b53", + "0x00050000249bfc2f3cc8d68f6b6bf7230ea0a8ed853de731000000000000024f" + ], "0x000000000000000000000000000000000000000000000000aab260d4f14e0000"}, {<<254, 68, 107, 239, 29, 191, 122, 254, 36, 232, 30, 5, 188, 139, 39, 28, 27, 169, 165, 96>>, [ - <<39, 51, 62, 219, 139, 220, 212, 10, 10, 233, 68, 251, 18, 27, 94, 45, 98, 234, 120, 38, 131, 148, 102, - 84, 160, 245, 230, 7, 169, 8, 213, 120>>, - <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 95, 197, 45, 138, 86, 59, 47, 24, 28, 106, 82, 125, 66, 46, 21, - 146, 201, 236, 250>>, - <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 166, 139, 214, 89, 169, 22, 127, 61, 60, 1, 186, 151, 118, 161, 32, - 141, 174, 143, 0, 59>>, - <<0, 5, 0, 0, 36, 155, 252, 47, 60, 200, 214, 143, 107, 107, 247, 35, 14, 160, 168, 237, 133, 61, 231, 49, - 0, 0, 0, 0, 0, 0, 2, 79>> - ], <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1>>} + "0x27333edb8bdcd40a0ae944fb121b5e2d62ea782683946654a0f5e607a908d578", + "0x0000000000000000000000002a5fc52d8a563b2f181c6a527d422e1592c9ecfa", + "0x000000000000000000000000a68bd659a9167f3d3c01ba9776a1208dae8f003b", + "0x00050000249bfc2f3cc8d68f6b6bf7230ea0a8ed853de731000000000000024f" + ], "0x0000000000000000000000000000000000000000000000000000000000000001"} ]} type = "tuple[bytes32,uint256,bool,tuple[address,bytes32[],bytes][]]" diff --git a/apps/explorer/lib/explorer/smart_contract/reader.ex b/apps/explorer/lib/explorer/smart_contract/reader.ex index 4fa3e8dadf..6222438d4f 100644 --- a/apps/explorer/lib/explorer/smart_contract/reader.ex +++ b/apps/explorer/lib/explorer/smart_contract/reader.ex @@ -754,6 +754,27 @@ defmodule Explorer.SmartContract.Reader do Map.put_new(output, "value", Encoder.unescape(value)) end + defp new_value(%{"type" => "tuple" <> _types = type} = output, values, index) do + value = Enum.at(values, index) + + result = + if String.ends_with?(type, "[]") do + value + |> Enum.map(fn tuple -> new_value(%{"type" => String.slice(type, 0..-3)}, [tuple], 0) end) + |> flat_arrays_map() + else + value + |> zip_tuple_values_with_types(type) + |> Enum.map(fn {type, part_value} -> + new_value(%{"type" => type}, [part_value], 0) + end) + |> flat_arrays_map() + |> List.to_tuple() + end + + Map.put_new(output, "value", result) + end + defp new_value(output, [value], _index) do Map.put_new(output, "value", value) end @@ -762,6 +783,97 @@ defmodule Explorer.SmartContract.Reader do Map.put_new(output, "value", Enum.at(values, index)) end + defp flat_arrays_map(%{"value" => value}) do + flat_arrays_map(value) + end + + defp flat_arrays_map(value) when is_list(value) do + Enum.map(value, &flat_arrays_map/1) + end + + defp flat_arrays_map(value) when is_tuple(value) do + value + |> Tuple.to_list() + |> flat_arrays_map() + |> List.to_tuple() + end + + defp flat_arrays_map(value) do + value + end + + def zip_tuple_values_with_types(value, type) do + types_string = + type + |> String.slice(6..-2) + + types = + if String.trim(types_string) == "" do + [] + else + types_string + |> String.split(",") + end + + {tuple_types, _} = + types + |> Enum.reduce({[], nil}, fn val, acc -> + {arr, to_merge} = acc + + if to_merge do + compose_array_if_to_merge(arr, val, to_merge) + else + compose_array_else(arr, val, to_merge) + end + end) + + values_list = + value + |> Tuple.to_list() + + Enum.zip(tuple_types, values_list) + end + + def compose_array_if_to_merge(arr, val, to_merge) do + if count_string_symbols(val)["]"] > count_string_symbols(val)["["] do + updated_arr = update_last_list_item(arr, val) + {updated_arr, !to_merge} + else + updated_arr = update_last_list_item(arr, val) + {updated_arr, to_merge} + end + end + + def compose_array_else(arr, val, to_merge) do + if count_string_symbols(val)["["] > count_string_symbols(val)["]"] do + # credo:disable-for-next-line + {arr ++ [val], !to_merge} + else + # credo:disable-for-next-line + {arr ++ [val], to_merge} + end + end + + defp update_last_list_item(arr, new_val) do + arr + |> Enum.with_index() + |> Enum.map(fn {item, index} -> + if index == Enum.count(arr) - 1 do + item <> "," <> new_val + else + item + end + end) + end + + defp count_string_symbols(str) do + str + |> String.graphemes() + |> Enum.reduce(%{"[" => 0, "]" => 0}, fn char, acc -> + Map.update(acc, char, 1, &(&1 + 1)) + end) + end + @spec bytes_to_string(<<_::_*8>>) :: String.t() defp bytes_to_string(value) do if value do From 993189f1183b3ff65c21ae31c322a7d642bef623 Mon Sep 17 00:00:00 2001 From: Nikita Pozdniakov Date: Mon, 11 Sep 2023 13:22:03 +0300 Subject: [PATCH 2/3] Refactor zip_tuple_values_with_types/2 --- .../lib/explorer/smart_contract/reader.ex | 45 ++++++++----------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/apps/explorer/lib/explorer/smart_contract/reader.ex b/apps/explorer/lib/explorer/smart_contract/reader.ex index 6222438d4f..3729cbe722 100644 --- a/apps/explorer/lib/explorer/smart_contract/reader.ex +++ b/apps/explorer/lib/explorer/smart_contract/reader.ex @@ -802,6 +802,7 @@ defmodule Explorer.SmartContract.Reader do value end + @spec zip_tuple_values_with_types(tuple, binary) :: [{binary, any}] def zip_tuple_values_with_types(value, type) do types_string = type @@ -815,18 +816,18 @@ defmodule Explorer.SmartContract.Reader do |> String.split(",") end - {tuple_types, _} = + {tuple_types_reversed, _} = types - |> Enum.reduce({[], nil}, fn val, acc -> + |> Enum.reduce({[], false}, fn val, acc -> {arr, to_merge} = acc - if to_merge do - compose_array_if_to_merge(arr, val, to_merge) - else - compose_array_else(arr, val, to_merge) - end + compose_array(to_merge, arr, val) end) + tuple_types = + tuple_types_reversed + |> Enum.reverse() + values_list = value |> Tuple.to_list() @@ -834,36 +835,28 @@ defmodule Explorer.SmartContract.Reader do Enum.zip(tuple_types, values_list) end - def compose_array_if_to_merge(arr, val, to_merge) do + defp compose_array(true, arr, val) do + updated_arr = update_last_list_item(arr, val) + if count_string_symbols(val)["]"] > count_string_symbols(val)["["] do - updated_arr = update_last_list_item(arr, val) - {updated_arr, !to_merge} + {updated_arr, false} else - updated_arr = update_last_list_item(arr, val) - {updated_arr, to_merge} + {updated_arr, true} end end - def compose_array_else(arr, val, to_merge) do + defp compose_array(to_merge, arr, val) do if count_string_symbols(val)["["] > count_string_symbols(val)["]"] do - # credo:disable-for-next-line - {arr ++ [val], !to_merge} + {[val | arr], !to_merge} else - # credo:disable-for-next-line - {arr ++ [val], to_merge} + {[val | arr], to_merge} end end defp update_last_list_item(arr, new_val) do - arr - |> Enum.with_index() - |> Enum.map(fn {item, index} -> - if index == Enum.count(arr) - 1 do - item <> "," <> new_val - else - item - end - end) + [last_element | remain] = arr + + [last_element <> "," <> new_val | remain] end defp count_string_symbols(str) do From 4ecd6a77e60e8a58e6e223d05cfd2d8f718c9a71 Mon Sep 17 00:00:00 2001 From: Maxim Filonov <53992153+sl1depengwyn@users.noreply.github.com> Date: Mon, 11 Sep 2023 14:01:16 +0300 Subject: [PATCH 3/3] Refactor zip_tuple_values_with_types/2 --- .../lib/explorer/smart_contract/reader.ex | 62 ++++++------------- 1 file changed, 20 insertions(+), 42 deletions(-) diff --git a/apps/explorer/lib/explorer/smart_contract/reader.ex b/apps/explorer/lib/explorer/smart_contract/reader.ex index 3729cbe722..19b8a86af1 100644 --- a/apps/explorer/lib/explorer/smart_contract/reader.ex +++ b/apps/explorer/lib/explorer/smart_contract/reader.ex @@ -813,19 +813,29 @@ defmodule Explorer.SmartContract.Reader do [] else types_string - |> String.split(",") + |> String.graphemes() end - {tuple_types_reversed, _} = - types - |> Enum.reduce({[], false}, fn val, acc -> - {arr, to_merge} = acc - - compose_array(to_merge, arr, val) - end) - tuple_types = - tuple_types_reversed + types + |> Enum.reduce( + {[""], 0}, + fn + ",", {types_acc, 0} -> + {["" | types_acc], 0} + + char, {[acc | types_acc], bracket_stack} -> + new_bracket_stack = + case char do + "[" -> bracket_stack + 1 + "]" -> bracket_stack - 1 + _ -> bracket_stack + end + + {[acc <> char | types_acc], new_bracket_stack} + end + ) + |> elem(0) |> Enum.reverse() values_list = @@ -835,38 +845,6 @@ defmodule Explorer.SmartContract.Reader do Enum.zip(tuple_types, values_list) end - defp compose_array(true, arr, val) do - updated_arr = update_last_list_item(arr, val) - - if count_string_symbols(val)["]"] > count_string_symbols(val)["["] do - {updated_arr, false} - else - {updated_arr, true} - end - end - - defp compose_array(to_merge, arr, val) do - if count_string_symbols(val)["["] > count_string_symbols(val)["]"] do - {[val | arr], !to_merge} - else - {[val | arr], to_merge} - end - end - - defp update_last_list_item(arr, new_val) do - [last_element | remain] = arr - - [last_element <> "," <> new_val | remain] - end - - defp count_string_symbols(str) do - str - |> String.graphemes() - |> Enum.reduce(%{"[" => 0, "]" => 0}, fn char, acc -> - Map.update(acc, char, 1, &(&1 + 1)) - end) - end - @spec bytes_to_string(<<_::_*8>>) :: String.t() defp bytes_to_string(value) do if value do