|
|
@ -2,6 +2,7 @@ defmodule BlockScoutWeb.SmartContractView do |
|
|
|
use BlockScoutWeb, :view |
|
|
|
use BlockScoutWeb, :view |
|
|
|
|
|
|
|
|
|
|
|
alias Explorer.Chain |
|
|
|
alias Explorer.Chain |
|
|
|
|
|
|
|
alias Explorer.Chain.Hash.Address |
|
|
|
|
|
|
|
|
|
|
|
def queryable?(inputs) when not is_nil(inputs), do: Enum.any?(inputs) |
|
|
|
def queryable?(inputs) when not is_nil(inputs), do: Enum.any?(inputs) |
|
|
|
|
|
|
|
|
|
|
@ -41,46 +42,244 @@ defmodule BlockScoutWeb.SmartContractView do |
|
|
|
def named_argument?(%{"name" => _}), do: true |
|
|
|
def named_argument?(%{"name" => _}), do: true |
|
|
|
def named_argument?(_), do: false |
|
|
|
def named_argument?(_), do: false |
|
|
|
|
|
|
|
|
|
|
|
def values(addresses, type) when is_list(addresses) and type == "address[]" do |
|
|
|
def values_with_type(value, type, components \\ nil) |
|
|
|
addresses |
|
|
|
|
|
|
|
|> Enum.map(&values(&1, "address")) |
|
|
|
def values_with_type(value, type, components) when is_list(value) do |
|
|
|
|> Enum.join(", ") |
|
|
|
cond do |
|
|
|
|
|
|
|
String.starts_with?(type, "tuple") -> |
|
|
|
|
|
|
|
tuple_types = |
|
|
|
|
|
|
|
type |
|
|
|
|
|
|
|
|> String.slice(0..-3) |
|
|
|
|
|
|
|
|> supplement_type_with_components(components) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
values = |
|
|
|
|
|
|
|
value |
|
|
|
|
|
|
|
|> tuple_array_to_array(tuple_types) |
|
|
|
|
|
|
|
|> Enum.join(", ") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
render_array_type_value(type, values) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
String.starts_with?(type, "address") -> |
|
|
|
|
|
|
|
values = |
|
|
|
|
|
|
|
value |
|
|
|
|
|
|
|
|> Enum.map(&binary_to_utf_string(&1)) |
|
|
|
|
|
|
|
|> Enum.join(", ") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
render_array_type_value(type, values) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
String.starts_with?(type, "bytes") -> |
|
|
|
|
|
|
|
values = |
|
|
|
|
|
|
|
value |
|
|
|
|
|
|
|
|> Enum.map(&binary_to_utf_string(&1)) |
|
|
|
|
|
|
|
|> Enum.join(", ") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
render_array_type_value(type, values) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
true -> |
|
|
|
|
|
|
|
values = |
|
|
|
|
|
|
|
value |
|
|
|
|
|
|
|
|> Enum.join(", ") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
render_array_type_value(type, values) |
|
|
|
|
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
def values(values, type) when is_list(values) and type == "tuple[]" do |
|
|
|
def values_with_type(value, type, _components) when is_tuple(value) do |
|
|
|
array_from_tuple = tuple_array_to_array(values) |
|
|
|
values = |
|
|
|
|
|
|
|
value |
|
|
|
|
|
|
|
|> tuple_to_array(type) |
|
|
|
|
|
|
|
|> Enum.join(", ") |
|
|
|
|
|
|
|
|
|
|
|
array_from_tuple |
|
|
|
render_type_value(type, values) |
|
|
|
|> Enum.join(", ") |
|
|
|
|
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
def values(value, _type) when is_tuple(value) do |
|
|
|
def values_with_type(value, type, _components) when type in ["address", "address payable"] do |
|
|
|
tuple_to_array(value) |
|
|
|
{:ok, address} = Address.cast(value) |
|
|
|
|
|
|
|
render_type_value("address", to_string(address)) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
def values(value, type) when type in ["address", "address payable"] do |
|
|
|
def values_with_type(value, "string", _components), do: render_type_value("string", value) |
|
|
|
{:ok, address} = Explorer.Chain.Hash.Address.cast(value) |
|
|
|
|
|
|
|
to_string(address) |
|
|
|
def values_with_type(value, "bool", _components), do: render_type_value("bool", to_string(value)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def values_with_type(value, type, _components), do: render_type_value(type, binary_to_utf_string(value)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def values_only(value, type, components) when is_list(value) do |
|
|
|
|
|
|
|
cond do |
|
|
|
|
|
|
|
String.starts_with?(type, "tuple") -> |
|
|
|
|
|
|
|
tuple_types = |
|
|
|
|
|
|
|
type |
|
|
|
|
|
|
|
|> String.slice(0..-3) |
|
|
|
|
|
|
|
|> supplement_type_with_components(components) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
values = |
|
|
|
|
|
|
|
value |
|
|
|
|
|
|
|
|> tuple_array_to_array(tuple_types) |
|
|
|
|
|
|
|
|> Enum.join(", ") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
render_array_value(values) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
String.starts_with?(type, "address") -> |
|
|
|
|
|
|
|
values = |
|
|
|
|
|
|
|
value |
|
|
|
|
|
|
|
|> Enum.map(&binary_to_utf_string(&1)) |
|
|
|
|
|
|
|
|> Enum.join(", ") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
render_array_value(values) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
String.starts_with?(type, "bytes") -> |
|
|
|
|
|
|
|
values = |
|
|
|
|
|
|
|
value |
|
|
|
|
|
|
|
|> Enum.map(&binary_to_utf_string(&1)) |
|
|
|
|
|
|
|
|> Enum.join(", ") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
render_array_value(values) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
true -> |
|
|
|
|
|
|
|
values = |
|
|
|
|
|
|
|
value |
|
|
|
|
|
|
|
|> Enum.join(", ") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
render_array_value(values) |
|
|
|
|
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
def values(values, _) when is_list(values), do: Enum.join(values, ",") |
|
|
|
def values_only(value, type, _components) when is_tuple(value) do |
|
|
|
def values(value, _), do: value |
|
|
|
values = |
|
|
|
|
|
|
|
value |
|
|
|
|
|
|
|
|> tuple_to_array(type) |
|
|
|
|
|
|
|
|> Enum.join(", ") |
|
|
|
|
|
|
|
|
|
|
|
defp tuple_array_to_array(values) do |
|
|
|
|
|
|
|
values |
|
|
|
values |
|
|
|
|> Enum.map(fn value -> |
|
|
|
|
|
|
|
tuple_to_array(value) |
|
|
|
|
|
|
|
end) |
|
|
|
|
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
defp tuple_to_array(value) do |
|
|
|
def values_only(value, type, _components) when type in ["address", "address payable"] do |
|
|
|
|
|
|
|
{:ok, address} = Address.cast(value) |
|
|
|
|
|
|
|
to_string(address) |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def values_only(value, "string", _components), do: value |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def values_only(value, "bool", _components), do: to_string(value) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def values_only(value, _type, _components), do: binary_to_utf_string(value) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
defp tuple_array_to_array(value, type) do |
|
|
|
value |
|
|
|
value |
|
|
|
|> Tuple.to_list() |
|
|
|
|> Enum.map(fn item -> |
|
|
|
|> Enum.map(&binary_to_utf_string(&1)) |
|
|
|
tuple_to_array(item, type) |
|
|
|
|> Enum.join(",") |
|
|
|
end) |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
defp tuple_to_array(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 |
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
end) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
values_list = |
|
|
|
|
|
|
|
value |
|
|
|
|
|
|
|
|> Tuple.to_list() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
values_types_list = Enum.zip(tuple_types, values_list) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
values_types_list |
|
|
|
|
|
|
|
|> Enum.map(fn {type, value} -> |
|
|
|
|
|
|
|
values_with_type(value, type) |
|
|
|
|
|
|
|
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 |
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
defp binary_to_utf_string(item) do |
|
|
|
defp binary_to_utf_string(item) do |
|
|
|
if is_binary(item), do: "0x" <> Base.encode16(item, case: :lower), else: item |
|
|
|
if is_binary(item) do |
|
|
|
|
|
|
|
if String.starts_with?(item, "0x") do |
|
|
|
|
|
|
|
item |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
"0x" <> Base.encode16(item, case: :lower) |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
item |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
defp render_type_value(type, value) do |
|
|
|
|
|
|
|
"<div style=\"padding-left: 20px\">(#{type}) : #{value}</div>" |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
defp render_array_type_value(type, values) do |
|
|
|
|
|
|
|
value_to_display = "[" <> values <> "]" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
render_type_value(type, value_to_display) |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
defp render_array_value(values) do |
|
|
|
|
|
|
|
value_to_display = "[" <> values <> "]" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
value_to_display |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
defp supplement_type_with_components(type, components) do |
|
|
|
|
|
|
|
if type == "tuple" && components do |
|
|
|
|
|
|
|
types = |
|
|
|
|
|
|
|
components |
|
|
|
|
|
|
|
|> Enum.map(fn component -> |
|
|
|
|
|
|
|
Map.get(component, "type") |
|
|
|
|
|
|
|
end) |
|
|
|
|
|
|
|
|> Enum.join(",") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"tuple[" <> types <> "]" |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
type |
|
|
|
|
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|
end |
|
|
|