parent
4432ba4222
commit
21a1c1403c
@ -0,0 +1,108 @@ |
||||
defmodule BlockScoutWeb.ABIEncodedValueView do |
||||
@moduledoc """ |
||||
Renders a decoded value that is encoded according to an ABI. |
||||
|
||||
Does not leverage an eex template because it renders formatted |
||||
values via `<pre>` tags, and that is hard to do in an eex template. |
||||
""" |
||||
use BlockScoutWeb, :view |
||||
|
||||
require Logger |
||||
|
||||
def value_html(type, value) do |
||||
decoded_type = ABI.FunctionSelector.decode_type(type) |
||||
|
||||
do_value_html(decoded_type, value) |
||||
rescue |
||||
exception -> |
||||
Logger.warn(fn -> |
||||
["Error determining value html for #{inspect(type)}: ", Exception.format(:error, exception)] |
||||
end) |
||||
end |
||||
|
||||
def copy_text(type, value) do |
||||
decoded_type = ABI.FunctionSelector.decode_type(type) |
||||
|
||||
do_copy_text(decoded_type, value) |
||||
rescue |
||||
exception -> |
||||
Logger.warn(fn -> |
||||
["Error determining copy text for #{inspect(type)}: ", Exception.format(:error, exception)] |
||||
end) |
||||
end |
||||
|
||||
def do_copy_text({:bytes, _type}, value) do |
||||
hex(value) |
||||
end |
||||
|
||||
def do_copy_text({:array, type, _}, value) do |
||||
do_copy_text({:array, type}, value) |
||||
end |
||||
|
||||
def do_copy_text({:array, type}, value) do |
||||
values = |
||||
value |
||||
|> Enum.map(&do_copy_text(type, &1)) |
||||
|> Enum.intersperse(", ") |
||||
|
||||
~E|[<%= values %>]| |
||||
end |
||||
|
||||
def do_copy_text(_, {:dynamic, value}) do |
||||
hex(value) |
||||
end |
||||
|
||||
def do_copy_text(type, value) when type in [:bytes, :address] do |
||||
hex(value) |
||||
end |
||||
|
||||
def do_copy_text(_type, value) do |
||||
to_string(value) |
||||
end |
||||
|
||||
defp do_value_html(type, value, depth \\ 0) |
||||
|
||||
defp do_value_html({:bytes, _}, value, depth) do |
||||
do_value_html(:bytes, value, depth) |
||||
end |
||||
|
||||
defp do_value_html({:array, type, _}, value, depth) do |
||||
do_value_html({:array, type}, value, depth) |
||||
end |
||||
|
||||
defp do_value_html({:array, type}, value, depth) do |
||||
values = |
||||
Enum.map(value, fn inner_value -> |
||||
do_value_html(type, inner_value, depth + 1) |
||||
end) |
||||
|
||||
spacing = String.duplicate(" ", depth * 2) |
||||
delimited = Enum.intersperse(values, ",\n") |
||||
|
||||
~E|<%= spacing %>[<%= "\n" %><%= delimited %><%= "\n" %><%= spacing %>]| |
||||
end |
||||
|
||||
defp do_value_html(type, value, depth) do |
||||
spacing = String.duplicate(" ", depth * 2) |
||||
~E|<%= spacing %><%=base_value_html(type, value)%>| |
||||
[spacing, base_value_html(type, value)] |
||||
end |
||||
|
||||
def base_value_html(_, {:dynamic, value}) do |
||||
hex(value) |
||||
end |
||||
|
||||
def base_value_html(:address, value) do |
||||
address = hex(value) |
||||
|
||||
~E|<a href="<%= address_path(BlockScoutWeb.Endpoint, :show, address) %>" target="_blank"><%= address %></a>| |
||||
end |
||||
|
||||
def base_value_html(:bytes, value) do |
||||
hex(value) |
||||
end |
||||
|
||||
def base_value_html(_, value), do: Phoenix.HTML.html_escape(value) |
||||
|
||||
defp hex(value), do: "0x" <> Base.encode16(value, case: :lower) |
||||
end |
@ -1,4 +1,10 @@ |
||||
defmodule BlockScoutWeb.TransactionLogView do |
||||
use BlockScoutWeb, :view |
||||
@dialyzer :no_match |
||||
|
||||
alias Explorer.Chain.Log |
||||
|
||||
def decode(log, transaction) do |
||||
Log.decode(log, transaction) |
||||
end |
||||
end |
||||
|
@ -0,0 +1,91 @@ |
||||
defmodule BlockScoutWeb.ABIEncodedValueViewTest do |
||||
use BlockScoutWeb.ConnCase, async: true |
||||
|
||||
alias BlockScoutWeb.ABIEncodedValueView |
||||
|
||||
defp value_html(type, value) do |
||||
type |
||||
|> ABIEncodedValueView.value_html(value) |
||||
|> Phoenix.HTML.Safe.to_iodata() |
||||
|> IO.iodata_to_binary() |
||||
end |
||||
|
||||
defp copy_text(type, value) do |
||||
type |
||||
|> ABIEncodedValueView.copy_text(value) |
||||
|> Phoenix.HTML.Safe.to_iodata() |
||||
|> IO.iodata_to_binary() |
||||
end |
||||
|
||||
describe "value_html/2" do |
||||
test "it formats addresses as links" do |
||||
address = "0x0000000000000000000000000000000000000000" |
||||
address_bytes = address |> String.trim_leading("0x") |> Base.decode16!() |
||||
|
||||
expected = ~s(<a href=\"/address/#{address}\" target=\"_blank\">#{address}</a>) |
||||
|
||||
assert value_html("address", address_bytes) == expected |
||||
end |
||||
|
||||
test "it formats lists with newlines and spaces" do |
||||
expected = String.trim(""" |
||||
[ |
||||
1, |
||||
2, |
||||
3, |
||||
4 |
||||
] |
||||
""") |
||||
|
||||
assert value_html("uint[]", [1, 2, 3, 4]) == expected |
||||
end |
||||
|
||||
test "it formats nested lists with nested depth" do |
||||
expected = String.trim(""" |
||||
[ |
||||
[ |
||||
1, |
||||
2 |
||||
], |
||||
[ |
||||
3, |
||||
4 |
||||
] |
||||
] |
||||
""") |
||||
|
||||
assert value_html("uint[][]", [[1, 2], [3, 4]]) == expected |
||||
end |
||||
|
||||
test "it formats lists of addresses as a list of links" do |
||||
address = "0x0000000000000000000000000000000000000000" |
||||
address_link = ~s(<a href=\"/address/#{address}\" target=\"_blank\">#{address}</a>) |
||||
|
||||
expected = String.trim(""" |
||||
[ |
||||
#{address_link}, |
||||
#{address_link}, |
||||
#{address_link}, |
||||
#{address_link} |
||||
] |
||||
""") |
||||
|
||||
address_bytes = "0x0000000000000000000000000000000000000000" |> String.trim_leading("0x") |> Base.decode16!() |
||||
|
||||
assert value_html("address[]", [address_bytes, address_bytes, address_bytes, address_bytes]) == expected |
||||
end |
||||
end |
||||
|
||||
describe "copy_text/2" do |
||||
test "it skips link formatting of addresses" do |
||||
address = "0x0000000000000000000000000000000000000000" |
||||
address_bytes = address |> String.trim_leading("0x") |> Base.decode16!() |
||||
|
||||
assert copy_text("address", address_bytes) == address |
||||
end |
||||
|
||||
test "it skips the formatting when copying lists" do |
||||
assert copy_text("uint[]", [1, 2, 3, 4]) == "[1, 2, 3, 4]" |
||||
end |
||||
end |
||||
end |
Loading…
Reference in new issue